Есть ли способ применить тип 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 ответ
Ошибка исходит от
v1
имея типexpr
пока тебе нуженchar
так как вы сравниваетеv1
с каждымv
или жеVar v
, которые имеют типchar
, Корень проблемы в том, что вашBes
конструктор имеет неправильные типы, он должен бытьBes of char * expr * expr
, посколькуv
вlet v = x in y
конструкция вашего языка должна быть переменной, которая представленаchar
введите вашу реализацию.Использование предварительной обработки для реализации let не очень хорошая идея:
2.1. Это взорвет ваш код, рассмотрим следующий пример:
let x = <very-big-expr> in let y = x + x + x in y + y
в конечном итоге в дублировании
<very-big-exp>
6 раз В общем, это будет экспоненциальный взрыв, который приведет к гигабайту AST2.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
как примитив вашего языка и правильно его скомпилировать.Если вы решите придерживаться этапа предварительной обработки, то вам не следует добавлять
Bes
к набору примитивов из первых рук, и выполняйте перевод AST->AST каждый раз, когда ваш анализатор видит оператор let. Тогда ты никогда не увидишьBes
в коде компилятора. Это по существу сделаетlet
синтаксический сахар или макрос, который, опять же, неверная семантика Let, см. (1) и (2) выше.