# 서울숲_D_Day13 리뷰 레포트
## 참석자
신용호, 여재환, 이영훈, 이정민
## 1. 코드 동작 이해
### Tokenizer
- syntax를 통해서 정의해준 token unit으로 쪼개는 역할을 한다.
- switch문이나 if문을 통해서 syntax별로 분류하여 array에 담는다.
### Lexer
- token에 의미(NULL, string, Number, Array)를 부여한다
- 기본적으로 Tokenizer와 같은 역할을 수행하지만 대개 extra context로의 접근이 필요하다.
- switch문이나 if문을 통해서 각 token별로 의미를 넣어준다.
### Parser
- Lexer를 통해 의미를 부여한 데이터를 JSON 형태로 구조화한다.
- 일반적으로 Parser의 역할은 lexer에서 불러들인 토큰들을 AST의 형태로 프로그램을 표현하는 것이다.
- 반복문 혹은 재귀문을 통해 array를 만나면 child에 새로운 array 객체를 생성하고 아닐경우 child 배열에 객체를 넣는다.
## 2. 코드 동작 개선
### 신용호
- Tokeniser에서 문자열 단위로 쪼개서 해석하는 것보다 문자 단위로 쪼개서 해석해보는 것을 구현
- 이스케이프 문자를 입력에 사용했을 때의 처리를 추가
### 여재환
- 정규표현식의 다양한 활용을 통해 코드의 간결성을 높일 수 있다.
- 예외처리에 대해 보다 많은 경우의 수에 대한 고려가 필요하다.
- Parser의 함수구조가 매우 복잡한데 보다 깊이있는 설계를 통해 간결한 recursion으로 바꿔보자.
### 이영훈
- flag를 두는 것보다 함수에 상태를 만들어 처리하는 것이 좋을 것 같다.
- 에러처리나 조건문에서 비교를 하는 함수를 만들어 가독성을 높여야한다.
### 이정민
* parser에서 반복문을 사용하며, stack에 현재 위치를 저장하는 방식을 선택했는데, 대신 재귀를 사용해볼 것.
* 더 많은 예외처리에 대비해보기.
## 3. Consideration
### 스스로 확인할 사항
#### 무한 중첩된 구조를 프로그래밍 하는데 효과적인 방법은 무엇인가? 재귀인가, 반복인가?
* 특정한 basecase를 통해 반복의 끝을 표시함으로써 코드의 간결성, 가독성을 위해서는 재귀가 더 우수하다.
* 그러나 재귀는 많은 스택으로 인하여 디버깅이 힘들고, 스택오버플로우가 발생하기 쉽다는 문제가 있다.
* 재귀에서 중복되는 부분은 memoization이라는 기법을 통해 값을 바로 불러오는 방식으로 처리해주면 성능이 좋아진다.
#### 에러처리에 대한 흐름제어를 할 수 있는가?
* 전체적인 함수를 `try {} catch(error){}` 구문으로 감싸고, 함수 실행시 에러 처리를 해줄 부분에서 throw Error를 해준다.
* throw Error를 만나는 즉시 함수는 try 를 빠져나가 catch로 넘어간다. 에러가 발생하지 않으면 try 안에서 정상 실행된다.
* `finally{}`는 에러 발생 여부와 관계없이 try, catch 가 모두 실행된 후 실행된다.
### 다같이 확인할 사항
#### 소프트웨어 개발에서, token, lexer, parser의 역할은 각각 무엇인지 조사해보자.
- 컴파일러의 front end에 해당하며 특정 언어에 대한 syntax와 semantic을 확인하는 과정이다.
#### [token의 종류](https://en.wikipedia.org/wiki/Lexical_analysis#Token)
+ identifier: names the programmer chooses;
+ keyword: names already in the programming language;
+ separator (also known as punctuators): + punctuation characters and paired-delimiters;
+ operator: symbols that operate on arguments and produce results;
+ literal: numeric, logical, textual, reference literals;
+ comment: line, block.
#### [lexer](https://en.wikipedia.org/wiki/Lexical_analysis)
- 일련의 character를 일련의 token들로 만들어주는 작업을 담당
#### [parser](https://en.wikipedia.org/wiki/Parsing#Parser)
- 특정 입력을 받아서 그것으로 데이터 구조를 만들어주는 작업을 담당
- 토큰들을 AST(Abstract syntax tree)라는 트리를 구성함으로써 프로그램의 형태를 표현.
#### 이번 미션에서 구현한, token, lexer, parser의 각각 역할과 동작방식을 요약해서 설명해보자.
- token은 syntax에 따라 나누는 작업으로 character등을 구분할 syntax로 나누어 준다.
- lexer: tokenizer로 가공된 배열을 순회하며 bracket, string, number, null 등 원하는 분류에 따라 구분해주며 semantic을 부여한다.
- `{type: string, value: 'hi'}` 등의 object array로 구현하거나
- `[type, value]` 의 배열이 중첩된 형태로 구현했다.
- parser: lexer을 통해 의미가 부여된 토큰들에 대해 recursive 혹은 stack을 이용하여 iterative하게 순회함으로써 AST를 구성.
### reference
- 참고: [Compiler 위키](https://en.wikipedia.org/wiki/Compiler)
- [Automata theory](https://en.wikipedia.org/wiki/Automata_theory)
### abstract syntax tree
- 각 노드는 소스코드에서 발생하는 구조체
- 컴파일러는 내부적으로 여러가지 컴포넌트로 구성되어 있는데 첫단계가 소스코드를 파싱하여 AST로 만드는 작업
모든 고급언어의 컴파일러가 하는 역할
- AST를 검사하고 최적화하고 정해진 기계어로 대치해서 나오는것이 기계어 덩어리, 목적코드
- 컴파일러는 파싱과정에서 문법의 일치 유무를 판단 할 수 있는데 문법상 틀리지 않았지만 연산자 우선순위가 구현되지 않았다면
결정을 못내리는 경우가 생긴다 -> 모호하다(ambiguous)
- 연산자 우선순위는 AST를 구성하는 과정에서 문법적으로 옳은 두가지 이상의 경우를 만났을 때 모호함을 없애기 위한 규칙
### 정규식을 만드는 방법
- 리터럴을 사용하는 방법
```Javascript
var re = /ab+c/;
```
- RegExp 객체의 생성자
```Javascript
var re = new RegExp("ab+c");
```
- 생성자 함수를 사용하면 정규식이 실행 시점에 컴파일 된다.
- 정규식의 패턴이 변경될 수 있는 경우 혹은 사용자 입력과 같이 다른 출처로 부터 패턴을 가져 오는 경우 생성자 사용
#### test
- 대응되는 문자열이 있는지 검사하는 RegExp 메소드 입니다. true 나 false를 반환합니다.
- test() 는 regexp.test(string)
- test() 함수는 정규식과 비교하여 포함되지 않으면 true 반환, 포함되면 false 반환
#### match
- 대응되는 문자열을 찾는 RegExp 메소드입니다. 정보를 가지고 있는 배열을 반환합니다. 대응되는 문자열을 찾지 못했다면 null을 반환합니다.
- match() 는 string.match(regexp)
- match() 함수는 정규식과 비교하여 포함되지 않으면 비교문구를 반환, 포함되면 null, 빈값 반환