it-swarm-es.com

¿Cómo creo mi propio lenguaje de programación y un compilador para él?

Soy minucioso con la programación y he encontrado lenguajes que incluyen BASIC, FORTRAN, COBOL, LISP, LOGO, Java, C++, C, MATLAB, Mathematica, Python, Ruby, Perl, JavaScript, Assembly, etc. No puedo entender cómo las personas crean lenguajes de programación y diseñan compiladores para ello. Tampoco podía entender cómo las personas crean sistemas operativos como Windows, Mac, UNIX, DOS, etc. La otra cosa que es misteriosa para mí es cómo las personas crean bibliotecas como OpenGL, OpenCL, OpenCV, Cocoa, MFC, etc. Lo último que no puedo entender es cómo los científicos diseñan un lenguaje ensamblador y un ensamblador para un microprocesador. Realmente me gustaría aprender todas estas cosas y tengo 15 años. Siempre quise ser un informático, alguien como Babbage, Turing, Shannon o Dennis Ritchie.


Ya leí el libro de conceptos de Aho's Compiler Design y Tanenbaum OS y todos ellos solo discuten conceptos y códigos en un alto nivel. No entran en detalles y matices y cómo diseñar un compilador o sistema operativo. Quiero una comprensión concreta para poder crear una yo mismo y no solo una comprensión de lo que es un hilo, semáforo, proceso o análisis. Le pregunté a mi hermano sobre todo esto. Él es un estudiante de SB en EECS en MIT y no tiene idea de cómo crear realmente todas estas cosas en el mundo real. Todo lo que sabe es solo una comprensión del diseño del compilador y el sistema operativo conceptos como los que ustedes han mencionado (es decir, subprocesos, sincronización, concurrencia, administración de memoria, análisis léxico, generación de código intermedio, etc.)

427
Dave

Básicamente, su pregunta es "¿cómo se diseñan e implementan los chips de computadora, los conjuntos de instrucciones, los sistemas operativos, los idiomas, las bibliotecas y las aplicaciones?" Esa es una industria mundial multimillonaria que emplea a millones de personas, muchas de las cuales son especialistas. Es posible que desee centrar su pregunta un poco más.

Dicho esto, puedo echar un vistazo a:

No puedo entender cómo las personas crean lenguajes de programación y diseñan compiladores para ello.

Me sorprende, pero mucha gente considera que los lenguajes de programación son mágicos. Cuando me encuentro con personas en fiestas o lo que sea, si me preguntan qué hago, les digo que diseño lenguajes de programación e implemento los compiladores y herramientas, y es sorprendente la cantidad de veces que las personas, programadores profesionales, lo entienden. "wow, nunca lo pensé, pero sí, alguien tiene que diseñar esas cosas". Es como si pensaran que los idiomas simplemente surgen totalmente formados con infraestructuras de herramientas a su alrededor.

No solo aparecen. Los idiomas están diseñados como cualquier otro producto: haciendo cuidadosamente una serie de compensaciones entre las posibilidades de la competencia. Los compiladores y las herramientas se crean como cualquier otro producto de software profesional: analizando el problema, escribiendo una línea de código a la vez y luego probando el programa resultante.

El diseño del lenguaje es un gran tema. Si está interesado en diseñar un idioma, un buen lugar para comenzar es pensar en cuáles son las deficiencias en un idioma que ya conoce. Las decisiones de diseño a menudo surgen al considerar un defecto de diseño en otro producto.

Alternativamente, considere un dominio que le interese y luego diseñe un lenguaje específico de dominio (DSL) que especifique soluciones a problemas en ese dominio. Mencionaste LOGO; ese es un gran ejemplo de un DSL para el dominio de "dibujo lineal". Las expresiones regulares son un DSL para el dominio "buscar un patrón en una cadena". LINQ en C #/VB es un DSL para el dominio "filtrar, unir, ordenar y proyectar datos". HTML es un DSL para el dominio "describir el diseño del texto en una página", y así sucesivamente. Hay muchos dominios que son aptos para soluciones basadas en el lenguaje. Uno de mis favoritos es Inform7, que es un DSL para el dominio "juego de aventura basado en texto"; Es probablemente el lenguaje de programación serio de más alto nivel que he visto. Elija un dominio del que sepa algo y piense en cómo usar el lenguaje para describir problemas y soluciones en ese dominio.

Una vez que haya esbozado cómo quiere que se vea su idioma, intente escribir ¡precisamente cuáles son las reglas para determinar qué es un programa legal e ilegal. Por lo general, querrá hacer esto en tres niveles:

  1. ¡léxico: cuáles son las reglas para las palabras en el idioma, qué caracteres son legales, qué aspecto tienen los números, etc.
  2. sintáctico: ¿cómo se combinan las palabras del idioma en unidades más grandes? En C #, las unidades más grandes son cosas como expresiones, declaraciones, métodos, clases, etc.
  3. ¡semántico: dado un programa sintácticamente legal, ¿cómo se da cuenta de lo que el programa hace?

Escriba estas reglas ¡con la mayor precisión posible. Si hace un buen trabajo, puede usarlo como base para escribir un compilador o un intérprete. Eche un vistazo a la especificación C # o la especificación ECMAScript para ver a qué me refiero; Están repletos de reglas muy precisas que describen lo que hace un programa legal y cómo averiguar qué hace.

Una de las mejores maneras de comenzar a escribir un compilador es escribiendo un compilador lenguaje de alto nivel a lenguaje de alto nivel . Escriba un compilador que incluya cadenas en su idioma y escupe cadenas en C # o JavaScript o cualquier idioma que conozca; deje que el compilador para ese idioma se encargue del trabajo pesado de convertirlo en código ejecutable.

Escribo un blog sobre el diseño de C #, VB, VBScript, JavaScript y otros lenguajes y herramientas; si este tema te interesa, échale un vistazo. http://blogs.msdn.com/ericlippert (histórico) y http://ericlippert.com (actual)

En particular, puede encontrar esta publicación interesante; Aquí enumero la mayoría de las tareas que el compilador de C # realiza para usted durante su análisis semántico. Como puede ver, hay muchos pasos. Dividimos el gran problema de análisis en una serie de problemas que podemos resolver individualmente.

http://blogs.msdn.com/b/ericlippert/archive/2010/02/04/how-many-passes.aspx

Finalmente, si estás buscando un trabajo para hacer estas cosas cuando seas mayor, entonces considera venir a Microsoft como pasante universitario e intentar ingresar a la división de desarrolladores. ¡Así es como terminé con mi trabajo hoy!

407
Eric Lippert

Puede encontrar Permite construir un compilador de Jack Crenshaw una introducción interesante para escribir compiladores y lenguaje ensamblador.

El autor lo mantuvo muy simple y se centró en la construcción de la funcionalidad real.

127
user1249

"Me gustaría ¡realmente me gustaría aprender estas cosas". Si usted es serio a largo plazo:

  • Ir a la universidad, especializarse en ingeniería de software. Tome cada clase de compilador que pueda obtener. Las personas que imparten las clases tienen una mejor educación y más experiencia que usted; es bueno que sus perspectivas expertas se utilicen para presentarle la información de maneras que nunca obtendrá al leer el código.

  • Seguir con las clases de matemáticas hasta la escuela secundaria y continuar en la universidad durante los 4 años. Centrarse en las matemáticas no estándar: lógica, teoría de grupos, metamatemáticas. Esto te obligará a pensar de manera abstracta. Le permitirá leer los documentos de teoría avanzada sobre la compilación y comprender por qué esas teorías son interesantes y útiles. Puede ignorar esas teorías avanzadas, si siempre quiere estar detrás del estado del arte.

  • Recopile/lea los textos estándar del compilador: Aho/Ullman, etc. Contienen lo que la comunidad generalmente acepta es algo fundamental. Es posible que no use todo de esos libros, pero debe saber que existe, y debe saber por qué no lo está usando. Pensé que Muchnick era genial, pero es para temas bastante avanzados.

  • Construye un compilador. Comience AHORA construyendo uno podrido. Esto te enseñará algunos problemas. Construye una segunda. Repetir. Esta experiencia genera una gran sinergia con el aprendizaje de su libro.

  • Un buen lugar para comenzar es aprender sobre BNF (Backus Naur Form), analizadores y generadores de analizadores. BNF se usa universalmente de manera efectiva en tierra de compiladores, y no puede hablar de manera realista con sus compañeros de tipo de compilador si no lo sabe.

Si desea una excelente primera introducción a la compilación, y el valor directo de BNF no solo para la documentación sino como un metalenguaje procesable por herramienta, vea esto tutorial (no mío) sobre la construcción de compiladores "meta" (compiladores que compilan compiladores) basado en un documento de 1964 (sí, lo leíste bien) ["META II, un lenguaje de escritura de compilador orientado a la sintaxis" de Val Schorre (http://doi.acm.org/10.1145/800257.808896)] Este en mi humilde opinión es uno de los mejores documentos de comp-sci jamás escritos: te enseña a construir compiladores en 10 páginas. Aprendí inicialmente de este artículo.

Lo que escribí anteriormente es mucho de la experiencia personal, y creo que me ha servido bastante bien. YMMV, pero en mi humilde opinión, no por mucho.

72
Ira Baxter

Aquí hay un libro/curso en línea que puede seguir llamado Los elementos de los sistemas informáticos: construir una computadora moderna a partir de los primeros principios .

Usando simuladores, en realidad construyes un sistema informático completo desde cero. Si bien muchos comentaristas han declarado que su pregunta es demasiado amplia, este libro en realidad la responde mientras se mantiene muy manejable. Cuando haya terminado, habrá escrito un juego en un lenguaje de alto nivel (que usted diseñó), que utiliza la funcionalidad de su propio sistema operativo, que se compila en un lenguaje VM (que diseñado por su compilador, que se traduce a un lenguaje ensamblador (que usted diseñó) por su traductor VM, que se ensambla en el código de máquina (que usted diseñó) por su ensamblador, que se ejecuta en su sistema informático que compiló a partir de chips que diseñó usando lógica booleana y un lenguaje de descripción de hardware simple.

Los capítulos:

  1. Resumen del curso
  2. Lógica booleana
  3. Chips combinatorios
  4. Chips secuenciales
  5. Lenguaje de máquina
  6. Arquitectura de Computadores
  7. Ensamblador
  8. Máquina virtual I: aritmética
  9. Máquina virtual II: control
  10. Lenguaje de programación
  11. Compilador I: Análisis de sintaxis
  12. Compilador II: Generación de Código
  13. Sistema operativo
  14. Elemento de la lista

Más diversión para llevar

46
Joe Internet

Da un paso atrás. Un compilador es simplemente un programa que traduce un documento en un idioma a un documento en otro idioma. Ambos idiomas deben estar bien definidos y específicos.

Los lenguajes no tienen que ser lenguajes de programación. Pueden ser cualquier idioma cuyas reglas se puedan escribir. Probablemente has visto Traductor de Google ; es un compilador porque puede traducir un idioma (por ejemplo, alemán) a otro (japonés, tal vez).

Otro ejemplo de un compilador es un motor de representación HTML. Su entrada es un archivo HTML y la salida es una serie de instrucciones para dibujar los píxeles en la pantalla.

Cuando la mayoría de la gente habla de un compilador, generalmente se refieren a un programa que traduce un lenguaje de programación de alto nivel (como Java, C, Prolog) a uno de bajo nivel (código ensamblador o de máquina). Eso puede ser desalentador. Pero no es tan malo cuando consideras que un compilador es un programa que traduce un idioma a otro.

¿Puedes escribir un programa que invierta cada palabra en una cadena? Por ejemplo:

When the cat's away, the mice will play.

se convierte

nehW eht s'tac yawa, eht ecim lliw yalp.

Ese no es un programa difícil de escribir, pero debes pensar en algunas cosas:

  • ¿Qué es una "palabra"? ¿Puedes definir qué caracteres forman una palabra?
  • ¿Dónde comienzan y terminan las palabras?
  • ¿Las palabras están separadas por un solo espacio, o puede haber más, o menos?
  • ¿Es necesario revertir la puntuación también?
  • ¿Qué pasa con la puntuación dentro de una palabra?
  • ¿Qué pasa con las mayúsculas?

Las respuestas a estas preguntas ayudan a que el lenguaje esté bien definido. Ahora ve y escribe el programa. Felicidades, acabas de escribir un compilador.

¿Qué tal esto? ¿Puedes escribir un programa que tome una serie de instrucciones de dibujo y genere un archivo PNG (o JPEG)? Tal vez algo como esto:

image 100 100
background black
color red
line 20 55 93 105
color green
box 0 0 99 99

Nuevamente, necesitará pensar un poco para definir el lenguaje:

  • ¿Cuáles son las instrucciones primitivas?
  • ¿Qué viene después de la palabra "línea"? ¿Qué viene después del "color"? Del mismo modo para "fondo", "cuadro", etc.
  • ¿Qué es un número?
  • ¿Se permite un archivo de entrada vacío?
  • ¿Está bien capitalizar las palabras?
  • ¿Se permiten números negativos?
  • ¿Qué sucede si no le das la directiva "imagen"?
  • ¿Está bien no especificar un color?

Por supuesto, hay más preguntas que responder, pero si puede concretarlas, ha definido un idioma. El programa que escribes para hacer la traducción es, supongo, un compilador.

Verá, escribir un compilador no es tan difícil. Los compiladores que ha utilizado en Java o C son solo versiones más grandes de estos dos ejemplos. ¡Así que adelante! Defina un lenguaje simple y escriba un programa para que ese lenguaje haga algo. Más pronto o más adelante querrá ampliar su lenguaje. Por ejemplo, puede agregar variables o expresiones aritméticas. Su compilador se volverá más complejo, pero comprenderá cada parte porque lo escribió usted mismo. Así es cómo surgen los lenguajes y los compiladores.

46
Barry Brown

Si está interesado en el diseño del compilador, consulte Dragon Book (título oficial: Compiladores: principios, técnicas y herramientas). Es ampliamente considerado como un libro clásico sobre este tema.

21
Brian Agnew

No creas que hay algo mágico en un compilador o un sistema operativo: no lo hay. ¿Recuerdas los programas que escribiste para contar todas las vocales en una cadena o sumar los números en una matriz? Un compilador no es diferente en concepto; Es mucho más grande.

Cada programa tiene tres fases:

  1. lee algunas cosas
  2. procesar esas cosas: traducir los datos de entrada a los datos de salida
  3. escribir algunas otras cosas: los datos de salida

Piénselo: ¿qué es la entrada al compilador? Una cadena de caracteres de un archivo fuente.

¿Qué es la salida del compilador? Una cadena de bytes que representan las instrucciones de la máquina para la computadora de destino.

Entonces, ¿cuál es la fase de "proceso" del compilador? ¿Qué hace esa fase?

Si considera que el compilador, como cualquier otro programa, tiene para incluir estas tres fases, tendrá un buen idea de cómo se construye un compilador.

10
Pete Wilson

"Construyamos un compilador" ya se sugirió. Hay una versión "modernizada" con Haskell en lugar de Turbo Pascal: http://alephnullplex.appspot.com/blog/view/2010/01/12/lbach-1-introduction

Siguiendo con Haskell, hay un intérprete de esquemas muy instructivo que podría dar más ideas: Escríbete un esquema en 48 horas

10
Landei

No soy un experto, pero aquí está mi puñalada:

No pareces preguntar sobre escribir un compilador, solo un ensamblador. Esto no es realmente mágico.

Robar a alguien más responde de SO ( https://stackoverflow.com/questions/3826692/how-do-i-translate-Assembly-to-binary ), El montaje se ve así:

label:  LDA #$00
        JMP label

Luego lo ejecuta a través de un ensamblador y se convierte en algo como esto:

$A9 $00
$4C $10 $00

Solo que está todo aplastado, así:

$A9 $00 $4C $10 $00

Realmente no es magia.

No puede escribir eso en el bloc de notas, porque el bloc de notas usa ASCII (no hexadecimal). Usaría un editor hexadecimal, o simplemente escribiría los bytes programáticamente. Escribe ese hexadecimal en un archivo , asígnele el nombre "a.exe" o "a.out", luego dígale al sistema operativo que lo ejecute.

Por supuesto, las CPU y los sistemas operativos modernos son realmente bastante complicados, pero esa es la idea básica.

Si desea escribir un nuevo compilador, así es como se hace:

1) Escriba un lenguaje interpretado usando algo como el ejemplo de la calculadora en pyparsing (o cualquier otro buen marco de análisis). Eso lo pondrá al día sobre los conceptos básicos del análisis.

2) Escribe un traductor. Traduce tu idioma a, digamos, Javascript. Ahora su idioma se ejecutará en un navegador.

3) Escriba un traductor a un nivel inferior, como LLVM, C o Assembly.

Puedes parar aquí, este es un compilador. No es un compilador optimizador, pero esa no era la pregunta. Es posible que también deba considerar escribir un enlazador y ensamblador, pero ¿realmente desea hacerlo?

4) (Loco) Escribe un optimizador. Grandes equipos trabajan durante décadas en esto.

4) (Sane) Participe en una comunidad existente. GCC, LLVM, PyPy, el equipo central que trabaja en cualquier intérprete.

8
wisty

Varios otros han dado excelentes respuestas. Solo agregaré algunas sugerencias más. Primero, un buen libro para lo que está tratando de hacer son los textos de implementación del compilador moderno de Appel (elija C , Java =, o ML estándar ). Este libro lo lleva a través de una implementación completa de un compilador para un lenguaje simple, Tiger, a MIPS Assembly que se puede ejecutar en un emulador, junto con una biblioteca de soporte de tiempo de ejecución mínimo. Para un solo paso por todo lo necesario para que un lenguaje compilado funcione, es un libro bastante bueno1.

Appel lo guiará a través de cómo compilar un lenguaje que viene prediseñado, pero no pasa mucho tiempo en lo que significan las diversas características del lenguaje o cómo pensar en ellas en términos de sus méritos relativos para diseñar el suyo. Para ese aspecto, Lenguajes de programación: conceptos y construcciones es decente. Conceptos, técnicas y modelos de programación de computadoras también es un buen libro para pensar profundamente sobre el diseño del lenguaje, aunque lo hace en el contexto de un solo idioma ( Oz ).

Finalmente, mencioné que Appel tiene su texto en C, Java y ML estándar: si usted es serio sobre la construcción del compilador y los lenguajes de programación, le recomiendo aprender ML y usar esa versión de Appel. Los idiomas de la familia ML tienen sistemas de tipo fuerte y son predominantemente funcionales, características que serán diferentes de muchos otros idiomas, por lo que aprenderlos si aún no conoce un idioma funcional perfeccionará su oficio lingüístico. Además, su mentalidad funcional y de coincidencia de patrones es extremadamente adecuada para los tipos de manipulaciones que necesita hacer a menudo en un compilador, por lo que los compiladores escritos en lenguajes basados ​​en ML suelen ser mucho más cortos y fáciles de entender que los compiladores escritos en C, Java o lenguajes similares. Libro de Harper en ML estándar es una guía bastante buena para comenzar; trabajar con eso debería prepararlo para asumir el libro de implementación del compilador ML estándar de Appel. Si aprende ML estándar, también será bastante fácil elegir OCaml para un trabajo posterior; En mi opinión, tiene mejores herramientas para el programador de trabajo (se integra de manera más limpia con el entorno del sistema operativo circundante, produce programas ejecutables fácilmente y tiene algunas herramientas espectaculares de creación de compiladores como ulex y Menhir).


1Para referencia a largo plazo, prefiero el Libro del Dragón, ya que tiene más detalles sobre las cosas a las que probablemente me referiré, como el funcionamiento interno de los algoritmos analizadores y tiene una cobertura más amplia de diferentes enfoques, pero el libro de Appel es muy bueno para Un primer pase. Básicamente, Appel te enseña una forma de hacer las cosas a través del compilador y te guía a través de él. Dragon Book cubre diferentes alternativas de diseño con más detalle, pero proporciona mucha menos orientación sobre cómo hacer que algo funcione.


Editado : reemplace la referencia Aho incorrecta con Sethi, mencione CTMCP.

8
Michael Ekstrand

Tuve que crear un compilador para la clase en la universidad.

Los principios básicos para hacerlo no son tan complicados como parece. El primer paso es crear tu gramática. Piensa en la gramática del idioma inglés. Del mismo modo, puede analizar una oración si tiene un sujeto y un predicado. Para obtener más información sobre eso, lea Context Free Grammars .

Una vez que tiene la gramática abajo (las reglas de su idioma), escribir un compilador es tan simple como seguir esas reglas. Los compiladores generalmente se traducen al código de la máquina, pero a menos que desee aprender x86, le sugiero que mire MIPS o cree su propia máquina virtual.

Compiladores generalmente tienen dos partes, un escáner y un analizador. Básicamente, el escáner lee el código y lo separa en tokens. El analizador analiza la estructura de esas fichas. Luego, el compilador revisa y sigue algunas reglas bastante simples para convertirlo a cualquier código que necesite (ensamblado, código intermedio como bytecode, etc.). Si lo divide en partes cada vez más pequeñas, esto eventualmente no es desalentador en absoluto.

¡Buena suerte!

6
Jerr

El libro de Petzold Código es una gran introducción a los no técnicos y técnicos, comenzando por los primeros principios. Es altamente legible y de gran alcance sin quedar empantanado.

Ahora que he escrito esto, voy a tener que volver a leerlo.

6
Kevin Won

Hay excelentes respuestas en este hilo, pero solo quería agregar las mías, ya que una vez tuve la misma pregunta. (Además, me gustaría señalar que el libro sugerido por Joe-Internet es un excelente recurso).

Primero está la cuestión de cómo funciona una computadora. Así es como: Entrada -> Calcular -> Salida.

Primero considere la parte "Calcular". Más adelante veremos cómo funciona la entrada y la salida.

Una computadora consiste esencialmente en un procesador (o CPU) y algo de memoria (o RAM). La memoria es una colección de ubicaciones, cada una de las cuales puede almacenar un número finito de bits, y cada una de esas ubicaciones de memoria puede ser referenciada por un número, esto se llama la dirección de la ubicación de la memoria. El procesador es un dispositivo que puede obtener datos desde la memoria, realice algunas operaciones basadas en los datos y vuelva a escribir algunos datos en la memoria. ¿Cómo determina el procesador qué leer y qué hacer después de leer los datos de la memoria?

Para responder a esto, necesitamos comprender la estructura de un procesador. La siguiente es una vista bastante simple. Un procesador consta esencialmente de dos partes. Uno es un conjunto de ubicaciones de memoria integradas dentro del procesador que sirven como memoria de trabajo. Estos se llaman "registros". El segundo es un montón de maquinaria electrónica construida para realizar ciertas operaciones utilizando los datos en los registros. Hay dos registros especiales llamados "Contador de programa" o la PC y el "Registro de instrucciones" o ir. El procesador considera que la memoria está dividida en tres partes. La primera parte es la "memoria de programa", que almacena el programa de computadora que se está ejecutando. El segundo es la "memoria de datos". El tercero se usa para algunos fines especiales, hablaremos de ello más adelante. El contador de programa contiene la ubicación de la siguiente instrucción para leer de la memoria de programa. El contador de instrucciones contiene un número que se refiere a la operación actual que se realiza. Cada operación que puede realizar un procesador se refiere a un número llamado código operativo de la operación. El funcionamiento esencial de una computadora es leer la ubicación de la memoria a la que hace referencia el contador de programas en el registro de instrucciones (e incrementa el contador de programas para que apunte a la ubicación de la memoria de la siguiente instrucción). A continuación, lee el Registro de instrucciones y realiza la operación deseada. Por ejemplo, la instrucción podría ser leer una ubicación de memoria específica en un registro, o escribir en algún registro o realizar alguna operación utilizando los valores de dos registros y escribir la salida en un tercer registro.

Ahora, ¿cómo realiza la computadora Entrada/Salida? Proporcionaré una respuesta muy simplificada. Ver http://en.wikipedia.org/wiki/Input/output y http://en.wikipedia.org/wiki/Interrupt . para más. Utiliza dos cosas, esa tercera parte de la memoria y algo llamado Interrupciones. Todos los dispositivos conectados a una computadora deben poder intercambiar datos con el procesador. Lo hace utilizando la tercera parte de la memoria mencionada anteriormente. El procesador asigna una porción de memoria a cada dispositivo y el dispositivo y el procesador se comunican a través de esa porción de memoria. Pero, ¿cómo sabe el procesador qué ubicación se refiere a qué dispositivo y cuándo necesita un dispositivo para intercambiar datos? Aquí es donde entran las interrupciones. Una interrupción es esencialmente una señal para que el procesador pause lo que es actualmente y guarde todos sus registros en una ubicación conocida y luego comience a hacer otra cosa. Hay muchas interrupciones, cada una se identifica por un número único. Para cada interrupción, hay un programa especial asociado a ella. Cuando ocurre la interrupción, el procesador ejecuta el programa correspondiente a la interrupción. Ahora, dependiendo de la BIOS y de cómo están conectados los dispositivos de hardware a la placa base de la computadora, cada dispositivo tiene una interrupción única y una porción de memoria. Al arrancar el sistema operativo con la ayuda de la BIOS, determina la interrupción y la ubicación de la memoria de cada dispositivo y configura los programas especiales para que la interrupción maneje los dispositivos correctamente. Entonces, cuando un dispositivo necesita algunos datos o desea enviar algunos datos, indica una interrupción. El procesador pausa lo que está haciendo, maneja la interrupción y luego vuelve a lo que está haciendo. Hay muchos tipos de interrupciones, como el disco duro, el teclado, etc. Una importante es el temporizador del sistema, que invoca una interrupción a intervalos regulares. También hay códigos de operación que pueden provocar interrupciones, llamadas interrupciones de software.

Ahora casi podemos entender cómo funciona un sistema operativo. Cuando se inicia, el sistema operativo configura una interrupción del temporizador, de modo que le da control al sistema operativo a intervalos regulares. También configura otras interrupciones para manejar otros dispositivos, etc. Ahora, cuando la computadora está ejecutando un montón de programas, y ocurre la interrupción del temporizador, el sistema operativo toma el control y realiza tareas importantes como la gestión de procesos, la gestión de memoria, etc. También un sistema operativo generalmente proporciona Una forma abstracta para que los programas accedan a los dispositivos de hardware, en lugar de permitirles acceder a los dispositivos directamente. Cuando un programa quiere acceder a un dispositivo, llama a un código proporcionado por el sistema operativo que luego se comunica con el dispositivo. Hay mucha teoría involucrada en estos que trata con la concurrencia, hilos, bloqueos, administración de memoria, etc.

Ahora, uno puede en teoría escribir un programa directamente usando códigos de operación. Esto es lo que se llama código de máquina. Esto obviamente es muy doloroso. Ahora, un lenguaje ensamblador para el procesador no es más que mnemotecnia para estos códigos de operación, lo que facilita la escritura de programas. Un ensamblador simple es un programa que toma un programa escrito en ensamblador y reemplaza los mnemónicos con los códigos de operación apropiados.

¿Cómo se puede diseñar un procesador y lenguaje ensamblador? Para saber que tienes que leer algunos libros sobre arquitectura de computadoras. (véanse los capítulos 1-7 del libro referido por joe-internet). Esto implica aprender sobre álgebra booleana, cómo construir circuitos combinatorios simples para sumar, multiplicar, etc., cómo construir memoria y circuitos secuenciales, cómo construir un microprocesador, etc.

Ahora, ¿cómo se escriben los idiomas de la computadora? Uno podría comenzar escribiendo un ensamblador simple en código máquina. Luego use ese ensamblador para escribir un compilador para un subconjunto simple de C. Luego use ese subconjunto de C para escribir una versión más completa de C. Finalmente use C para escribir un lenguaje más complicado como python = o C++. Por supuesto, para escribir un lenguaje primero debe diseñarlo (de la misma manera que desea un procesador). De nuevo, mire algunos libros de texto sobre eso.

¿Y cómo se escribe un sistema operativo? Primero apunta a una plataforma como x86. Luego descubres cómo arranca y cuándo se invocará tu sistema operativo. Una PC típica arranca de esta manera. Se inicia y BIOS realiza algunas pruebas. Luego, la BIOS lee el primer sector del disco duro y carga el contenido en una ubicación específica de la memoria. Luego configura la CPU para comenzar a ejecutar estos datos cargados. Este es el punto en el que se invoca. Un sistema operativo típico en este punto carga el resto de la memoria. Luego inicializa los dispositivos y configura otras cosas y finalmente te saluda con la pantalla de inicio de sesión.

Por lo tanto, para escribir un sistema operativo debe escribir el "gestor de arranque". Luego debe escribir código para manejar las interrupciones y dispositivos. Luego debe escribir todo el código para la gestión de procesos, gestión de dispositivos, etc. Luego debe escribir una API que permita que los programas que se ejecutan en su sistema operativo accedan a dispositivos y otros recursos. Y finalmente debe escribir código que lea un programa del disco, lo configure como un proceso y comience a ejecutarlo.

Por supuesto, mi respuesta es abiertamente simplificada y probablemente de poco uso práctico. En mi defensa, ahora soy un estudiante graduado en teoría, así que he olvidado muchas de estas cosas. Pero puedes buscar en Google muchas de estas cosas y obtener más información.

5
dubyaman

Es posible que desee comprobar esta excelente pregunta (y respuestas) en StackOverflow: Aprendiendo a escribir un compilador . Contiene una amplia lista de recursos.

5
Angry Lettuce

Puedo recordar un punto en mi carrera de programación cuando estaba en un estado de confusión similar al tuyo: había leído bastante sobre la teoría, el libro del Dragón, el libro del Tigre (rojo), pero todavía no tenía mucho de Una pista de cómo poner todo junto.

Lo que lo unió fue encontrar un proyecto concreto para hacer (y luego descubrir que solo necesitaba un pequeño subconjunto de toda la teoría).

El Java VM) me proporcionó un buen punto de partida: conceptualmente es un "procesador" pero está muy abstraído de los detalles desordenados de las CPU reales. También ofrece Una parte importante y a menudo pasada por alto del proceso de aprendizaje: desarmar las cosas antes de volver a armarlas (como solían hacer los niños con los aparatos de radio en los viejos tiempos).

Juega con un descompilador y la clase Hello, World en Java. Lea las especificaciones de JVM e intente comprender lo que está sucediendo. Esto le dará una perspectiva sólida de lo que es el compilador haciendo.

Luego juegue con un código que crea la clase Hello, World. (En efecto, está creando un compilador específico de la aplicación, para un lenguaje altamente especializado en el que solo puede decir Hello, World).

Intente escribir código que podrá leer en Hello, World escrito en algún otro idioma y generar la misma clase. Hágalo para que pueda cambiar la cadena de "Hola, Mundo" a otra cosa.

Ahora intente compilar (en Java) una clase que calcule alguna expresión aritmética, como "2 * (3 + 4)". Desarme esta clase, escriba un "compilador de juguetes" que pueda armarlo nuevamente.

4
Morendil

1) Grandes video conferencias de la Universidad de Washington:

Construcción del compilador CSE P 501 - Otoño 2009 www.cs.washington.edu/education/courses/csep501/09au/lectures/video.html *

2) SICP http://groups.csail.mit.edu/mac/classes/6.001/abelson-sussman-lectures/ Y el libro con el mismo nombre. Esto es realmente obligatorio para cualquier ingeniero de software.

3) Además, sobre programación funcional, Haskell, cálculo lambda, semántica (incluida la denotación) e implementación del compilador para lenguajes funcionales. Puede comenzar desde 2005-SS-FP.V10.2005-05-24.HDV si ya conoce a Haskell. Los videos Uxx son respuestas. Por favor, siga Vxx videos primero.

http://video.s-inf.de/#FP.2005-SS-Giesl. (COt) .HD_Videoaufzeichnung

(los videos están en inglés, aunque otros cursos están en alemán).

  • los nuevos usuarios solo pueden publicar un máximo de dos hipervínculos.
3
Zura

ANTLR es un buen punto de partida. Es un marco generador de lenguaje, similar a Lex y Yacc. Hay una interfaz gráfica de usuario llamada ANTLRWorks que simplifica el proceso.

En el mundo .NET existe el Dynamic Language Runtime que se puede usar para generar código en el mundo .NET. He escrito un lenguaje de expresión llamado Zentrum que genera código usando el DLR. Le mostrará cómo analizar y ejecutar expresiones escritas de forma estática y dinámica.

3
Sean

Si todo lo que dice es cierto, tiene el perfil de un investigador prometedor, y una comprensión concreta solo se puede obtener de una manera: estudiando. Y no estoy diciendo " Lea todos estos libros de ciencias de la computación de alto nivel (especialmente estos ) escrito por este genio !"; Quiero decir: debes estar con personas de alto nivel para ser un informático como Charles Babbage, Alan Turing, Claude Shannon o Dennis Ritchie. No desprecio a las personas autodidactas (soy una de ellas) pero no hay mucha gente como tú por ahí. Realmente recomiendo Programa de Sistemas Simbólicos (SSP) en niversidad de Stanford . Como dice su sitio web:

El Programa de Sistemas Simbólicos (SSP) en la Universidad de Stanford se enfoca en computadoras y mentes: sistemas artificiales y naturales que usan símbolos para representar información. SSP reúne a estudiantes y profesores interesados ​​en diferentes aspectos de la relación humano-computadora, incluyendo ...

  • ciencia cognitiva : estudio de la inteligencia humana, los lenguajes naturales y el cerebro como procesos computacionales;
  • inteligencia artificial : dotar a las computadoras de comportamiento y comprensión similares a los humanos; y
  • interacción humano-computadora : diseño de software de computadora e interfaces que funcionan bien con usuarios humanos.
2
quantme

Voy a sugerir algo un poco fuera del campo izquierdo: aprender Python (o quizás Ruby, pero tengo mucha más experiencia en Python, así que eso es de lo que hablaré). Y no solo incursionar en ello, sino realmente llegar a conocerlo a un nivel profundo.

Hay varias razones por las que sugiero esto:

  1. Python es un lenguaje excepcionalmente bien diseñado. Si bien tiene algunas verrugas, tiene menos IMHO que muchos otros idiomas. Si es un diseñador de idiomas en ciernes, es bueno exponerse a tantos idiomas buenos como sea posible.

  2. La implementación estándar de Python (CPython) es de código abierto y está bien documentada, lo que facilita la comprensión del funcionamiento del lenguaje.

  3. Python se compila en un código de byte simple que es más fácil de entender que Assembly y que funciona igual en todas las plataformas Python se ejecuta. Entonces aprenderá sobre la compilación (ya que Python compila su código fuente en código de bytes) e interpretación (ya que este código de bytes se interpreta en la máquina virtual Python).

  4. Python tiene muchas características nuevas propuestas, documentadas en PEP numeradas (propuestas de mejora de Python). PEP interesantes para leer para ver cómo los diseñadores de idiomas consideraron implementar una característica antes de elegir la forma en que realmente lo hicieron. (Las PEP que todavía están bajo consideración son especialmente interesantes a este respecto).

  5. Python tiene una combinación de características de varios paradigmas de programación, por lo que aprenderá sobre diversas formas de abordar la resolución de problemas y tendrá una gama más amplia de herramientas para considerar, incluso en su propio lenguaje.

  6. Python hace que sea bastante fácil extender el idioma de varias maneras con decoradores, metaclases, ganchos de importación, etc. para que pueda jugar con nuevas características del lenguaje hasta cierto punto sin abandonar el idioma. (Como comentario: ¡los bloques de código son objetos de primera clase en Ruby, por lo que en realidad puedes escribir nuevas estructuras de control como bucles! Tengo la impresión de que Ruby no necesariamente consideran que extender el lenguaje, sin embargo, es solo cómo se programa en Ruby. Pero es bastante genial).

  7. En Python, puedes desmontar el código de bytes generado por el compilador, o incluso escribir el tuyo desde cero y hacer que el intérprete lo ejecute (lo hice yo mismo, y fue alucinante pero divertido).

  8. Python tiene buenas bibliotecas para analizar. Puede analizar el código Python en un árbol de sintaxis abstracta y luego manipularlo utilizando el módulo AST. El módulo PyParsing es útil para analizar lenguajes arbitrarios, como los usted diseña. En teoría, podría escribir su compilador de primer idioma en Python si lo desea (y podría generar C, Asamblea o incluso Python salida).

Este enfoque de investigación podría ir bien con un enfoque más formal, ya que comenzará a reconocer los conceptos que ha estudiado en el idioma con el que está trabajando, y viceversa.

¡Que te diviertas!

2
kindall

Para una introducción simple sobre cómo funcionan los compiladores y cómo crear su propio lenguaje de programación, recomendaría el nuevo libro http://createyourproglang.com que se enfoca más en la teoría del diseño del lenguaje sin tener que conocer los aspectos internos del sistema operativo/CPU, es decir, lexers, analizadores, intérpretes, etc.

Utiliza las mismas herramientas que se usaron para crear los lenguajes de programación recientemente populares Coffee Script y Fancy .

2
mythz

Ver el libro de Kenneth Louden, "Construcción del compilador"

http://www.cs.sjsu.edu/~louden/cmptext/

Proporciona un mejor enfoque práctico para el desarrollo del compilador.

La gente aprende haciendo. Solo un pequeño número puede ver símbolos garabateados en el tablero y saltar inmediatamente de la teoría a la práctica. Desafortunadamente, esas personas son a menudo dogmáticas, fundamentalistas y las más ruidosas al respecto.

1
Jarvis Jones

Bueno, creo que su pregunta podría reescribirse para ser: "¿Cuáles son los conceptos prácticos básicos de un título en informática?", Y la respuesta total es, por supuesto, obtener su propia licenciatura en informática.

Básicamente, crea su propio compilador de lenguaje de programación leyendo un archivo de texto, extrayendo información de él y realizando transformaciones en el texto en función de la información que ha leído de él, hasta que lo haya transformado en bytes que pueden leerse el cargador (cf, Linkers and Loaders de Levine). Un compilador trivial es un proyecto bastante riguroso cuando se realiza por primera vez.

El corazón de un sistema operativo es el núcleo, que administra los recursos (por ejemplo, asignación de memoria/desasignación), y cambia entre tareas/procesos/programas.

Un ensamblador es una transformación de texto-> byte.

Si está interesado en estas cosas, le sugiero que escriba un ensamblador X86, en Linux, que admita algún subconjunto del ensamblaje X86 estándar. Será un punto de entrada bastante sencillo y le presentará estos problemas. No es un proyecto para bebés, y te enseñará muchas cosas.

Yo recomendaría escribirlo en C; C es la lengua franca para ese nivel de trabajo.

1
Paul Nathan

Tuve la suerte de estar expuesto al PDP-8 como mi primer lenguaje ensamblador. El PDP-8 tenía solo seis instrucciones, que eran tan simples que era fácil imaginar que fueran implementadas por unos pocos componentes discretos, que de hecho eran. Realmente eliminó la "magia" de las computadoras.

Otra puerta de entrada a la misma revelación es el lenguaje ensamblador "mixto" que Knuth usa en sus ejemplos. "Mix" parece arcaico hoy, pero todavía tiene ese efecto de-mistificación.

1
ddyer

Los compiladores y los lenguajes de programación (y todo lo relacionado con la construcción de uno, como la definición de una gramática finita y la conversión a ensamblaje) es una tarea muy compleja que requiere una gran comprensión sobre los sistemas en su conjunto. Este tipo de curso generalmente se ofrece como una clase de comp.

Recomiendo encarecidamente que primero comprenda mejor los sistemas operativos en general y cómo se compilan/ejecutan los lenguajes existentes (es decir, de forma nativa (C/C++), en un VM (Java) o por un intérprete (Python/Javascript)).

Creo que usamos el libro Conceptos del sistema operativo de Abraham Silberschatz, Peter B. Galvin, Greg Gagne en mi curso de Sistemas operativos (en 2º año). Este fue un excelente libro que dio un recorrido exhaustivo de cada componente de un sistema operativo: un poco caro pero valió la pena y las copias antiguas/usadas deberían estar flotando.

0
plafond

Es un gran tema, pero en lugar de ignorarlo con un pomposo "ve a leer un libro, chico", en su lugar, con gusto te daré consejos para ayudarte a entenderlo.

La mayoría de los compiladores y/o intérpretes trabajan así:

Tokenizar : Escanee el texto del código y divídalo en una lista de tokens.

Este paso puede ser complicado porque no puede simplemente dividir la cadena en espacios, debe reconocer que if (bar) foo += "a string"; es una lista de 8 tokens: Word, OPEN_PAREN, Word, CLOSE_PAREN, Word, ASIGNMENT_ADD, STRING_LITERAL, TERMINATOR. Como puede ver, simplemente dividir el código fuente en los espacios no funcionará, debe leer cada carácter como una secuencia, por lo que si encuentra un carácter alfanumérico, sigue leyendo los caracteres hasta que toque un carácter no alfanumérico y esa cadena Acabo de leer es una palabra que se clasificará más adelante. Puedes decidir por ti mismo cuán granular es tu tokenizer: si se traga "a string" Como un token llamado STRING_LITERAL para analizarlo más adelante, o si ve "a string" Como OPEN_QUOTE, UNPARSED_TEXT, CLOSE_QUOTE, o lo que sea , esta es solo una de las muchas opciones que tiene que decidir por sí mismo mientras lo codifica.

Lex : Entonces ahora tienes una lista de tokens. Probablemente etiquetó algunos tokens con una clasificación ambigua como Word porque durante la primera pasada no gasta demasiado esfuerzo tratando de descubrir el contexto de cada cadena de caracteres. Así que ahora lea nuevamente su lista de tokens de origen y reclasifique cada uno de los tokens ambiguos con un tipo de token más específico basado en las palabras clave en su idioma. Por lo tanto, tiene una palabra como "if" y "if" en su lista de palabras clave especiales llamadas símbolo IF, por lo que cambia el tipo de símbolo de ese token de Word a IF, y cualquier palabra que no esté en su lista de palabras clave especiales , como Word foo, es un IDENTIFICADOR.

Parse : Así que ahora giró if (bar) foo += "a string"; una lista de tokens lexados que se ve así: SI OPEN_PAREN IDENTIFER CERRAR_PAREN IDENTIFICADOR ASIGN_ADD STRING_LITERAL TERMINATOR. El paso es reconocer secuencias de tokens como declaraciones. Esto está analizando. Lo haces usando una gramática como:

DECLARACIÓN: = ASIGN_EXPRESSION | IF_STATEMENT

IF_STATEMENT: = IF, PAREN_EXPRESSION, STATEMENT

ASIGN_EXPRESSION: = IDENTIFICADOR, ASIGN_OP, VALOR

PAREN_EXPRESSSION: = OPEN_PAREN, VALUE, CLOSE_PAREN

VALOR: = IDENTIFICADOR | STRING_LITERAL | PAREN_EXPRESSION

ASIGN_OP: = IGUAL | ASIGN_ADD | ASIGN_SUBTRACT | ASIGN_MULT

Las producciones que usan "|" entre términos significa "coincidir con cualquiera de estos", si hay comas entre términos significa "coincidir con esta secuencia de términos"

¿Cómo usas esto? Comenzando con el primer token, intente hacer coincidir su secuencia de tokens con estas producciones. Entonces, primero intenta hacer coincidir su lista de tokens con STATEMENT, así que lee la regla para STATEMENT y dice "una STATEMENT es ASIGN_EXPRESSION o IF_STATEMENT", por lo que intenta hacer coincidir ASIGN_EXPRESSION primero, así que busca la regla gramatical de ASIGN_EXPRESSION y dice "ASIGN_EXPRESSION es un IDENTIFICADOR seguido de un ASIGN_OP seguido de un VALOR, por lo que busca la regla gramatical para IDENTIFICADOR y ve que no hay un gramatical para IDENTIFICADOR, lo que significa que IDENTIFICADOR es un" terminal ", lo que significa que no requiere más análisis para que coincida para que pueda intentar hacerlo directamente con su token. Pero su primer token de origen es un IF, y IF no es lo mismo que un IDENTIFICADOR, por lo que la coincidencia falló. ¿Qué pasa ahora? Vuelva a la regla STATEMENT para que coincida con el siguiente término: IF_STATEMENT. Busca IF_STATEMENT, comienza con IF, busca IF, IF es un terminal, compara el terminal con tu primer token, si el token coincide, increíble, continúa, el próximo término es PAREN_EXPRESSION, busca PAREN_EXPRESSION, no es una terminal, cuál es su primer término, PAREN_EXPRESSION comienza con OPEN_PAREN, busca OPEN_PAREN, es una terminal, combina OPEN_PAREN con tu próximo token, coincide, ... y así sucesivamente.

La forma más fácil de abordar este paso es tener una función llamada parse (), a la que le pasa el token de código fuente con el que intenta hacer coincidir y el término de gramática con el que intenta hacer coincidir. Si el término de gramática no es una terminal, entonces recurre: llama a parse () nuevamente y le pasa el mismo token de origen y el primer término de esta regla de gramática. Es por eso que se llama un "analizador de descenso recursivo". La función parse () devuelve (o modifica) su posición actual en la lectura de los tokens de origen, esencialmente devuelve el último token en la secuencia coincidente, y continúa la siguiente llamada a analizar () desde allí.

Cada vez que parse () coincide con una producción como ASIGN_EXPRESSION, crea una estructura que representa ese fragmento de código. Esta estructura contiene referencias a los tokens de origen originales. Empiezas a construir una lista de estas estructuras. Llamaremos a esta estructura completa el Árbol de sintaxis abstracta (AST)

Compilar y/o Ejecutar : Para ciertas producciones en su gramática, ha creado funciones de controlador que si se le da una estructura AST) compilaría o ejecutaría esa porción de AST.

Así que echemos un vistazo a la parte de su AST que tiene el tipo ASIGN_ADD. Entonces, como intérprete, tiene una función ASIGN_ADD_execute (). Esta función se pasa como parte de la AST que corresponde al árbol de análisis para foo += "a string", por lo que esta función mira esa estructura y sabe que el primer término en la estructura debe ser un IDENTIFICADOR, y el segundo término es el VALOR, entonces ASIGN_ADD_execute () pasa el término VALOR a una función VALOR_eval () que devuelve un objeto que representa el valor evaluado en la memoria, luego ASIGN_ADD_execute () realiza una búsqueda de "foo" en la tabla de variables y almacena una referencia a lo que devuelve eval_value () función.

Eso es un intérprete. En cambio, un compilador tendría funciones de controlador que traducen el AST a código de bytes o código de máquina en lugar de ejecutarlo).

Los pasos 1 a 3, y algunos 4, se pueden facilitar con herramientas como Flex y Bison. (también conocido como Lex y Yacc), pero escribir un intérprete desde cero es probablemente el ejercicio más enriquecedor que cualquier programador podría lograr. Todos los demás desafíos de programación parecen triviales después de la cumbre de este.

Mi consejo es comenzar poco a poco: un lenguaje pequeño, con una gramática pequeña, e intente analizar y ejecutar algunas declaraciones simples, luego crezca a partir de ahí.

¡Lee esto y buena suerte!

http://www.iro.umontreal.ca/~felipe/IFT2030-Automne2002/Complements/tinyc.c

http://en.wikipedia.org/wiki/Recursive_descent_parser

0
snorkel

El campo de la computadora solo es complicado porque ha tenido tiempo de evolucionar en muchas direcciones. En esencia, se trata solo de máquinas que computan.

Mi computadora muy básica favorita es Computadora de retransmisión de Harry Porter . Da una idea de cómo funciona una computadora en el nivel base. Entonces puede comenzar a apreciar por qué se necesitan cosas como idiomas y sistemas operativos.

La cuestión es que es difícil entender algo sin entender lo que lo necesita . Buena suerte, y no solo lea cosas. Hacer cosas.

0
Mike Dunlavey