Есть ли способ применить тип Variant в моей рекурсивной функции

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

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

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

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

Я добавил функциональность в синтаксический анализатор, который переводит оператор let в форме "~ var1 = exp1> exp2", где вывод синтаксического анализатора имеет форму Bes "(v1, e1, e2)". Это все проверено и работает. Мои проблемы возникают, когда я добавил регистр в оператор соответствия компилятора, который распознает только что упомянутую 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 ответ

Решение
  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) выше.

Другие вопросы по тегам