Skip to content

명세

The ECMAScript® Language Specification(살아 있는 표준)은 JavaScript 언어 전부를 정의하므로 누구나 자바스크립트 엔진을 구현할 수 있습니다.

파서를 위해 익혀야 할 장은 다음과 같습니다.

  • 5장: 표기 관례
  • 11장: ECMAScript 언어: 소스 텍스트
  • 12장: ECMAScript 언어: 렉시컬 문법
  • 13–16장: 표현식, 문, 함수, 클래스, 스크립트와 모듈
  • Annex B: 웹 브라우저용 추가 ECMAScript 기능
  • Annex C: ECMAScript Strict Mode

명세 안에서 이동할 때:

  • 클릭 가능한 요소에는 고유 링크가 있으며 URL 앵커로 보입니다(예: #sec-identifiers)
  • 호버 시 툴팁이 뜰 수 있고, References를 누르면 모든 참조를 볼 수 있습니다

표기 관례

읽어야 할 절은 5.1.5 Grammar Notation입니다.

여기서 기억할 점은 다음입니다.

재귀

문법에서 리스트를 표현하는 방식입니다.

ArgumentList :
  AssignmentExpression
  ArgumentList , AssignmentExpression

의미:

javascript
a, b = 1, c = 2
^_____________^ ArgumentList
   ^__________^ ArgumentList, AssignmentExpression,
          ^___^ AssignmentExpression

선택(optional)

선택 요소에는 _opt_ 접미사가 붙습니다. 예:

VariableDeclaration :
  BindingIdentifier Initializer_opt

의미:

javascript
var binding_identifier;
var binding_identifier = Initializer;
                       ______________ Initializer_opt

매개변수

[Return], [In] 등은 문법 매개변수입니다.

예:

ScriptBody :
    StatementList[~Yield, ~Await, ~Return]

는 스크립트 최상위에서는 yield·await·return이 허용되지 않지만,

ModuleItem :
  ImportDeclaration
  ExportDeclaration
  StatementListItem[~Yield, +Await, ~Return]

는 최상위 await를 허용합니다.

소스 텍스트

11.2 Types of Source Code에 따르면 스크립트 코드와 모듈 코드는 크게 다릅니다. 또한 use strict 모드가 옛 동작을 막아 문법을 더 단정하게 만듭니다.

스크립트 코드는 기본적으로 strict가 아니며, 파일 상단에 use strict를 넣어야 strict가 됩니다. HTML에서는 <script src="javascript.js"></script>로 씁니다.

모듈 코드는 자동으로 strict입니다. HTML에서는 <script type="module" src="main.mjs"></script>로 씁니다.

ECMAScript 언어: 렉시컬 문법

더 깊이 있게는 V8 블로그 Understanding the ECMAScript spec를 읽으세요.

Automatic Semicolon Insertion

세미콜론을 생략할 수 있는 규칙을 모두 기술합니다. 요약하면 다음 코드로 정리할 수 있습니다.

rust
    pub fn asi(&mut self) -> Result<()> {
        if self.eat(Kind::Semicolon) || self.can_insert_semicolon() {
            return Ok(());
        }
        let range = self.prev_node_end..self.cur_token().start;
        Err(SyntaxError::AutoSemicolonInsertion(range.into()))
    }

    pub const fn can_insert_semicolon(&self) -> bool {
        self.cur_token().is_on_new_line || matches!(self.cur_kind(), Kind::RCurly | Kind::Eof)
    }

asi는 적용 가능한 곳에서 직접 호출해야 합니다(예: 문장 끝).

rust
fn parse_debugger_statement(&mut self) -> Result<Statement<'a>> {
    let node = self.start_node();
    self.expect(Kind::Debugger)?;
    self.asi()?; 
    self.ast.debugger_statement(self.finish_node(node))
}

INFO

이 ASI 절은 파서 관점으로 쓰여 있으며, 소스를 왼쪽에서 오른쪽으로 파싱한다고 명시해 다른 방식의 파서를 거의 쓸 수 없게 만듭니다. jsparagus 작성자가 여기서 이에 대해 토로합니다.

이 기능의 명세는 매우 고수준인 동시에 절차가 이상하게 기술되어 있습니다(“소스 텍스트를 왼쪽에서 오른쪽으로 파싱하다가 토큰을 만나면…” 마치 브라우저 이야기를 하는 듯합니다. 알기로는 명세 어디에서도 파서 내부 구현을 가정·암시하는 유일한 곳입니다.) 하지만 ASI를 다른 방식으로 정의하기는 어렵습니다.

표현식, 문, 함수, 클래스, 스크립트와 모듈

문법을 이해하고 파서에 적용하는 데 시간이 걸립니다.