개발자, SaLang 언어를 만들다.

jinho park·2023년 12월 19일
28

예전부터 만들고 싶었던 언어를 요 며칠동안 GPT-4와 함께 설계를 해보고 있다.
기본 컨셉은 HTML, CSS, JS 를 하나의 파일로 작성해서 최종 적으로 WebComponent 를 쉽게 만들어 주는 트랜스파일러를 구현하는 것이다.

이 언어의 이름은 SaLang 이다. (Stylish and Language)

아래는 며칠 동안 GPT-4 와 같이 설계한 코드이다.

@component MyComponent {
  @attribute title: "My Amazing Component";
  @attribute isVisible: true;

  @style {
    .my-component {
      color: var(primaryColor);
      padding: 10px;
    }

    .hidden {
      display: none;
    }
  }

  @function toggleVisibility() {
    @js {
      this.isVisible = !var(isVisible);
    }
  }

  @event click {
    @selector .toggle-btn {
      background-color: var(secondaryColor);
    }
    @js {
      toggleVisibility();
    }
  }

  @template {
    div(class: "my-component" var(isVisible ? '' : ' hidden')) {
      "Title: " var(title)

      @if var(isVisible) {
        div { "This content is visible" }
      } @else {
        div { "This content is hidden" }
      }

      @repeat item in var(items) {
        @if var(item.isVisible) {
          div(class: "item") { "Item: " var(item.name) }
        } @else {
          div(class: "item hidden") { "Hidden Item" }
        }
      }

      button(class: "toggle-btn") { "Toggle Visibility" }
    }
  }
}

기본적으로 CSS 문법을 베이스로 하고 있다.
복잡한 JS 코드는 별도의 코드 블럭으로 나눠서 처리 한다.

기능들

  • @component: 새로운 웹컴포넌트를 정의한다.
  • @attribute: 웹 컴포넌트 에서 사용할 속성(attribute)을 정의한다.
  • @style: 웹 컴포넌트내에서 CSS 스타일을 정의한다. (웹컴포넌트에서 스타일 사용을 좀 더 쉽게 하려고 한다.)
  • @function: 웹 컴포넌트에서 사용할 수 있는 함수를 정의한다. 외부에서 실행할 수도 있다.
  • @event: 내부 템플릿에서 사용되는 이벤트를 정의한다. 특이하게 이벤트 안에서 @selector 를 사용해서 스타일을 좀 더 쉽게 적용할 수 있다.
  • @template: 웹 컴포넌트의 템플릿을 정의한다. 조건부 렌더링(@if, @else)과 반복 렌더링(@repeat)을 사용할 수 있다.

사실 난 언어를 만들어 본적은 없다.
예전에 간단한 위키 파서를 만들어 본적은 있어도 언어는 생소하다.

그래서 GPT-4 의 힘을 빌려서 해보기로 했다.

연말에 하나씩 해놔야 내년이 재밌어서 지금 하는 중

GPT-4 를 사용해서 언어 설계하기

gpt-3.5 를 안쓰고 gpt-4 를 쓰는 이유는 gpt-4 가 질문에 훨씬 유연하게 잘 대처해주기 때문에 그렇다.
전반적으로 문맥을 큰 틀에서 이해(?)하고 결과물을 생각보다 잘 만들어준다.
물론 만들어주는 코드를 바로 실행하면 안되고 수정을 하긴 해야하지만, 대략 80~90% 가까이는 힌트를 주는 것 같다.

그럼 GPT-4 와 언어를 어떻게 만들어야 할까?
대략 아래와 같은 형태로 방향을 정한다.

  1. 언어 스펙을 간략하게 먼저 작성한다.
  2. 작성한 언어 스펙을 GPT-4 에게 분석하고 개선점을 알려달라고 한다.
  3. 개선점이 나오면 몇개의 개선점을 추가해서 언어스펙을 업데이트 해달라고 한다.
  4. (여기서 부터 중요한데, 검증을 내가 하지 않을 것이다. )
  5. 언어 스펙을 검증을 하기 위해서 파서를 생성해달라고 한다.
  6. 파서는 PEG.js 에서 사용할 수 있는 문법으로 해달라고 하고
  7. PEG 문법이 나오면 vitest 로 테스트 할 수 있는 코드를 만들어 달라고 한다.
  8. GPT-4 가 만든 PEG 문법을 PEG.js 를 통해서 컴파일 해서 parser 를 만들고
  9. 이 Parser 를 사용해서 vitest 테스트 코드를 실행해서 검증이 통과하면 성공이다.

대략 이정도 하면 쉬울 것 같지만 몇가지 문제가 있다.

  1. 언어 스펙이 명확 하지 않기 때문에 중간 중간 계속 바뀐다.
  2. PEG 문법이 내가 정한 언어 스펙에 완전히 맞지 않을 수도 있다. (덕분에 어디가 잘 못 됐는지 몰라서 한참을 헤매었다.)
  3. vitest 코드로 만들어진 테스트 코드도 정확하지 않을 때가 있다.

그래서 초기에 언어 스펙을 크게 설정하면 처음부터 멘붕에 빠질 수 있다.

간단하게 시작해보자.

처음에는 컴포넌트 개념만 있었다.

@component my-component {
    background-color: red; 
}

너무 아름다운 코드 인 것 같다. 이걸 가지고 웹컴포넌트를 만들 수 있다니

이 코드만 가지고 GPT-4 에게 개선점을 찾아달라고 해보자.
대략 이렇게 질문 할 것이다.

새로운 언어를 만들고 있어
HTML,CSS,JS 를 합쳐서 CSS 비슷한 문법으로 웹컴포넌트를 만드는게 목표야.

@component my-component {
background-color: red;
}

이런 문법으로 시작하려는데 개선점을 찾아줄 수 있어?

그럼 이렇게 답변이 온다.

새로운 언어를 개발하는 것은 흥미롭고 도전적인 작업입니다. HTML, CSS, JS를 결합하여 CSS와 유사한 문법으로 웹 컴포넌트를 만드는 아이디어는 매력적이네요. 제안하신 문법에 대한 개선점을 몇 가지 제안해 드리겠습니다:

명확한 구조 정의: 컴포넌트의 구조를 더 명확하게 정의할 필요가 있어 보입니다. 예를 들어, HTML과 JavaScript 코드를 어떻게 포함시킬 것인지 정의하는 것이 중요합니다. 이를 위해 특정한 구문이나 규칙을 설정할 수 있습니다.

스코핑과 격리: CSS의 중요한 문제 중 하나는 스타일의 스코핑과 격리입니다. 새로운 언어에서는 각 컴포넌트의 스타일이 다른 컴포넌트에 영향을 미치지 않도록 설계하는 것이 중요합니다.

재사용성과 확장성: 컴포넌트를 쉽게 재사용하고 확장할 수 있도록 설계하는 것이 중요합니다. 이를 위해, 컴포넌트 내에서 변수를 사용하거나, 상속과 같은 개념을 도입하는 것이 좋을 수 있습니다.

성능 최적화: 웹 컴포넌트를 많이 사용할 경우, 성능 문제가 발생할 수 있습니다. 따라서, 성능 최적화를 위한 메커니즘을 고려하는 것이 중요합니다. 예를 들어, 불필요한 렌더링을 방지하기 위한 전략 등을 고려할 수 있습니다.

툴링과 에코시스템 지원: 개발자들이 쉽게 새로운 언어를 채택하고 사용할 수 있도록, 적절한 툴링(IDE 지원, 린팅 도구 등)과 라이브러리/프레임워크와의 호환성을 고려하는 것이 중요합니다.

문서화와 커뮤니티 구축: 어떤 새로운 언어나 기술도, 충분한 문서화와 커뮤니티 지원이 없다면 널리 사용되기 어렵습니다. 사용자들이 쉽게 배우고, 문제를 해결할 수 있도록 하는 것이 중요합니다.

이러한 개선점들을 고려하여 언어를 발전시키면, 더 많은 개발자들이 효과적으로 사용할 수 있을 것입니다.

이 중에 명확한 구조 정의에 집중을 해서 질문을 좀 더 이어나가면 된다.
한번더 질문을 해보자.

구조를 어떻게 더 명확하게 하면 될까?
예제 코드를 만들어줘

간단한 질문만 던져도 된다.
그럼 아래와 같이 답변을 준다.

구조를 더 명확하게 만들기 위해서는 HTML, CSS, 및 JavaScript 코드가 어떻게 통합되고, 각각의 역할이 어떻게 구분되는지를 분명하게 해야 합니다. 예제 코드를 통해 이를 설명하겠습니다. 이 예제에서는 CSS와 유사한 문법을 사용하되, HTML과 JavaScript 코드를 통합하는 방법을 보여줍니다.

@component my-component {
    /* CSS 부분 */
    style {
        background-color: red;
        color: white;
        padding: 10px;
        border-radius: 5px;
    }
    /* HTML 템플릿 부분 */
    template {
        <div class="my-component">
            <h1>{{ title }}</h1>
            <button id="myButton">Click me</button>
        </div>
    }
    /* JavaScript 부분 */
    script {
        data() {
            return {
                title: 'Hello, World!'
            };
        }
        mounted() {
            document.getElementById('myButton').addEventListener('click', () => {
                alert('Button clicked!');
            });
        }
    }
}

이 코드는 다음과 같은 특징을 가집니다:

스타일 정의: style 블록 내에서 CSS 속성을 정의합니다.
HTML 템플릿: template 블록은 HTML 마크업을 포함합니다. 여기서 {{ title }} 같은 표현식을 사용하여 동적 데이터를 렌더링할 수 있습니다.
JavaScript 로직: script 블록은 컴포넌트의 동작을 정의합니다. 여기서 data 함수는 컴포넌트의 데이터를 정의하고, mounted 함수는 컴포넌트가 마운트될 때 실행될 로직을 정의합니다.
이 예제는 CSS와 유사한 문법을 사용하여 웹 컴포넌트를 정의하는 방법을 보여주며, 각 요소 (스타일, HTML, > JavaScript)가 분리되어 있어 코드의 가독성과 유지보수성이 향상됩니다. 또한, 이 구조는 기존의 웹 개발 방식과 유사하면서도 새로운 언어의 이점을 제공합니다.

간단한 기본 코드만 가지고도 그럴싸한 예제 코드를 만들었다.
이제부터는 여기서 어떤걸 빼고 넣을지에 대한 것을 계속 GPT-4 와 협업을 하면 된다.

GPT-4 와 어떻게 협업을 잘 할 수 있을까?

기본 코드는 그럴싸 하지만 실제로 내가 원하는 것과 거리가 조금 멀다. 왜냐하면 기본 코드가 js 에 가깝게 만들어졌기 때문에 그렇다. 난 CSS 베이스 코드가 더 좋은데 ...

하지만 걱정할 필요없다. 한마디만 해주면 된다.

CSS 베이스 코드가 좀 더 잘 어울릴 것 같아

이렇게 제시를 해주면 아래와 같이 반응한다.

CSS 기반 코드를 사용하여 웹 컴포넌트를 더 잘 구성하기 위해, CSS의 구문과 스타일을 최대한 활용하면서 HTML과 JavaScript를 효과적으로 통합하는 방법을 고려할 수 있습니다. 다음은 CSS를 기반으로 하는 웹 컴포넌트의 예제 코드입니다:

@component my-component {
    /* CSS 스타일 정의 */
    background-color: red;
    color: white;
    padding: 10px;
    border-radius: 5px;

    /* HTML 템플릿 */
    @template {
        <div class="my-component">
            <h1>@{title}</h1>
            <button id="myButton">Click me</button>
        </div>
    }

    /* JavaScript 코드 */
    @script {
        var title = "Hello, World!";

        document.addEventListener("DOMContentLoaded", function() {
            document.getElementById("myButton").addEventListener("click", function() {
                alert("Button clicked!");
            });
        });
    }
}

이 코드에서 다음과 같은 방법을 사용했습니다:

스타일 선언: 컴포넌트의 최상위에서 직접 CSS 속성을 선언합니다. 이것은 전통적인 CSS 문법과 유사합니다.

HTML 템플릿 정의: @template 지시어를 사용하여 HTML 마크업을 포함합니다. 데이터 바인딩은 @{...} 구문을 사용하여 처리됩니다.

JavaScript 로직: @script 지시어를 통해 컴포넌트의 JavaScript 코드를 정의합니다. 이 부분은 컴포넌트의 동적인 기능을 담당합니다.

이러한 접근 방식은 CSS 문법에 충실하면서도 HTML과 JavaScript를 포함할 수 있는 유연성을 제공합니다. 컴포넌트의 정의가 CSS에 더 가까워, 웹 개발자들에게 친숙하게 느껴질 수 있습니다.

아하, 이렇게 이야기 하다 보면 @ 표시를 특정 기능을 시작하는 형태로 봐도 될 것 같다.
이렇게 힌트를 하나씩 얻어가면서 조합한다.

진짜 파서를 만들어달라고 해보자.

이제 기본 문법이 되었으니 진짜 파서를 만들어달라고 해보자.

PEG.js 로 파서로 만들고 싶어, PEG 문법을 만들어줘

라고 제시를 하면 대략 아래와 같은 코드를 만들어준다.

start
  = component+

component
  = "@component" _ name:identifier _ "{" _ styles:style* templates:template? scripts:script? _ "}" { 
      return { 
        type: "component", 
        name, 
        styles, 
        templates, 
        scripts 
      }; 
    }

style
  = property:identifier ":" _ value:identifier ";" _ { 
      return { 
        type: "style", 
        property, 
        value 
      }; 
    }

template
  = "@template" _ "{" _ html:htmlContent _ "}" { 
      return { 
        type: "template", 
        html 
      }; 
    }

script
  = "@script" _ "{" _ code:jsContent _ "}" { 
      return { 
        type: "script", 
        code 
      }; 
    }

identifier
  = [a-zA-Z_][a-zA-Z0-9_]*

htmlContent
  = [^}]*

jsContent
  = [^}]*

_ "whitespace"
  = [ \t\n\r]*

이 코드는 동작 할 수도 있고, 안할 수도 있다. 그건 PEG.js 를 돌려봐야 하는 문제다.
그리고 내가 생각한 문법이 맞을 수도 있고 틀렸을 수도 있다.
PEG 문법 하에서 파싱 우선순위 같은 것도 영향을 줄 수 있다.

가장 큰 문제는 내가 PEG 문법을 공부해본적이 없다는 것이다.
하지만 GPT-4 를 쓰면 만들어 낼 수 있다.

내가 모르는 문제를 테스트 하려면 어떻게 해야할까?

PEG 문법을 테스트 해보자.

보통 언어를 만들 때 AST 라는 것을 만들게 된다.
AST 는 Abstract syntax tree 로 언어를 하나의 데이타로 보고 트리 형태의 구조로 만들어 둔 것을 말한다.
이것이 있으면 내가 파싱을 잘 하고 있는지 아닌지 알 수 있다.

그래서 GPT-4 또 만들어 달라고 한다.

만들어진 PEG 문법을 테스트 할 수 있는 vitest 테스트 코드를 만들어줘

그러면 대략 이런 코드를 만들어준다.

import { describe, it, expect } from 'vitest';
import { parse } from './path/to/your/parser'; // PEG.js 파서 import

describe('PEG.js Web Component Parser', () => {
  
  it('parses a basic component', () => {
    const input = `
      @component my-component {
        background-color: red;
        color: white;

        @template {
          <div class="my-component">
            <h1>Hello World</h1>
          </div>
        }

        @script {
          console.log("Component loaded");
        }
      }
    `;

    const expected = {
      type: "component",
      name: "my-component",
      styles: [
        { type: "style", property: "background-color", value: "red" },
        { type: "style", property: "color", value: "white" }
      ],
      templates: { type: "template", html: '<div class="my-component">\n            <h1>Hello World</h1>\n          </div>' },
      scripts: { type: "script", code: 'console.log("Component loaded");' }
    };

    const result = parse(input);
    expect(result).toEqual(expected);
  });

  // 추가적인 테스트 케이스들...
});

지금까지 난 코드 작업을 거의 하지 않았음에도 불구하고, 언어를 정하고, 그걸 파싱할 수 있는 PEG 문법을 얻었고, 이걸 테스트 할 수 있는 AST 구조도 발견했다.

이 코드가 바로 돌아가진 않지만, 내가 무엇을 해야하는지는 알 수 있다.
이제부터는 내가 정한 언어 스펙에 맞게 AST 를 계속 검증하고, 필요하면 PEG 문법을 업데이트 한 다음에 다시 테스트 코드를 만들어 달라고 하면 되는 것이다.

이 과정을 반복하면 내가 원하는 언어의 AST 를 만들 수 있게 된다.


최근 3일 동안 만들어 본 언어의 최종 스펙의 PEG 문법이다.
주석도 gpt-4 에게 넣어달라고 했다.

{
  // Helper function to remove whitespace nodes
  function filterWhitespace(nodes) {
    return nodes.filter(node => {
      if (typeof node === 'string') return node.trim() !== '';
      return node;
    });
  }

  // Helper function to create a text node
  function createTextNode(text) {
    return { type: 'TextNode', value: text.trim() };
  }
}

// The entry point of the grammar
Start
  = components:Component+ { return components; }

// Defines a component
Component
  = "@component" _ id:identifier _ "{" _ body:ComponentBody _ "}" {
      return { type: "Component", id, body };
    }

// Body of a component, can contain various types of content
ComponentBody
  = content:(_ Attribute _ / _ Event _ / _ Style _ / _ Function _ / _ Template _)* {
      return filterWhitespace(content.flat(Infinity));
    }

// Defines an attribute of a component
Attribute
  = "@attribute" _ id:identifier ":" _ value:attributeValue _ ";" {
      return { type: "Attribute", id, value };
    }

// Possible values for an attribute
attributeValue
  = StringLiteral / NumberLiteral / BooleanLiteral / identifier

// Defines an event handler
Event
  = "@event" _ eventName:identifier _ eventArgs:EventArgs? _ "{" _ eventBody:EventBody _ "}" {
      return { type: "Event", eventName, args: eventArgs || [], body: eventBody };
    }

// Event arguments
EventArgs
  = "(" _ argList:identifierList _ ")" {
      return argList;
    }

// List of identifiers, used for event arguments
identifierList
  = first:identifier _ rest:("," _ identifier)* {
      return [first, ...rest.map(r => r[2])];
    }

// Body of an event, can contain selectors and JavaScript sections
EventBody
  = sections:(_ SelectorRule _ / _ JavaScriptSection _)* {
      return filterWhitespace(sections.flat(Infinity));
    }

// JavaScript code section within an event
JavaScriptSection
  = "@js" _ "{" _ code:JavaScriptCode _ "}" {
      return { type: "JavaScript", code };
    }

// JavaScript code, can be a mix of text and variables
JavaScriptCode
  = code:(_ JavaScriptCodeChunk _)* {
      return filterWhitespace(code.flat(Infinity)).map(it => {
        if (typeof it === 'string') return { type: 'JavaScriptCodeChunk', value: it.trim() };
        return it;
      });
    }

// Chunk of JavaScript code, either a variable or plain text
JavaScriptCodeChunk
  = Variable / NonVariableChunk

// Plain text chunk within JavaScript code
NonVariableChunk
  = chars:[^{}]+ {
      return chars.join('');
    }

// Selector rule within an event or function
SelectorRule
  = "@selector" _ selector:selector _ "{" _ properties:CSSPropertiesWithVars _ "}" {
      return { type: "CSSRule", selector, properties };
    }

// CSS properties, allowing variables
CSSPropertiesWithVars
  = properties:(_ CSSPropertyWithVar _ / _ CSSProperty _)* {
      return filterWhitespace(properties.flat(Infinity));
    }

// CSS property that includes a variable
CSSPropertyWithVar
  = property:property _ ":" _ value:Variable _ ";" {
      return { type: "CSSPropertyWithVar", property, value };
    }

// Variable within CSS or JavaScript
Variable
  = "var(" _ varName:identifier _ ")" {
      return { type: "Variable", varName };
    }

// Defines a function
Function
  = "@function" _ functionName:identifier _ functionArgs:FunctionArgs? _ "{" _ functionBody:FunctionBody _ "}" {
      return { type: "Function", functionName, args: functionArgs ? functionArgs : [], body: functionBody };
    }

// Arguments of a function
FunctionArgs
  = "(" _ argList:identifierList _ ")" {
      return argList;
    }

// Body of a function, can contain selectors and JavaScript sections
FunctionBody
  = sections:( _ SelectorRule _ / _ JavaScriptSection _)* {
      return filterWhitespace(sections.flat(Infinity));
    }

// Defines CSS styles
Style
  = "@style" _ "{" _ rules:CSSRules _ "}" {
      return { type: "Style", rules };
    }

// Template of a component
Template
  = "@template" _ "{" _ content:TemplateContent _ "}" {
      return { type: "Template", content: filterWhitespace(content.flat(Infinity)) };
    }

// Content of a template, including conditional rendering, repeat rendering, elements, and text
TemplateContent
  = elements:(_ ConditionalRendering _ / _ RepeatRendering _ / _ Element _ / _ Text _ )* { 
    return filterWhitespace(elements.flat(Infinity)); 
}

// Conditional rendering rules
ConditionalRendering
  = _ ifContent:IfConditionalRendering _ elseContent:ElseConditionalRendering? _  {
      return { type: "Conditional", ifContent, elseContent };
    }

// 'If' part of conditional rendering
IfConditionalRendering
  = "@if" _ condition:JavaScriptCode _ "{" _ content:TemplateContent _ "}" {
      return { type: "IfConditional", condition, content };
    }

// 'Else' part of conditional rendering
ElseConditionalRendering
  = "@else" _ "{" _ content:TemplateContent _ "}" {
      return { type: "ElseConditional", content };
    }    

// Repeat rendering rule
RepeatRendering
  = "@repeat" _ variable:identifier _ "in" _ collection:JavaScriptCode _ "{" _ content:TemplateContent _ "}" {
      return { type: "Repeat", variable, collection, content };
    }

// HTML element
Element
  = tag:TagName _ "{" _ content:(_ Element _ / _ Text _)* _ "}" {
      return { type: "Element", tag, content: filterWhitespace(content.flat(Infinity)) };
    }

// Tag name of an HTML element
TagName
  = id:identifier { return id; }

// Text node
Text
  = chars: [^{}\n]+ { return createTextNode(chars.join('')); }  

// CSS rules
CSSRules
  = rules:CSSRule* { return rules; }

// Single CSS rule
CSSRule
  = selector:selector _ "{" _ properties:CSSProperties _ "}" {
      return { type: "CSSRule", selector, properties };
    }

// CSS properties
CSSProperties
  = properties:CSSProperty* { return properties; }

// Single CSS property
CSSProperty
  = property:property _ ":" _ value:value _ ";" {
      return { type: "CSSProperty", property, value };
    }

// Whitespace
_ "whitespace"
  = [ \t\n\r]*

// Identifier (includes complex identifiers with dot notation)
identifier "identifier"
  = head:[a-zA-Z_] tail:[a-zA-Z0-9_.]* { return head + tail.join(''); }

// String literal
StringLiteral "string"
  = DoubleQuotedString / SingleQuotedString / BacktickQuotedString / TripleDoubleQuotedString
  
DoubleQuotedString "double quoted string"  
  = "\"" chars:[^\"]* "\"" { return chars.join(''); }

SingleQuotedString "single quoted string"
  = "'" chars:[^']* "'" { return chars.join(''); }

BacktickQuotedString "backtick quoted string"
  = '`' chars:[^`]* '`' { return chars.join(''); }

TripleDoubleQuotedString "triple double quoted string"
  = '"""' chars:(TripleDoubleQuotedChar*) '"""' { return chars.join(''); }

TripleDoubleQuotedChar
  = !'"""' char:. { return char; }

// Number literal
NumberLiteral "NumberLiteral"
  = digits:[0-9]+ { return parseInt(digits.join('')); }

// Boolean literal
BooleanLiteral "BooleanLiteral"
  = "true" { return true; }
  / "false" { return false; }

// CSS selector
selector "CSS selector"
  = chars:[^{]+ { return chars.join('').trim(); }

// CSS property
property "CSS property"
  = chars:[^:]+ { return chars.join('').trim(); }

// CSS value
value "CSS value"
  = chars:[^;]+ { return chars.join('').trim(); }

참고로 난 PEG 문법을 이번에 처음 봤다. 문서도 보지 않았다.

결론

지금까지 간단하게 gpt-4 를 사용해서 어떻게 언어를 만들어갈까 고민하다가 나온 결과물이다.
gpt-3.5 로 이 작업을 했으면 상당히 힘들었을 수도 있다.
문맥이 이어지지 않아서 매번 다른 코드가 나오는 경우가 많은데 gpt-4 는 이해의 수준이 다르다. (이걸 이해라고 불러야 할지 조금 고민 되지만 )

암튼, 이번에 느낀건 어떤 일이든 질문을 잘 던지는 것과 의도를 명확하 하는 것이 상당히 중요하다는 것을 느꼈다.

내가 언어를 만들거라고 컴퓨터 랭귀지 책을 보고 파서를 처음부터 연구하며 만들었으면 간단한 언어 스펙 하나 파싱하는것만 해도 대략 6개월이 넘게 걸렸을 수도 있다.

하지만 GPT-4 는 그 중간 과정을 아주 드라마틱 하게 넘어가게 해준다. 내가 잘 몰라도 의도만 명확히 하면 바로 방향을 바꿔준다.

확실히 세상이 많이 바뀌었다는 것을 느끼고 있다.
앞으로 더 큰 변화가 올 것 같은 느낌도 들고 있다.

이제 개발자로써 무엇을 해야할지 점점 더 어려워지고 있지만, 오히려 더 선명해지고 있기도 한 듯 하다.

3일동안 재밌게 잘 놀았다.

앞으로 해야할 것들

  • SaLang 명세를 좀 더 테스트 해본다.
  • PEG 문법을 더 강화한다.
  • AST 기반의 테스트 코드를 많이 만든다.
  • 정리된 AST 기반으로 웹컴포넌트를 만들 수 있는 코드 제너레이터를 만든다.
  • VSCode 에 문법정의 기능을 적용해본다.

등등 수많은 일들이 있을 것 같지만 이건 내년에 조금씩 해보자.

profile
행복개발자

2개의 댓글

comment-user-thumbnail
2023년 12월 20일

https://github.com/kode-team/salang

마지막 샘플 테스트

답글 달기
comment-user-thumbnail
2024년 1월 10일

엄청 흥미로운 내용이네요. 잘 봤습니다!

답글 달기