본문 바로가기

개발

protocol-buffers-schema 로 보는 proto 구조 이해하기(2)

이전글에 다 못쓴 내용을 적을려한다

 

read tokens

case syntax

case 'message'

case 'enum'

case 'option'

enum에서와 token을 읽고, 세미콜론으로 라인을 잡지만, option의 값은 Map일수있기에 onoptionMap을 호출해주는방식으로 Map을 다룬다.

 

case 'import'

var onimport = function (tokens) {
  tokens.shift()
  var file = tokens.shift().replace(/^"+|"+$/gm, '')
  ...
  return file
}

파일 경로에서 파일명을 반환

 

 

case 'extend'

var onextend = function (tokens) {
  var out = {
    name: tokens[1],
    message: onmessage(tokens)
  }
  return out
}

확장하려는 타입명을 포함시키고, 필드들에 대해서 얻기위해 onmessage를 사용.

case 'service'

service에는 RPC (Remote Procedure Call) system을 정의하기에 

var onservice = function (tokens) {
  tokens.shift()

  var service = {
    name: tokens.shift(),
    methods: [],
    options: {}
  }

  if (tokens[0] !== '{') throw new Error('Expected { but found ' + tokens[0])
  tokens.shift()

  while (tokens.length) {
    if (tokens[0] === '}') {
      tokens.shift()
      // there goes optional semicolon after the enclosing "}"
      if (tokens[0] === ';') tokens.shift()
      return service
    }

    switch (tokens[0]) {
      case 'option':
        var opt = onoption(tokens)
        if (service.options[opt.name] !== undefined) throw new Error('Duplicate option ' + opt.name)
        service.options[opt.name] = opt.value
        break
      case 'rpc':
        service.methods.push(onrpc(tokens))
        break
      default:
        throw new Error('Unexpected token in service: ' + tokens[0])
    }
  }

  throw new Error('No closing tag for service')
}

읽어내리는 토큰이 rpc인 경우 methods에 rpc 를 추가해주는데 이과정은 onrpc 함수에서 이루어진다.

var rpc = {
    name: tokens.shift(),
    input_type: null,
    output_type: null,
    client_streaming: false,
    server_streaming: false,
    options: {}
  }

rpc선언에는 위와 같은 선언요소들이 필요하다.

var onrpc = function (tokens) {
  tokens.shift()

  var rpc = {
    name: tokens.shift(),
    input_type: null,
    output_type: null,
    client_streaming: false,
    server_streaming: false,
    options: {}
  }

  if (tokens[0] !== '(') throw new Error('Expected ( but found ' + tokens[0])
  tokens.shift()

  if (tokens[0] === 'stream') {
    tokens.shift()
    rpc.client_streaming = true
  }

  rpc.input_type = tokens.shift()

  if (tokens[0] !== ')') throw new Error('Expected ) but found ' + tokens[0])
  tokens.shift()

  if (tokens[0] !== 'returns') throw new Error('Expected returns but found ' + tokens[0])
  tokens.shift()

  if (tokens[0] !== '(') throw new Error('Expected ( but found ' + tokens[0])
  tokens.shift()

  if (tokens[0] === 'stream') {
    tokens.shift()
    rpc.server_streaming = true
  }

  rpc.output_type = tokens.shift()

  if (tokens[0] !== ')') throw new Error('Expected ) but found ' + tokens[0])
  tokens.shift()

  if (tokens[0] === ';') {
    tokens.shift()
    return rpc
  }

  if (tokens[0] !== '{') throw new Error('Expected { but found ' + tokens[0])
  tokens.shift()

  while (tokens.length) {
    if (tokens[0] === '}') {
      tokens.shift()
      // there goes optional semicolon after the enclosing "}"
      if (tokens[0] === ';') tokens.shift()
      return rpc
    }

    if (tokens[0] === 'option') {
      var opt = onoption(tokens)
      if (rpc.options[opt.name] !== undefined) throw new Error('Duplicate option ' + opt.name)
      rpc.options[opt.name] = opt.value
    } else {
      throw new Error('Unexpected token in rpc options: ' + tokens[0])
    }
  }

  throw new Error('No closing tag for rpc')
}

 

 

지금까지 본과정들을 거치게되면, schema에 .proto의 내용들이 jsonObject형식으로 제공을 받을 수 있다.

protocol-buffers-schema의 parse의 핵심은 tokenize후. tokens를 하나씩인출하며 operaction에 대한 내용에 대해서 파악하는 방식으로, 의미와 무관한  { } [ ] ;  에 제거(무시)하며 읽어가는 과정으로 사람이 순차적으로 읽어나갈떄와 비슷한 과정이라 이해가 어렵진는 않았다.