프로그래밍 언어를 만들고 있습니다: 영패 만들기

지난번에 나는 너희들에게 내가 왜 이런 프로그래밍 언어를 해야 하는지, 그리고 내가 어떻게 해야 하는지를 알려주었다.여기서 우리는 코드 파일을 읽고 그것을 사용하기 시작할 것이다.
프로그래밍 언어의 코드를 평가할 때 원본 텍스트를 계속 사용할 수 없습니다.봐라, 텍스트를 처리하는 것은 매우 비싸다. 예를 들어, 텍스트를 처리하는 것은 비교적 느리다.잊지 마라, 무거워, 내가 너에게 어떻게 하는지 알려줄게.
코드에 모듈 (함수 집합) 이 있다고 가정하십시오.
module  my_module {

    function say_hello() {
        let name = my_module.get_name()
        print("Hello, ${name}!")
    }

    function get_name() {
        print("What is your name? ")
        let name = get_input()
        return name
    }

}

그런 다음 다음과 같이 모듈에서 함수를 호출했다고 가정합니다.
my_module.say_hello()
텍스트만 사용할 경우 파일에서 my_module 모듈을 검색한 다음 함수say_hello를 검색해야 합니다.좋아, 너는 이 기능을 가지고 있어.문제는 이 함수 내부에 쌍my_module.get_name의 호출이 있다는 것이다.그래서 파일에서 이 모듈을 다시 찾아야 해요. 그리고 이 모듈에서 함수를 찾아야 해요...

원본 텍스트를 처리하는 것이 더 느려질 수 있다는 것을 깨달았다.물론 현대 하드웨어에 얼마나 걸리는지 두 가지 기능만 있다고 말할 수도 있다.네가 옳다. 이 예에서 오래 걸리지 않을 것이다.그러나 코드 파일은 보통 상당히 길기 때문에 큰 파일에서 한 번 또 한 번 이렇게 하는 데 시간이 오래 걸린다는 것을 너도 알고 있다.
이것이 바로 우리가 텍스트를 계속 직접 처리하고 싶지 않은 이유이다.우리는 코드에서 메모리의 추상을 구축한 다음에 추상을 바탕으로 추상을 구축하여 우리의 프로그래밍 언어를 더욱 쉽게 만들 수 있도록 텍스트를 한 번 읽었다.첫 번째 단계는 어법 분석이라고 한다.
어법 분석에서, 우리는 텍스트 형식으로 원시 코드를 두루 훑어보고, 이를 표기로 변환하는데, 이것은 우리가 만들 수 있는 가장 작은 추상적인 단계이다. (내가 아는 바에 의하면)
내가 너에게 간단한 표현식이 어떻게 표시되는지 알려줄게.간단한 표현을 고려합니다.
(+ 2 5)
다음과 같이 토큰 흐름으로 전환해야 합니다.
[
  Token { kind: OpenParen, value: None },
  Token { kind: Plus, value: None },
  Token { kind: Literal(Number), value: 2.0 },
  Token { kind: Literal(Number), value: 5.0 },
  Token { kind: CloseParen, value: None }
]
나는 이 단계를 완성하기 위해 몇 가지 방법을 연구했다.하나는 단독 어법 분석기를 작성하는 것이고, 다른 하나는 해석기 조합기를 작성하는 것이다.해석기 조합기는 놀랍지만 내가 가고 싶었던 길은 아니었다.그 밖에 나는 원본 파일을 문자별로 스캔하는 것에 흥미가 없어서, 이 일을 간소화하기 위해 라이브러리를 찾기 시작했다.그때 나는 우연히 표지를 발견했다.

마시예르츠 / 표식


터무니없이 빠른 어휘량을 창조해 내다



표식






터무니없이 빠른 어휘량을 창조해 내다.
로고는 두 가지 목표가 있다.
  • Lexer를 쉽게 만들 수 있으므로 보다 복잡한 문제에 집중할 수 있습니다.
  • 손으로 쓴 것보다 생성된 렉서가 빠르다.
  • 이러한 목표를 실현하기 위해 로고:
  • 모든 영패의 정의를 하나로 합친다deterministic state machine.
  • 분기를 lookup tables 또는 jump tables로 최적화합니다.
  • 내부 영패 정의를 막는다.

  • backtracking 경계 검사를 최소화하기 위해 대량으로 읽습니다.
  • 컴파일할 때 이 모든 번거로운 작업을 완성한다.
  • 예.

    use logos::Logos
    #[derive(Logos, Debug, PartialEq)]
    enum Token {
        // Tokens can be literal strings, of any length.
        #[token("fast")]
        Fast
        #[token(".")]
        Period
        // Or regular expressions.
        #[regex("[a-zA-Z]+")]
        Text
        // Logos requires one token variant to handle errors,
        // it can be named anything you wish.
        #[error]
        // We can also use this variant to define whitespace,

    It is a library that generates optimised, fast lexers and that are extremely easy to use. Since they claim to generate lexers that are faster than anything I could write by hand, and is used by lots of people, I guess I gotta take their word for it. They also promised that it will be extremely easy to use, and what do you know, it really is.

    It is as easy as mapping an enum value to a string or regex expression, and Logos does everything for you.

    #[derive(Debug, PartialEq, Logos)]
    pub enum TokenKind<'a> {
        #[token("(")]
        OpenParen,
    
        #[token(")")]
        CloseParen,
    
        #[token("+")]
        Plus,
    
        #[token("-")]
        Minus,
    
        #[regex("-?([0-9])+", |lex| lex.slice().parse())]
        Number(f64),
    
        #[regex(r"[ \t\n\f]+", logos::skip)]
        Whitespace,
    
        #[error]
        Error,
    }
    
    네, 이것은 아주 간단한lexer입니다.그게 바로 내가 지금 원하는 거야.나는 그것이 작고 간단한 표현식을 해석할 수 있기를 바란다.이렇게:
    (+ 2 (- 4 6) 8)
    
    아래에서 보듯이 그것은 확실히 이 점을 해냈다.
    방금 읽은lexer 코드는 자동으로 아래의 태그 목록을 생성하지 않습니다.Token는 내가 Rust 코드 라이브러리에서 본 추상과 유사하다.그것은 해석기가 이 영패 흐름을 사용할 때 해야 할 일을 줄였다.
    [
      Token { kind: OpenParen, value: None }, 
      Token { kind: Plus, value: None }, 
      Token { kind: Literal(Number), value: Some(Value::Number(2.0)) }, 
      Token { kind: OpenParen, value: None }, 
      Token { kind: Minus, value: None }, 
      Token { kind: Literal(Number), value: Some(Value::Number(4.0)) }, 
      Token { kind: Literal(Number), value: Some(Value::Number(6.0)) }, 
      Token { kind: CloseParen, value: None }, 
      Token { kind: Literal(Number), value: Some(Value::Number(8.0)) }, 
      Token { kind: CloseParen, value: None }, 
    ]
    
    이 영패 흐름에서 보신 Some 은 Rust의 Option 형식입니다. 일부 영패는value 필드에 아무런 소용이 없기 때문입니다.
    개발 과정에서 어떤 형태로든 원본 코드에서 실행 가능한 바이너리 파일로 바꿀 수 있기를 희망했던 것을 기억하십니까?지금 나는 이 간단한 표현식을 계산하고 결과를 내고 싶다.
    이를 위해, 나는 간단한 창고 기반 방법을 사용할 것이다.기본적으로 모든 영패를 창고에 밀어넣고 CloseParen를 받았을 때 이 표현식의 값을 팝업하고 표현식의 결과를 창고로 되돌려줍니다.
    코드를 실행했습니다.
    Unwinds loops

    성공했어!보시다시피 결과는 8의 영패입니다.
    나는 지금 이렇게 되고 싶다.다음 게시물 만나요.
    주의(이것은 나의 마지막 약속): 나의 게시물의 질을 받아들일 수 있는 수준으로 유지하기 위해 나는 내가 언어에 기능을 추가하는 속도보다 게시물을 제출하는 속도가 훨씬 느리다.
    코드 라이브러리를 보고 싶거나 참여하고 싶으면 여기로 오세요.

    / 파라자흐마드



    Tisp(Lisp 입력)


    입력하고 컴파일할 수 있는Lisp와 유사한 프로그래밍 언어그 목적은
    LLVM에서 다중 프로세서 아키텍처를 지원합니다.필요하다
    Rust, Lisp, Elixir 등의 프로그래밍 언어에서 영감을 얻었습니다.

    현재 작업 예


    처음 5개의 피보나 계수를 계산하는 프로그램:
    (우리 먼저 0)
    (두 번째 1에게 양보)
    (작은 거짓말)
    (n0 설정)
    (while((fib(+1초))
    (두 번째부터 보자)
    (작은 거짓말을 먼저 한다)
    (설정n(+n1))
    (작은 거짓말 인쇄)
    )

    구성할 기능


    원본 코드를 영패 흐름으로 변환영패 흐름을 표현식 트리로 변환다중 중첩 표현식 처리파일당 여러 표현식현재 지원되는 기능에 대한 LLVM IR 생성llvm을 실행하는 CLI 플래그 추가
    while 순환 추가선언 변수중첩된while 순환 추가에 유형 추가 tisp

    좋은 웹페이지 즐겨찾기