it-swarm.com.ru

Написать переводчик Haskell в Haskell

Классическим упражнением в программировании является написание интерпретатора LISP/Scheme на LISP/Scheme. Сила полного языка может быть использована для создания переводчика для подмножества языка.

Есть ли подобное упражнение для Haskell? Я хотел бы реализовать подмножество Haskell, используя Haskell в качестве движка. Конечно, это можно сделать, но есть ли доступные онлайн-ресурсы для просмотра?


Я изучаю идею использования Haskell в качестве языка для изучения некоторых концепций в дискретных структурах курсе, который я преподаю. В этом семестре я остановился на Miranda , меньшем языке, который вдохновил Haskell. Миранда делает около 90% того, что я хотел бы сделать, но Хаскелл делает около 2000%. :)

Поэтому моя идея состоит в том, чтобы создать язык, обладающий именно теми характеристиками Haskell, которые мне нравятся, и запрещающий все остальное. По мере развития учеников я могу выборочно "включать" различные функции, когда они освоили основы.

Педагогические "языковые уровни" были успешно использованы для обучения Java и Scheme . Ограничивая то, что они могут делать, вы можете запретить им стрелять себе в ногу, пока они еще осваивают синтаксис и концепции, которым вы пытаетесь научить. И вы можете предложить лучшие сообщения об ошибках.

84
Barry Brown

Я люблю твою цель, но это большая работа. Пара подсказок:

  • Я работал над GHC, и вам не нужна какая-либо часть источников. Hugs - намного более простая и понятная реализация, но, к сожалению, она написана на C.

  • Это небольшая часть головоломки, но Марк Джонс написал прекрасную статью под названием Печатать на Haskell на языке Haskell , которая была бы отличной отправная точка для вашего переднего конца.

Удачи! Определение языковых уровней для Haskell, с подтверждающими данными из класса, было бы очень полезным для сообщества и определенно публикуемым результатом!

74
Norman Ramsey

Существует полный анализатор Haskell: http://hackage.haskell.org/package/haskell-src-exts

После того, как вы проанализировали это, легко удалить или запретить некоторые вещи. Я сделал это для tryhaskell.org, чтобы запретить операторы импорта, поддерживать определения верхнего уровня и т.д.

Просто разберите модуль:

parseModule :: String -> ParseResult Module

Тогда у вас есть AST для модуля:

Module SrcLoc ModuleName [ModulePragma] (Maybe WarningText) (Maybe [ExportSpec]) [ImportDecl] [Decl]    

Тип Decl обширный: http://hackage.haskell.org/packages/archive/haskell-src-exts/1.9.0/doc/html/Language-Haskell-Exts-Syntax.html#t% 3ADecl

Все, что вам нужно сделать, это определить белый список - из того, какие объявления, импорт, символы, синтаксис доступны, затем пройтись по AST и ​​выдать "ошибку разбора" на все, что вам не нужно быть в курсе еще. Вы можете использовать значение SrcLoc, прикрепленное к каждому узлу в AST:

data SrcLoc = SrcLoc
     { srcFilename :: String
     , srcLine :: Int
     , srcColumn :: Int
     }

Нет необходимости заново внедрять Haskell. Если вы хотите предоставить более дружественные ошибки компиляции, просто проанализируйте код, отфильтруйте его, отправьте его компилятору и проанализируйте выходные данные компилятора. Если это "не удалось сопоставить ожидаемый тип с логическим кодом a -> b", то вы знаете, что для функции, вероятно, слишком мало аргументов.

Если вы действительно не хотите тратить время на реализацию Haskell с нуля или возиться с внутренностями Hugs или какой-то глупой реализацией, я думаю, вам следует просто отфильтровать то, что передается в GHC. Таким образом, если ваши ученики захотят взять свою кодовую базу и перейти к следующему шагу и написать какой-то действительно полноценный код на Haskell, переход будет прозрачным.

37
Christopher Done

Вы хотите построить свой переводчик с нуля? Начните с реализации более простого функционального языка, такого как лямбда-исчисление или вариант LISP. Для последнего есть довольно хороший вики-сборник под названием напишите себе схему за 48 часов , дающий крутое и прагматичное введение в методы синтаксического анализа и интерпретации.

Интерпретация Haskell вручную будет намного более сложной, так как вам придется иметь дело с очень сложными функциями, такими как классы типов, чрезвычайно мощная система типов (вывод типов!) И ленивая оценка (методы сокращения).

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

Дополнение:

Обратите внимание, что в Haskell у вас есть полный доступ к API интерпретаторов (по крайней мере, в GHC), включая парсеры, компиляторы и, конечно, интерпретаторы.

Используемый пакет: подсказка (Language.Haskell. *) . К сожалению, я не нашел онлайн-учебников по этому вопросу и не опробовал сам, но это выглядит довольно многообещающе.

24
Dario

создайте язык, который имеет именно те функции Haskell, которые мне нравятся, и запрещает все остальное. По мере развития учеников я могу выборочно "включать" различные функции, когда они освоили основы.

Я предлагаю более простое (как при меньшей трудоемкости) решение этой проблемы. Вместо создания реализации на Haskell, в которой вы можете отключить функции, оберните компилятор Haskell программой, которая сначала проверяет, не использует ли код какую-либо запрещенную вами функцию, а затем использует готовый компилятор для ее компиляции.

Это было бы похоже на HLint (а также своего рода противоположность):

HLint (ранее Dr. Haskell) читает программы на Haskell и предлагает изменения, которые, надеюсь, облегчат их чтение. HLint также позволяет легко отключать нежелательные предложения и добавлять свои собственные предложения.

  • Реализуйте свои собственные предложения HLint, чтобы не использовать функции, которые вы не разрешаете
  • Отключите все стандартные предложения HLint.
  • Сделайте так, чтобы ваша обертка запустила измененный HLint в качестве первого шага
  • Относитесь к предложениям HLint как к ошибкам. То есть, если HLint "пожаловался", то программа не переходит на этап компиляции
20
yairchu

Баскелл является обучающей реализацией, http://hackage.haskell.org/package/baskell

Вы можете начать с выбора, скажем, системы типов для реализации. Это примерно так же сложно, как интерпретатор Scheme, http://hackage.haskell.org/package/thih

16
Don Stewart

Серия компиляторов EHC, пожалуй, лучшая ставка: она активно развивается и, кажется, именно то, что вам нужно - серия маленьких компиляторов/интерпретаторов лямбда-исчислений, кульминацией которых стал Haskell '98.

Но вы также можете взглянуть на различные языки, разработанные в типах и языках программирования Пирса или в интерпретаторе Helium (искалеченный Haskell, предназначенный для студентов http://en.wikipedia.org/wiki/Helium_(Haskell) ).

6
gwern

Если вы ищете подмножество Haskell, которое легко реализовать, вы можете покончить с классами типов и проверкой типов. Без классов типов вам не нужен вывод типов для оценки кода на Haskell.

Я написал самокомпилируемый компилятор подмножества Haskell для задачи Code Golf. Он принимает код подмножества Haskell на входе и создает код C на выходе. Мне жаль, что нет более читаемой версии; Я поднял вложенные определения вручную в процессе самостоятельной компиляции.

Студенту, заинтересованному во внедрении переводчика для подмножества Haskell, я бы рекомендовал начать со следующих функций:

  • Ленивая оценка. Если переводчик находится на Хаскеле, вам, возможно, не придется ничего делать для этого.

  • Определения функций с сопоставленными с шаблоном аргументами и средствами защиты. Беспокойство касается только шаблонов переменных, cons, nil и _.

  • Синтаксис простого выражения:

    • Целочисленные литералы

    • Символьные литералы

    • [] (ноль)

    • Применение функции (левый ассоциативный)

    • Инфикс : (минусы, право ассоциативно)

    • Круглая скобка

    • Имена переменных

    • Имена функций

Более конкретно, напишите интерпретатор, который может запустить это:

-- tail :: [a] -> [a]
tail (_:xs) = xs

-- append :: [a] -> [a] -> [a]
append []     ys = ys
append (x:xs) ys = x : append xs ys

-- zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith f (a:as) (b:bs) = f a b : zipWith f as bs
zipWith _ _      _      = []

-- showList :: (a -> String) -> [a] -> String
showList _    []     = '[' : ']' : []
showList show (x:xs) = '[' : append (show x) (showItems show xs)

-- showItems :: (a -> String) -> [a] -> String
showItems show []     = ']' : []
showItems show (x:xs) = ',' : append (show x) (showItems show xs)

-- fibs :: [Int]
fibs = 0 : 1 : zipWith add fibs (tail fibs)

-- main :: String
main = showList showInt (take 40 fibs)

Проверка типов является важной особенностью Haskell. Однако перейти от ничего к проверке типов компилятора Haskell очень сложно. Если вы начнете с написания интерпретатора для вышеупомянутого, добавление проверки типов к нему должно быть менее пугающим.

6
Joey Adams

Это может быть хорошей идеей - сделать крошечную версию NetLogo в Haskell. Здесь это крошечный переводчик.

3
Claudiu

Вы можете посмотреть на Happy (похожий на yacc парсер в Haskell), который имеет парсер Haskell.

3
Kathy Van Stone

посмотрим, может ли гелий создать лучшую базу для построения, чем стандартный haskell.

2
Martin DeMello

Uhc/Ehc - это серия компиляторов, которые включают/отключают различные функции Haskell. http://www.cs.uu.nl/wiki/Ehc/WebHome#What_is_UHC_And_EHC

2
ja.

У Андрея Бауэра Zoo Language Programming Zoo есть небольшая реализация чисто функционального языка программирования, который несколько нахально называют "мини-скелет". Это около 700 строк OCaml, поэтому очень легко усваивается.

Сайт также содержит игрушечные версии языков программирования в стиле ML, Prolog и OO.

2
niklas

Мне сказали, что Idris имеет довольно компактный парсер, не уверен, действительно ли он подходит для изменения, но он написан на Haskell.

2
Jeff Burdges

Не думаете ли вы, что было бы проще взять источники GHC и убрать то, что вы не хотите, чем написать собственный интерпретатор Haskell с нуля? Вообще говоря, должно быть много меньших усилий, связанных с удалением функций в отличие от создания/добавления функций.

GHC написан на Haskell, так или иначе, так что технически это соответствует вашему вопросу о переводчике на Haskell, написанном на Haskell.

Вероятно, было бы не слишком сложно сделать все это статически связанным, а затем только распространять ваш настроенный GHCi, чтобы ученики не могли загружать другие исходные модули Haskell. Что касается того, сколько работы потребуется, чтобы предотвратить загрузку других объектных файлов на Haskell, я понятия не имею. Вы также можете отключить FFI, если у вас есть кучка читеров в ваших классах :)

1
Mark Rushakoff

Причина, по которой существует так много интерпретаторов LISP, заключается в том, что LISP в основном является предшественником JSON: простой формат для кодирования данных. Это делает интерфейсную часть довольно простой в обращении. По сравнению с этим, Haskell, особенно с языковыми расширениями, не самый простой для анализа язык. Вот некоторые синтаксические конструкции, которые звучат сложно, чтобы получить право:

  • операторы с настраиваемым приоритетом, ассоциативностью и фиксированностью,
  • вложенные комментарии
  • правило размещения
  • синтаксис шаблона
  • do- блоки и десагаринг в монадический код

Каждый из них, за исключением, возможно, операторов, может быть решен студентами после их курса по компиляции, но это отвлечет внимание от того, как на самом деле работает Haskell. В дополнение к этому, вы можете не захотеть напрямую реализовывать все синтаксические конструкции Haskell, а вместо этого реализовывать проходы, чтобы избавиться от них. Что подводит нас к буквальному суть проблемы, каламбур полностью предназначен.

Я предлагаю реализовать проверку типов и интерпретатор для Core вместо полной версии Haskell. Обе эти задачи уже довольно сложны. Этот язык, хотя и является строго типизированным функциональным языком, гораздо менее сложен в плане оптимизации и генерации кода. Тем не менее, он по-прежнему не зависит от базовой машины. Поэтому GHC использует его в качестве языка-посредника и переводит большинство синтаксических конструкций Haskell в него.

Кроме того, вы не должны уклоняться от использования внешнего интерфейса GHC (или другого компилятора). Я не считаю это мошенничеством, поскольку пользовательские LISP используют синтаксический анализатор системы Host LISP (по крайней мере, во время начальной загрузки). Очистка фрагментов Core и представление их учащимся, а также исходный код должны дать вам представление о том, что делает интерфейс, и почему предпочтительно не переопределять его.

Вот несколько ссылок на документацию Core, которая используется в GHC:

0
MauganRa