Как разобрать имена типов, определенные во время разбора

Я использую pegjs для определения грамматики, которая позволяет определять новые типы. Как мне тогда распознать эти типы после их определения? У меня есть производство, которое определяет встроенные типы, например,

BuiltInType
  = "int"
  / "float"
  / "string"
  / TYPE_NAME

Но что мне делать для последнего? Я не знаю, какие возможные строки будут именами типов, пока они не будут определены в исходном коде.

В традиционном способе синтаксического анализа, где есть как лексер, так и парсер, синтаксический анализатор добавляет имя типа в таблицу, а лексер использует эту таблицу, чтобы определить, возвращать ли TYPE_NAME или IDENTIFIER для конкретного токена. Но у pegjs нет этого разделения.

1 ответ

Решение

Вы правы, вы не можете (легко) изменить сгенерированный парсером pegjs на лету, не зная много о его внутренностях. Но то, что вы теряете от стандартного LALR, вы получаете за счет встраивания кода JavaScript в сами правила синтаксического анализатора.

Для достижения вашей цели вам нужно будет распознать новые типы (в контексте) и сохранить их для последующего использования, например:

{
  // predefined types
  const types = {'int':true, 'float':true, 'string':true}

  // variable storage
  const vars = {}
}

start = statement statement* {
  console.log(JSON.stringify({types:types,vars:vars}, null, 2))
}

statement
  = WS* typedef EOL
  / WS* vardef EOL

typedef "new type definition" // eg. 'define myNewType'
  = 'define' SP+ type:symbol {
    if(types[type]) {
      throw `attempted redefinition of: "${type}"`
    }
    types[type]=true
  }

// And then, when you need to recognize a type, something like:

vardef "variable declaration" // eg: 'let foo:myNewType=10'
  = 'let' SP+ name:symbol COLON type:symbol SP* value:decl_assign? {
   if(!types[type]) {
     throw `unknown type encountered: ${type}`
   }
   vars[name] = { name: name, type:type, value: value }
}

decl_assign "variable declaration assignment"
  = '=' SP* value:number {
    return value
  }

symbol = $( [a-zA-Z][a-zA-Z0-9]* )

number = $( ('+' / '-')? [1-9][0-9]* ( '.' [0-9]+ )? )

COLON = ':'
SP = [ \t]
WS = [ \t\n]
EOL = '\n'

который, когда попросили разобрать:

define fooType
let bar:fooType = 1

напечатает:

{
  "types": {
    "int": true,
    "float": true,
    "string": true,
    "fooType": true
  },
  "vars": {
    "bar": {
      "name": "bar",
      "type": "fooType",
      "value": "1"
    }
  }
}
Другие вопросы по тегам