Есть ли способ применить тип Variant в моей рекурсивной функции - PullRequest
0 голосов
/ 15 февраля 2019

Я завершил последний вопрос моего назначения, в котором спрашивалось следующее:

"Добавить локальную конструкцию определения " let v = e в f ", где v - переменная иe и f являются выражениями. Это означает, что v имеет значение e с in f, и это переопределяет любое значение v в среде (или включающее в себя значение let). Как и в предыдущем примере, вам нужноподумайте о синтаксисе для этого, который вы можете легко проанализировать с расширением существующего синтаксического анализатора "

Моя проблема заключается в том, что предопределенный описанный ниже тип expr, используемый синтаксическим анализатором и компилятором в данной кодовой базе, не распознается в моей реализацииоператора let, и я получаю следующую ошибку:

Ошибка: это выражение имеет тип expr, но ожидалось выражение типа char

Я добавил функциональность всинтаксический анализатор, который переводит оператор let в форме " ~ var1 = exp1> exp2 ", где вывод синтаксического анализатора имеет форму Bes " (v1, e1, е2)".Это все проверено и работает.Мои проблемы возникают, когда я добавил регистр в оператор соответствия компилятора, который распознает только что упомянутую 2-ю форму;после сопоставления моя «препроцессорная» функция также вызывается, как показано ниже.Это должно взять v1, e1 и e2, совпадающие в компиляторе и рекурсивно совпадающие на e2, заменяя любой экземпляр переменной v1 выражением e1 перед возвратом этой обновленной формы выражения e2 для дальнейшей компиляции.Вместо этого я получаю ошибку соответствия выше.

ОПРЕДЕЛЕНИЕ ВЫРАЖЕНИЯ

type expr =
    Num of int
  | Var of char
  | Add of expr*expr
  | Mul of expr*expr
  | Con of expr*expr*expr
  | Bes of expr*expr*expr

ОПРЕДЕЛЕНИЕ ИНСТРУКЦИИ

type instr =
  | Push of int
  | Fetch of char
  | Add2
  | Mul2
  | Con2

ДРУГИЕ ВИДЫ

type program = instr list

type stack = int list

ФУНКЦИЯ МОЕГО ПРЕДПРОЦЕССОРА

let rec preprocessor eo2 eo1 vo1  : expr =

    match eo2 with
    | Num n -> eo2
    | Var v -> if (v = vo1) then eo1 else eo2
    | Add (e1, e2) -> Add ((preprocessor e1 eo1 vo1),(preprocessor e2 eo1 vo1))
    | Mul (e1, e2) -> Mul ((preprocessor e1 eo1 vo1),(preprocessor e2  eo1 vo1))
    | Con (e1,e2,e3) -> Con ((preprocessor e1 eo1 vo1), (preprocessor e2     eo1 vo1), (preprocessor e3 eo1 vo1))
(*  | Bes (v1,e1,e2) -> (preprocessor e2 e1 v1) *)

ФУНКЦИЯ КОМПИЛЕРА

(*
compile : expr -> instr list
*)

let rec compile e =


  match e with
  | Num n -> [Push n]
  | Var v -> [Fetch v]
  | Add (e1,e2) -> compile e2 @ compile e1 @ [Add2]
  | Mul (e1,e2) -> compile e2 @ compile e1 @ [Mul2]
  | Con (e1,e2,e3) -> compile e3 @ compile e2 @ compile e1 @ [Con2]
  | Bes (v1,e1,e2) -> compile (preprocessor e2 e1 v1) @ [] (*ERROR SOURCE LINE*)

Я ожидаю, что функция препроцессора изменит любые переменные в под-выражении e2 на выражение e1, которое затем можно будет продолжить в процессе компиляции.Вместо этого я получаю сообщение об ошибке, сообщающее, что вместо ожидаемого символа был указан expr.Я пробовал несколько вещей, таких как использование операторов let для назначения e2 e1 и v1 новым переменным, где я явно предоставлял им типы expr (e2: expr) и т. Д., И я также пытался явно вернуть expr из препроцессора, но я все еще застрял,Кодовая база казалась слишком большой, чтобы просто выкидывать сюда, так что, если мне следовало опубликовать функциональные парсеры, дайте мне знать.Спасибо за любую помощь

1 Ответ

0 голосов
/ 15 февраля 2019
  1. Ошибка возникает из-за v1, имеющего тип expr, в то время как вам нужно char, так как вы сравниваете v1 с каждым v или Var v, которые имеют типchar.Корень проблемы в том, что ваш конструктор Bes имеет неправильные типы, это должно быть Bes of char * expr * expr, поскольку v в конструкции let v = x in y вашего языка должна быть переменной, которая представлена ​​с типом charв вашей реализации.

  2. Использование предварительной обработки для реализации let не очень хорошая идея:

    2.1.Это взорвет ваш код, рассмотрим следующий пример:

    let x = <very-big-expr> in
    let y = x + x + x in
    y + y
    

    в конечном итоге будет дублировать <very-big-exp> 6 раз.В общем, это будет экспоненциальный взрыв, который приведет к гигабайту AST

    2.2.Дублирование выражения не всегда допустимо, например, в языках, где выражения могут иметь побочные эффекты, это нарушит семантику программы, рассмотрим следующий пример

    let x = read_int () in
    let y = read_int () in
    x*x + y
    

    при условии, что входные данные "3\n4\n",правильная реализация должна возвращать 3 * 3 + 4 = 13, а ваша реализация приведет к коду:

    read_int () * read_int () + read_int ()
    

    , который будет читать два целых числа, умножать их и запрашивать третье число и завершаться с ошибкой в ​​концеошибка канала.

    Это означает, что вы должны сохранять Bes как примитив вашего языка и правильно его компилировать.

  3. Если вы решите придерживаться этапа предварительной обработки, вам не следует добавлять Bes к набору примитивов из первых рук и выполнять перевод AST-> ASTкаждый раз, когда ваш синтаксический анализатор видит оператор let.Тогда вы никогда не увидите Bes в коде компилятора.Это по существу сделает let синтаксическим сахаром или макросом, что, опять же, является неверной семантикой Let, см. (1) и (2) выше.

...