옛날에 썼던 JSON Parser 읽기.

36121 단어 RustJSONtech
이 기사는'Wantedly 졸업생 Advent Calendaar 2021'의 15일째 기사다.
늦을 거 없어!(거짓말...16일 현재 공개)
집필을 했지만 소재가 별로 없어 마음이 설렌다.
소재를 찾다가 자신의 GiitHub 창고를 찾았지만 기억하지 못하는 json Parer가 있었어요.(그게 뭐가 무서워)
코드를 살짝 불러내면 재밌을 것 같은 예감이 들기 때문에 json parter 코드를 읽으면서 이해하고, 과거 자신의 코드에 개그 같은 걸 넣어서 같이 놀고 싶어요!
코드의 설명은 잘 하려고 했지만 잡담이 섞인 보도가 되었다!

tumekiri


이번에는 읽을 코드의 개요를 소개하겠습니다.
창고는 여기 있습니다.디덴!
https://github.com/k-nasa/tumekiri
제이슨 파서의 이름은 투미키리라고 합니다.
확실히 이 시기에는 처음 보는 것들의 이름을 짓는다.손톱깎이 근처에 있죠 w
다른 건 멘비라는 창고도 있어요.
이제 이름 짓는 느낌이 들어서 이렇게 되지는 않을 거야!마땅히!대략!
이 JSON Parser(이하 Parser라고 함)는 Rust로 쓴 것이다.
코드는 테스트를 포함하여 모두 300줄로 매우 짧다.(clone을 컴파일한 후 일반적인 동작인 것을 발견하여 기분이 좀 좋았다.)
~/lab/product/tumekiri / - master
:) % tokei
===============================================================================
 Language            Files        Lines         Code     Comments       Blanks
===============================================================================
 Markdown                1           16            0           11            5
 Rust                    2          414          376            3           35
 TOML                    1            9            6            1            2
===============================================================================
 Total                   4          439          382           15           42
===============================================================================

테스트 코드 읽기


README가 너무 스팸이어서 아무런 정보도 얻지 못해 테스트 코드를 보면서 파트너의 규격을 보기로 했다.(현재 README, issue,pr의 description 정보도 적기 때문에 변화가 없는 것 같다.)
테스트 용례를 보면 type에 따라 퍼스를 진행할 수 있는지 없는지를 알 수 있다.
#[test]
fn parse_string_test() {
...
}

#[test]
fn parse_number_test() {
...
}

#[test]
fn parse_array_test() {
...
}

#[test]
fn parse_object_test() {
...
}

#[test]
fn parse_bool_and_null_test() {
...
}
가장 복잡한 Object 테스트를 봅시다.
#[test]
fn parse_object_test() {
    let input = r#"
{
  "squadName": "Super hero squad",
  "homeTown": "Metro City",
  "formed": 2016,
  "secretBase": "Super tower"
  }
        "#;

    let parse_result = JsonParser::new(input.chars()).parse();

    assert!(parse_result.is_ok());

    let _value = parse_result.unwrap();

    // FIXME テスト書くのすごくめんどくさい。。。。プギャーーー
    // assert_eq!(value, JsonValue::Object());
}
뭐랄까, 이 코드에 대한 평론은 정말 감탄스럽다...
'나 아니야'하는 마음밖에 없어요.
어렵기 때문에 그를 대신해서 시험을 쓰기로 했다.
JsonObject 조립하는 거 되게 싫어하시나 봐요, ww.
이번 시험에 합격했기 때문에 괜찮은 상태에서 일하는 것 같아요.
#[test]
fn parse_object_test() {
    let input = r#"
{
  "squadName": "Super hero squad",
  "homeTown": "Metro City",
  "formed": 2016,
  "secretBase": "Super tower",
  }
        "#;

    let parse_result = JsonParser::new(input.chars()).parse();

    let value = parse_result.unwrap();

    let h = HashMap::from_iter(
        [
            (
                "squadName".to_owned(),
                JsonValue::String("Super hero squad".to_string()),
            ),
            (
                "homeTown".to_owned(),
                JsonValue::String("Metro City".to_string()),
            ),
            ("formed".to_owned(), JsonValue::Number(2016.0)),
            (
                "secretBase".to_owned(),
                JsonValue::String("Super tower".to_string()),
            ),
        ]
        .iter()
        .cloned(),
    );

    assert_eq!(value, JsonValue::Object(h));
}

code reading


그럼 중요한 이브닝 파티를 읽어볼까요?
테스트 코드를 보고 다음 코드의 input,output을 테스트했습니다.
이에 따라 이번JsonParser 구조체parse 방법이 접수처가 됐다.
JsonParser::new(input.chars()).parse();
JsonParer의 정의를 살펴보겠습니다.
열네크스를 낭비하고 있소.ITERATOr TRAIT를 설치하면 뭐든지 할 수 있을 것 같아요.수신된 문자열의 이동전화는 structPeekable로 싸서 사용한다.이거 먼저 읽을 수 있는 이동전화야. 돌려준 거야.이동전화를 추천하지 않으면 다음 값을 먼저 읽을 수 있다.
나머지는 행과 열도 있다.(난 Row가 아니라고 생각해,column)
use std::iter::Peekable;

pub struct JsonParser<C: Iterator<Item = char>> {
    chars: Peekable<C>,
    col: usize,
    line: usize,
}


impl<C> JsonParser<C>
where
    C: Iterator<Item = char>,
{
    pub fn new(input: C) -> Self {
        JsonParser {
            chars: input.peekable(),
            col: 0,
            line: 0,
        }
    }
}
다음 주요 방법을 살펴보겠습니다parse하는 일은 아래 두 사람처럼 보인다.
  • Row,col을 기반으로 현재 커서가 있는 문자
  • 읽기
  • 어떤 jsonvalue(type)에 부합되는지 관찰하고 각종parse 방법
  • 을 호출합니다
    pub fn parse(&mut self) -> ParseResult {
        let first_char = match self.peek() {
            None => return self.error_result("Invalid input"),
            Some(c) => c,
        };
    
        match first_char {
            '"' => self.parse_string(),
            '0'..='9' | '-' => self.parse_number(),
            '{' => self.parse_object(),
            '[' => self.parse_array(),
            't' | 'f' | 'n' => self.parse_bool_and_null(),
            c => self.error_result(&format!("Unsupported charactor {}", c)),
        }
    }
    
    이때 왜self.나는 넥스트가 아니라 peek이라고 생각했지만 깊이 생각하지 않기로 결정했다
    처음 한 글자에 커서 같은 거 없나?
    peek 방법을 봐도 이유를 잘 모르겠어요.그래.
    공백을 넘기면서 줄을 바꾸는 느낌을 잘 처리하면서 input chars를 읽는다.
    fn peek(&mut self) -> Option<char> {
        while let Some(&c) = self.chars.peek() {
            if c == '\n' {
                self.col = 0;
                self.line += 1;
            }
    
            self.col += 1;
            if !c.is_whitespace() {
                return Some(c);
            }
            self.chars.next();
        }
    
        None
    }
    
    너무 힘들어서 마지막parse_string까지 다 읽어야 돼!300줄 코드를 다 읽기는 어려우니까 포기해!parse_string 호출된 것은peek의 문자가 "로 결정되어 검사 중입니다.
    이후 대응하는 닫기"를 읽을 때까지 최종 결과output가 되고 변수를push만 합니다.
    pub fn parse_string(&mut self) -> ParseResult {
        if self.chars.next() != Some('"') {
            return self.error_result("");
        }
    
        let mut output = String::new();
    
        loop {
            let c = match self.chars.next() {
                Some('"') => break,
                None => return self.error_result(""),
    
                Some(c) => c,
            };
    
            output.push(c)
        }
    
        Ok(JsonValue::String(output))
    }
    
    응.마지막은 간단한 인코딩이지만 괜찮죠.

    감상


    나는 블로그를 쓰면서 코드를 읽으며 심혈을 기울여 매우 기뻤다.
    기회가 된다면 누구와 함께 엉엉 코드를 읽고 싶은가.
    이번에 다 읽지는 못했지만 300줄 정도의 제이슨 파서로 쓰니까 놀랍네요.더 힘든 프로그램인 것 같아서요.
    앞으로도 어딘가에서 코드를 주워서 읽으면서 정리할 거예요!그냥 간단히 하고 있어 난 너무 좋아 www
    여기까지 읽어주신 분들도 조금이나마 즐기시면 정말 좋을 것 같아요!
    안녕히 주무세요.

    좋은 웹페이지 즐겨찾기