new 내가 만든 언어의 개발환경을 -...
Post on 15-Oct-2020
3 Views
Preview:
TRANSCRIPT
내가 만든 언어의 개발환경을 Visual Studio Code로 빠르고 쉽게 구축하기
이승재 & 강성훈 넥슨코리아 데브캣스튜디오
2017-04-25 발표용
Part B 언어 확장 개발하기
안녕하세요
• 강 성훈
• @senokay @lifthrasiir
• http://mearie.org/
• 데브캣스튜디오
• 〈마비노기 듀얼〉 라이브 개발을 했었지요
• 기타 이상한 여러 가지 Rust, 아희, IOCCC, 리듬게임, IRC 네트워크, …
빠른 되감기
전문 용어로 재탕이라고 하기도 합니다…
〈마비노기 듀얼〉의 역사
2014 1 2 3 4 5 6 7 8 9 10 11 122015 1 2 3 4 5 6 7 8 9 10 11 122016 1 2 3 4 5 6 7 8 9 10 11 12
1차 CBT
2차 CBT 글로벌 CBT
한국 정식 런칭 글로벌 그랜드 런칭
글로벌 소프트 런칭
한국 PC 런칭
대책이 없는 기술 부채
?
잘못 쓰면 안 돌려준다잘못 쓰면 이상한 데서 터진다 잘못 쓰면 거기서 터진다
잘못
쓰면
빨리
알려준다
잘못
써도
늦게
알려준다
Python
LuaPHP
Haskell
Rust
CC++
ML/F#
Perl
Ruby
Java
JavaScript
LISP/Scheme
C#/VB.netObjective-C
D
Go
Scala
Perl6
ClojureDart
Coq/Agda/…
Swift
Julia
FORTRAN
Erlang
Ada
아희
가능성
타입 체커?
장기적으로 비용 대 효과를 높이려면 언어를 건드려야 한다?효과
비용
미지의 세계
🌴 카일루아Kailua
• 루아와 같은 문법을 공유하는 정적 언어
• 타입 선언, 명세 등등은 주석을 사용
• Rust로 작성
La'aloa Beach County Park, Kailua-Kona, Hawaii © W Nowicki, CC-BY
될까
?
🌴 이런 느낌의 언어입니다--# open lua51--v function(id: int) —> {id = int, name = string, meta = Meta?}local function build_packet(id) local p = {} p.id = id p.name = to_name(id) p.aliases = {} --: vector<string> if has_meta then local meta = get_meta_from_json(id) --# assume meta: Meta p.meta = meta end return pend
많은 곳에 타입 지정을 할 수 있음
필드가 고정된 테이블은 대부분 자동 추론
배열이나 연관 배열은 직접 지정
타입 시스템으로 해결되지 않는 경우 여러 가지 비상구를 쓸 수 있음
그대로 루아로 실행해도 돌아갑니다 (중요)
1년 후 .
무슨 일이 일어났나요?
이제 베이퍼웨어가 아닙니다!
nil에 민감하게 타입 시스템을 만드니 루아가 💩을
타협을 위해서는 시간이 필요했다
그래서 그 시간에 IDE 지원을 추가하기로 했고…
Visual Studio?
• Visual Studio와 Gideros Studio 중 선택하다 보니…
• VS는 거대한 레거시 덩어리!
• 주요 API가 둘로 쪼개져 있음: MEF, COM (EnvDTE)
• 꽤 많은 API가 한 쪽에만 있는 경우가 많음
• 차라리 COM만 쓰라면 할만하겠는데… 디버깅이…
Visual Studio Code
• 마이크로소프트가 내놓은 오픈소스 크로스플랫폼 개발 환경
• 최근의 웹 기술을 사용해서 웹 기술에 익숙하면 접근이 쉬움
• VS보다 확장 만들기가 훨씬 쉬움!
• 심지어 JavaScript 및 TypeScript 안 써도 대부분의 작업이 가능
2015 1 2 3 4 5 6 7 8 9 10 11 12
2016 1 2 3 4 5 6 7 8 9 10 11 12
2017 1 2 3
설계 변경안
설계 변경 완료 마무리 작업
VS 디버거 확장 개발 시작
설계 시작
VS Code로 전환
구현 시작 구현 마무리 NDC 발표
VS 확장 개발 시작
번아웃!!!
Star Trek: The Next Generation © Paramount Pictures
데모
제가 만든 언어를 VS Code에 연동하려면 어떻게 해야 하나요?
시작한지 8분만에 겨우 시작되는 본론
Read The Fine Manual
• 이 발표에서는 넘어가기 쉽거나 도움이 되는 조언을 중심으로 소개합니다.
• 구체적인 내용은 어차피 문서를 보셔야…
• https://code.visualstudio.com/Docs/extensionAPI/vscode-api TypeScript로 개발할 경우 필수적으로 보게 될 문서
• https://github.com/Microsoft/language-server-protocol 다른 언어로 언어 서버를 개발할 경우 필수적으로 보게 될 문서
• https://www.bpak.org/blog/?p=2160 (제가 개발에 참고하진 않았지만) 당장 도움이 되는 문서가 필요하다면 이런 작업 기록도 좋습니다
IDE 연동
문법 강조 (꽤 할만함)
실시간 체크 및 오류 표시 (쪼까 귀찮음)
정의로 가기, 이름 바꾸기 등 (으으음…)
자동 완성 (나를 죽여라)
IDE 연동: 문법 강조
• 실은 이게 가장 쉽습니다. (특히 VS Code에서라면)
• 코드를 짤 필요도 없음
• 최근 추가된 “TM 범위 검사” 기능이디버깅에 매우 유용
• 가성비가 가장 좋습니다.다른 거 없고 이것만 있어도 쓸만함.
"match": "\\bWHATEVER\\b|(?<!\\[)\\[\\s*NO_CHECK\\s*\\](?!\\])", "name": "invalid.kailua.dangerous" }, "kailua-literal-type": { "match": "\\b(false|nil|true)\\b", "name": "constant.language.lua" }, "kailua-prim-type": { "match": "\\b(any|bool|boolean|int|integer|number|string|table|thread|userdata)\\b", "name": "storage.type.kailua" }, "kailua-keyword": { "match": "\\b(assume|class|const|global|map|method|module|open|static|type|var|vector)\\b", "name": "keyword.control.kailua" }, "kailua-namelike": { "match": "(?:\\b([A-Za-z_]\\w*)\\b|`(?:[^\\\\`]|\\\\.)*`)(?:\\s*\\.\\s*(?:\\b([A-Za-z_]\\w*)\\b|`(?:[^\\\\`]|\\\\.)*`))*(?=\\s*[=:])", "name": "variable.other.kailua.field-or-argument-or-type-name" }, "kailua-quoted-name": { "begin": "`", "beginCaptures": { "0": { "name": "punctuation.definition.kailua.quoted-name.begin.lua" } }, "end": "`|(?=\\n)", "endCaptures": { "0": { "name": "punctuation.definition.kailua.quoted-name.end.lua" } }, "name": "entity.name.kailua.quoted-name", "patterns": [ { "match": "\\\\.", "name": "constant.character.escape.kailua" } ] }, "kailua-tag": { "match": "(?<!\\[)\\[[^\\[\\]]+\\](?!\\])", "name": "support.type.kailua.tag" }, "kailua-internal-type": { "match": "(?<![A-Za-z0-9_`])<.*?>", "name": "comment.kailua.type.internal"
근데 아주 쉬운 건 아니고 정규표현식을 알아야 하긴 합니다.
• 언어를 직접 만들고 있는데 문법 강조가 까다로운 것 같다면,문법 강조가 쉬운 쪽으로 언어를 바꾸는 게 여러 모로 이득입니다.
• 코드를 어쨌든 짜야 하는 경우도 존재하는데…
• 테마 색상과 상관 없이 원하는 색깔을 표시하고 싶은 경우
• 문법적으로는 같지만 의미에 따라 다르게 문법 강조를 하고 싶은 경우 (예: 지역 변수와 전역 변수를 다른 색깔로 표시하고 싶어요)
IDE 연동: 실시간 체크 및 오류 표시
• 언어 서버Language Server를 구현하면 처음으로 할만한 작업입니다.
• 언어 서버???
확장 TypeScript
꼬마 확장
언어 서버 아무 언어나
IDE 연동: 실시간 체크 및 오류 표시
• 언어 서버Language Server를 구현하면 처음으로 할만한 작업입니다.
• 언어 서버가 워크스페이스를 제대로 처리하는 지 확인할 수 있음
• 좀 느려도 됨 → 단, 체크를 동시에 여러 번 돌리지 않도록 조심!
• 하지만 위치까지 정확하게 나오려면 상당한 작업이 필요합니다.
• 터미널에서의 오류 메시지와 IDE에서의 오류 메시지는 다릅니다.
• VS Code는 오류 메시지의 순서를 무시하므로 주의가 필요
• 긴 오류 메시지는 IDE에서 특히 보기 어려움
• 점진적으로 만들어 보고, 실험하면서 바꿔 나가세요.
local x = {} local y = x .. 'string'
.. 연산자를 `{...}`와(과) `"string"`에 적용할 수 없습니다 └ `{...}`이(가) `(number|string)`의 서브타입이 아닙니다 (10:11)
string
IDE 연동: 정의로 가기• 왜 이게 어렵나요?
• 어떤 문법 요소가 변수 이름인지, 함수 이름인지 등을 알아야 하고
• 대응되는 정의가 어디 있는지도 저장해야 하기 때문
• 아무 기반 작업도 하지 않았다면 야매로 구현하는 게 더 쌉니다.
• 카일루아는 VS 확장 만드느라…
IDE 연동: 정의로 가기 + 이름 찾아 바꾸기• 왜 이게 어렵나요?
• 어떤 문법 요소가 변수 이름인지, 함수 이름인지 등을 알아야 하고
• 대응되는 정의가 어디 있는지도 저장해야 하기 때문
• 아무 기반 작업도 하지 않았다면 야매로 구현하는 게 더 쌉니다.
• 카일루아는 VS 확장 만드느라…
• 일단 정의로 가는 게 동작하면 이름 찾아 바꾸는 건 쉽습니다.
IDE 연동: 정의로 가기 + 타입 정보• 왜 이게 어렵나요?
• 어떤 문법 요소가 변수 이름인지, 함수 이름인지 등을 알아야 하고
• 대응되는 정의가 어디 있는지도 저장해야 하기 때문
• 아무 기반 작업도 하지 않았다면 야매로 구현하는 게 더 쌉니다.
• 카일루아는 VS 확장 만드느라…
• 타입 정보는 문법 요소에 붙인 위치 정보를 타입까지 끌고 가면 됩니다…
🌴 잠깐! 내가 카일루아를 다시 만든다면
구문분석 타입체크 IDE 지원• 문법을 확인한다 • 짜기 쉬움
• 타입을 추론하고 • 규칙에 맞나 확인 • 얼마든지 복잡해질수 있다
• 앞에서의 결과를사용해야 한다
• 잘 하기 어려움
초기 작업 1주, IDE 때문에 2주 정도 더 아마 순수 작업만 6개월 넘지 않았을까… VS 버린 거 빼면 2개월 정도?
🌴 잠깐! 내가 카일루아를 다시 만든다면구 문 분 석
I D E 지 원
구 문 분 석
I D E 지 원
타 입 체 크
구 문 분 석
I D E 지 원
타 입 체 크
I D E 지 원
구 문 분 석
I D E 지 원
구 문 분 석
타 입 체 크
I D E 지 원
I D E 지 원
타 입 체 크
I D E 지 원
• IDE 지원 여부는 구현의 내부 구조를 크게 바꿀 수 있습니다.
• 어떤 정보가 어느 시점까지 남아야 하는가? 파서가 재시작 가능해야 하나? 파일이 바뀌면 얼마나 다시 작업해야 하나? 등등등등…
• 괜히 저처럼 삽질하지 마시고 IDE 지원을 처음부터 고려하세요. ㅠㅠ
IDE 연동: 자동 완성
• 사실 이걸 하고 싶어서 IDE 연동을 하는 거긴 한데…
• 거의 모든 작업에 의존성이 있어서 매우 까다롭습니다.
• 현재 위치에서 어떤 문법 요소가 가능한지 알아내는 게 특히 어려움
• 자동화가 (아직) 어렵고, 자동 완성 전용 파서가 필요할 수도 있음
• 가성비가 끔찍하지만, 하기만 하면 확실히 보람은 넘칩니다.
local x = {a = 3, b = 'string'} local y = x.
a b
• 크게 두 가지 구현 전략이 있고 장단이 있습니다.
• 현재 커서를 기준으로 앞뒤로 부분 파싱partial parsing하기
• 파싱을 전부 다 하고 위치 정보를 붙여서 검색하기 (카일루아는 이쪽)
• 어느 쪽이든 상당한 작업이 필요하므로 처음부터 하는건 추천하지 않습니다.
개비스콘 광고 © 옥시레킷벤키저
개비스콘 광고 © 옥시레킷벤키저
https://marketplace.visualstudio.com/items?itemName=devCAT.kailua
감사합니다
🌴 카일루아의 내부 구조 II슬라이드만 보는 저같은 사람을 위한 특전 + 하도 오랫동안 하다 보니까 내가 말하고 싶었다
카일루아 타입 시스템 v2
• 선술했듯 nil이 문제가 되어서…
• 배열 접근만 해도 nil이 나올 수 있는 시점에서 사용성이 안 좋았음
• 기존 프로그래머들이 C#을 큰 문제 없이 썼다는 점에 착안
• TypeScript v1의 전례도 있어 과감하게 변경하기로 결정
T? 명시적으로 nil을 포함하므로, 연산 등에 바로 쓸 수는 없다. 테이블 필드나 함수 인자를 생략하려면 항상 이 타입이어야 함.
T 암묵적으로 nil을 포함한다. 연산에 사용할 수 있는데 nil이라 생기는 오류는 넘어간다.
T! nil을 포함할 수 없다(적어도 명시적으로는). T?을 여기에 바로 넣을 수는 없다. T를 거쳐야 한다.
실은 그래서 이건 타입 시스템이 아니고 일종의 문서화에 가깝습니다.
• nil을 무시하게 하니까 온갖 것들이 다 쉬워졌어요!
• 테이블이 공유되는 게 전혀 문제가 되지 않음 (없던 필드는 nil일테니)
• 타입이 제자리에서 바뀌(는 것처럼 보이)는 시스템이 거의 사라짐→ 거대한 버그의 온상이 사라졌다!
• 합집합 타입union type이 구현이 까다로워 제한적으로만 구현했지만,nil만은 자주 쓰여서 특수처리하고 있었는데 그럴 필요 없음
• 지금 돌아보면 이 결정이 타입 시스템의 복잡도를 크게 줄였다고 봄
IDE 붙이기: 위치 정보
• Spanned<T>
• 오프셋만 저장하고, 실제 데이터는 Source 타입에 있음
• 아무 데나 위치 정보를 붙일려면 일반화generic 타입이어야만…
• 그럼에도 불구하고 SpannedSlotSeq 같은 변종이 존재 orz
• 이걸 다 붙이는 것도 일, 써 먹어서 오류 메시지 제대로 내는 것도 일
#[derive(Clone, PartialEq)]pub struct Sig { pub attrs: Vec<Spanned<Attr>>, pub args: Spanned<Seq<TypeSpec<Spanned<ScopedId>>, Varargs>>, pub returns: Option<Returns>,}
--v [NO_CHECK]--v function(x: string, y: int) --> (int, int)
#[derive(Clone, PartialEq)]pub enum Returns { Seq(Seq<Spanned<Kind>>), Never(Span), // a span for `!`}
IDE 붙이기: 오류 보고
• 카일루아 극초기에는 오류 보고라는 게 없었음(…)
• 한참 뒤에 한 줄씩 나오는 오류 보고(Report)를 확립
• 테스트는 기본적으로 이 종류의 오류 보고를 확인함
• IDE 붙이는 과정에서 타입 오류 보고(TypeReport)가 추가됨
• 타입은 재귀적이기 때문에 오류 보고도 재귀적이어야 하므로
• 그래서 이론적으로는 트리 형태여야 하는데 아직… orz
IDE 붙이기: 백그라운드 작업
• 현재는 futures-rs를 쓰고 있는데, 이게 별로 좋은 선택이 아니었던듯
• 타입 체커는 근본적으로 CPU 의존적인데 futures-rs는 전반적으로 I/O 관련 보조 라이브러리(tokio 등)만 많음
• I/O 멀티플렉스와 CPU 작업을 함께 지원하는 솔루션은 없는 것 같음
• 당장은 동작해야 하니 좀 야매스럽게 붙여 놓은 상황
• Rayon이 더 적합하지 않겠느냐는 얘기가 있어서 조금씩 보는 중
테스팅
• 테스트 케이스가 1천개 정도 있습니다. 요런 느낌의…--8<-- assume-tablelocal f = function() end--# assume f: tablelocal p = f.index --@< Error: Cannot index `table` without further type information; specify more detailed type, or use `--# assume` as a last resort--! error
--8<-- conjunctive-lhs-1local a = ('string' and 53) + 42--! ok
--8<-- conjunctive-lhs-2local a = (53 and 'string') + 42 --@< Error: Cannot apply + operator to `"string"` and `42` --@^ Cause: `"string"` is not a subtype of `number`--! error
정말로 이렇게 하이라이팅되도록 편집기를 설정했습니다.
• 근데 카일루아 같은 류의 프로그램은 어지간히 테스트해도 버그가 나옵니다
• 원래 타입 체커라는 게 이런 것이지요…
• Line coverage로는 감당이 전혀 안 되고 path coverage가 필요
• 최근에 cargo-fuzz로 꽤 재미를 봤음 cargo-fuzz 버그도 밟고
• 어쨌든 제가 만든/관리한 소프트웨어 중에서는 가장 많이 테스트를 한다는 게 위안거리?
복잡도
• 아놔…~/Works/git/kailua$ tokei------------------------------------------------------------------------------- Language Files Lines Code Comments Blanks------------------------------------------------------------------------------- JSON 5 491 491 0 0 Lua 17 8365 2781 4201 1383 Markdown 5 983 983 0 0 Rust 60 29152 22122 3199 3831 TOML 11 304 262 1 41 TypeScript 4 225 161 30 34------------------------------------------------------------------------------- Total 102 39520 26800 7431 5289-------------------------------------------------------------------------------
심지어 루아는 주석이라고 센 것도 다 카일루아 명령이라서 과소평가되어 있음
복잡도 vs Rust
• 그래도 비교적 할만하긴 합니다. 아직까지는.
• Rust의 큰 특징: 컴파일이 되면 많은 문제는 넘어간 것이다
• 즉, 타입을 잘 쓰면 보통 로직 버그로 나올 걸 컴파일 시간에 잡을 수 있다
• 명세가 많이 바뀌어서 구현 전략을 대규모로 수정한 적이 여럿 있는데,타입 시스템으로 메꿔서 비교적 빠르게 대응한 경우가 많음
• Fearless Refactoring
그 밖에…
• 이제 내부적으로 쓰고 있고, 그만한 완성도도 가지고 있다고 판단됨
• 하지만 피드백과 참여는 아무리 많아도 부족하지 않습니다 (중요)
• 복잡도 대비 실 작업 시간이 생각보다 많지 않아서 코드 퀄리티는 그닥…
• 하고 싶은 말은 많지만 이 자리에서 말하기에는 너무 분량이 많습니다;
• 아마도 저널 글 같은 것으로 써 올릴 것 같으니 관심 있으면 그 쪽을.
끝
top related