1. 4
  1.  

  2. 9

    The ECMAScript spec was in part overseen by Mike Cowlishaw, the inventor of REXX. The ANSI REXX standard is written as a metacircular evaluator in REXX; you could almost copy and paste the spec into a REXX interpreter and have it just work. There would only be a few things you’d need to add

    1. 6

      Not sure I share this enthusiasm for the ECMAScript spec. Last year I extended the JavaScript implementation in Prince with a number of features including lexical bindings and for-of. I found that the description of behaviour was spread all over the spec making it very hard to know when all behaviours had been captured—and let me assure you there is no shortage of edge cases in JavaScript. Thank goodness for the test262 test suite.

      1. 5

        I wouldn’t have a job today if I hadn’t decided to mess around and do my dissertation in Prince instead of focusing on my studies. Thanks, Prince rules.

        1. 1

          Oh cool! You mean this one?

          1. 2

            Yes.

        2. 1

          I meant that ECMAScript spec has a lot more rigor than, say, C or C++ spec. ECMAScript is somewhat “executable”. It is easy to convert it to actual (slow) interpreter and then compare practical (fast) implementations against it. This is not possible with C or C++ spec

        3. 2

          I’d have to strongly disagree with the warm feelings towards the grammar, speaking as a beginner that tried to hack around with it. It definitely has its strong points, and like anything else could improve - but I firmly disagree with the idea of it being a ready-to-paste interpreter.


          Most notably, the tree-walking nature of the grammar hurts to comprehend. Particularly, the “Runtime Semantics”, “Static Semantics”, and possibly other Syntax-Directed Operations are hard to grapple with due to their loosely defined almost polymorphic nature.

          For example, the ExpectedArgumentCount has a piece-wise definition over a set of parse nodes, in particular FormalParameters. It defines static semantics for only these two:

          FormalParameters : [empty] FunctionRestParameter
                  1. Return 0.
          FormalParameters : FormalParameterList , FunctionRestParameter
                  1. Return ExpectedArgumentCount of FormalParameterList.
          

          However, the FormalParameters is defined as these possible cases:

          FormalParameters[Yield, Await] :
                  [empty]
                  FunctionRestParameter[?Yield, ?Await]
                  FormalParameterList[?Yield, ?Await]
                  FormalParameterList[?Yield, ?Await] ,
                  FormalParameterList[?Yield, ?Await] , FunctionRestParameter[?Yield, ?Await]
          

          It forgets about defining a production for FormalParameterList and FormalParameterList ,! You can encounter this behavior being necessary with a function such as function f(x) {}. I had to add cases for these two scenarios in my code, without the corresponding ECMAScript specification to back me up.

          It seems innocuous here, but this is just one strong example I have. The rest of my criticism stems from my mental frustration regarding it being difficult for an onlooker to piece together where productions and static semantics fit in code inside an interpreter. Essentially a criticism of the polymorphic nature of being able to stick on new static rules, as opposed to something more static/concrete that can easily fit in, such as seeing a pseudo-code switch statement.


          My second point is regarding the entrypoint of the spec. Where does an interpreter begin? Where do realms and agents begin/get initialized? I roughly pieced together an idea after combing through the spec, but it was not apparent in the slightest.

          Essentially, the routine for my interpreter goes:

          1. Parse the AST into a ton of runtime objects so we can tree-walk them
          2. Call InitializeHostDefinedRealm
          3. Create any host-defined hooks (e.g. setting values in the global object/realm)
          4. Call ParseScript
          5. Call ScriptEvaluation

          I fail to see any references to ScriptEvaluation, and no significant ones to InitializeHostDefinedRealm or ParseScript that would lend itself to this initialization routine. Oh, and I didn’t implement any early bailing errors properly, I just assume the script is valid. Good luck combining all the static semantics in relation to parse errors!


          My last point is regarding async and generators. I was happily implementing things, until I came across some scary lines of text that had huge implications for the entire design of my interpreter. Specifically, I’m thinking about the GeneratorStart, GeneratorResume, and GeneratorYield functions. They contain(ed[^1]) lines of pseudocode such as these:

          • GeneratorStart: 4. Set the code evaluation state of genContext such that when evaluation is resumed for that execution context the following steps will be performed:
          • GeneratorResume: 9. Resume the suspended evaluation of genContext using NormalCompletion(value) as the result of the operation that suspended it. Let result be the value returned by the resumed computation.
          • GeneratorYield: 7. Set the code evaluation state of genContext such that when evaluation is resumed with a Completion resumptionValue the following steps will be performed:

          Thankfully, these are able to be implemented with closures. I looked to engine262 and saw it use yield* to implement these, and was deeply concerned. It remains to be known if something like this will happen in the future.


          Of course, this is just my take on it after trying to implement enough to get function calls barely working. I’m by no means an expert, and this is just my take on some of the negative sides of the specification. Additionally my criticisms of the specification stem from the draft version present circa June 21, 2021 published on https://tc39.es/ecma262/ and it’s possibly things have changed. In-fact, they have:

          [^1]: Looks like as of today, these lines no longer exist and they implement them with closures. That’s great as that’s how I decided to implement them as well!

          1. 1

            You can tell them your criticism to bug tracker ( https://github.com/tc39/ecma262/issues ).

            My original point was this: ECMAScript spec is a lot more precisely specified than other specs, such as c and c++ spec or rust reference and even rust ferrocene spec. ECMAScript is (despite its shortcomings) the most precisely specified language among popular ones. ECMAScript sets high bar