diff --git a/eplan/.gitignore b/eplan/.gitignore new file mode 100644 index 0000000..d2f30a1 --- /dev/null +++ b/eplan/.gitignore @@ -0,0 +1,27 @@ +# -- Java +# -- virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +# -- Maven +target/* + +# -- IntelliJ IDEA +.idea +*.iml + +# Latex build +doc/_build + +# -- Emacs +*~ + +# output of running the eplan compiler +tests/*.dot* +tests/*.exe +tests/*.ll +tests/*.s + +unknown.dot* +unknown.exe +unknown.ll +unknown.s diff --git a/eplan/.ide/IntelliJIdea2016.2-settings.jar b/eplan/.ide/IntelliJIdea2016.2-settings.jar new file mode 100644 index 0000000..ae1f78f Binary files /dev/null and b/eplan/.ide/IntelliJIdea2016.2-settings.jar differ diff --git a/eplan/.ide/README.md b/eplan/.ide/README.md new file mode 100644 index 0000000..9fd808b --- /dev/null +++ b/eplan/.ide/README.md @@ -0,0 +1,6 @@ +IntelliJ IDEA +------------- + +The following IntelliJ IDEA settings are exported (File/Export Setting): + +* Code Style diff --git a/eplan/LICENSE b/eplan/LICENSE new file mode 100644 index 0000000..f08d0d8 --- /dev/null +++ b/eplan/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 José Romildo Malaquias + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/eplan/README.md b/eplan/README.md new file mode 100644 index 0000000..cc05cca --- /dev/null +++ b/eplan/README.md @@ -0,0 +1,211 @@ +# EPLan + +EPLan is an educational programming language for compiler construction +teaching. + +EPlan is used to teach compilation techniques to undergraduate students +at Universidade Federal de Ouro. The language specification starts with +a very minimal kernel and grows with new features as new techniques are +learnt. + +Tags will be used to give access to each important stage of the +compiler implementation. + +## Development tools needed + +- [GIT](https://git-scm.com/) +- [Java development kit](http://www.oracle.com/technetwork/java/javase/downloads/) (version 1.8 or greater) +- C compiler +- [Maven](https://maven.apache.org/) +- Text editor for software development (Suggested: [Atom](https://atom.io/), [Notepad++](https://notepad-plus-plus.org/), gedit, [Emacs](https://www.gnu.org/software/emacs/)) +- IDE for Java development (optional) (suggested: [IntelliJ IDEA](https://www.jetbrains.com/idea/)) + +## Notice about using Windows + +One of the library dependencies of the project (`javacpp-presets-llvm`) +currently is not available for Windows, and because of that the Windows +platform is not supported at the moment. + +## Initial setup to work with EPlan in BCC328 + +In order to develop the activities of the BCC328 (Compiler Construction) course you should: + +- Have a [github](https://github.com/) account. If you do not have one, visit the github site and [sign up](https://github.com/join). +- [Log in](https://github.com/login) the github. +- Visit the [EPLan](https://github.com/romildo/eplan) project page. +- Fork the EPlan project. +![forking](images/fork.png) +- In your computer clone your fork of the eplan project. Notice that in the commands that follow any text written between angular braces `<>` shold be replaced by an appropriate text. For instance ` +$ git clone https://github.com/romildo/eplan.git +$ cd eplan +``` +- Set the remote repository for your clone +``` +$ git remote add upstream https://github.com/romildo/eplan.git +$ git remote -v +``` + +## When testing a version of the eplan compiler + +- Change your working directory to the folder containing your clone. +``` +$ cd +``` +- Select the master branch of the clone of your forked project. +``` +$ git branch +$ git checkout master +``` +- Pull the latest changes from the remote repository. +``` +$ git pull upstream master +``` +- Select the appropriate branch for the activity. +``` +$ git checkout -b +``` +- Develop the activity. + +## To submit an activity + +- Select the master branch of the clone of your forked project. +``` +$ cd +$ git checkout master +``` +- Pull the latest changes from the remote repository. +``` +$ git pull upstream master +``` +- Create a new branch where you will develop the activity. +``` +$ git checkout -b +``` +- Develop the activity. +- See the status of your cloned repository: +``` +git status +``` +- Add any new or modified file to the revision history: +``` +git add +``` +- Commit the changes: +``` +git commit -m +``` +- Push your changes to your forked project. +``` +git push origin +``` +- Make a pull request (PR) from your forked project at github. + +## Some useful commands + +### To remove the generated files + +``` +$ mvn clean +``` + +### To compile the project + +``` +$ mvn compile + +``` + +### To make `jar` files of the project + +``` +$ mvn package +``` + +### To run the eplan compiler + +The EPlan compiler accepts some command line options. A summary of its +usage can be obtained with the `--help` option: + +``` +$ java -jar target/uber-eplan-0.1-SNAPSHOT.jar --help +``` + +Run the EPLan compiler with the command line: + +``` +$ java -jar target/uber-eplan-0.1-SNAPSHOT.jar [options] [file] +``` + +Or you may use the provided shell script `driver`: + +``` +$ ./driver +``` + +There is also the `run` script, which runs the compiler on the standard +input, and generates and runs the executable. It also shows the image of +the abstract syntax tree. For that the +[Graphviz](http://www.graphviz.org/) package is needed. + +``` +$ ./run +``` + +The current version of the library `javacpp-presets-llvm` is based on a +LLVM release that has an issue regarding floating point constants in the +LLVM IR language. It follows the locale setting for numeric formatting +and at the same time it does not accept a comma instead of a dot in the +literal. You may have to set you locale numeric setting to `en_US-UTF-8` +for it to work. + +``` +$ LC_NUMERIC=en_US-UTF-8 java -jar target/uber-eplan-0.1-SNAPSHOT.jar [options] [file] +``` + +or with the provided shell script `driver`: + +``` +$ LC_NUMERIC=en_US-UTF-8 ./driver +``` + +A better alternative may be setting your language to `en_US-UTF-8` in +the system configuration. + +The generated LLVM intermediate representation code should be compiled +with the `llc`: + +``` +$ llc .ll +``` + +The generated assembly code and the runtime library should be compiled and linked with a C compiler. If using CLang: + +``` +$ clang -o .exe src/main/c/bindings.c .s +``` + +Or if using GCC: + +``` +$ gcc -o .exe .s src/main/c/bindings.c +``` + +To get an image of the syntact tree of your program: + +``` +$ dot -O -Tpng .dot .dot +$ eog .dot.png +``` + +Finally run the binary obtained for the eplan source code: + +``` +$ ./.exe +``` + +These steps can be combined in a single command line: +``` +$ F= && ./driver $F && llc $F.ll && clang -o $F.exe $F.s src/main/c/bindings.c && dot -O -Tpng $F.dot && eog $F*png && ./$F.exe +``` diff --git a/eplan/doc/eplan-compiler.tex b/eplan/doc/eplan-compiler.tex new file mode 100644 index 0000000..33119f5 --- /dev/null +++ b/eplan/doc/eplan-compiler.tex @@ -0,0 +1,1563 @@ +\documentclass[smaller]{beamer} + +\usepackage{iftex} + +\RequireLuaTeX + +\usepackage[]{fontspec} +\usepackage[brazilian]{babel} + +\usepackage{dirtree} +\usepackage{tikz-qtree} +\usepackage{syntax} +\usepackage{pygmentex} +% \usepackage{jrmmisc} +\usepackage{tcolorbox} +\usepackage{relsize} +\usepackage{indentfirst} + +% \setmainfont{Times New Roman} +% \setsansfont[Scale=MatchLowercase]{Helvetica} +% \setmonofont[Scale=MatchLowercase]{Courier} + +% \setmainfont{Times New Roman} +% \setsansfont[Scale=MatchLowercase]{Helvetica LT Std} +% \setmonofont[Scale=MatchLowercase]{Courier Std} + +% \setmainfont{DejaVu Serif} +% \setsansfont[Scale=MatchLowercase]{DejaVu Sans} +% \setmonofont[Scale=MatchLowercase]{DejaVu Sans Mono} + +\setmainfont{Liberation Serif} +\setsansfont[Scale=MatchLowercase]{Liberation Sans} +\setmonofont[Scale=MatchLowercase]{Liberation Mono} + +% \setmainfont{Lucida Std} +% \setsansfont[Scale=MatchLowercase]{Lucida Sans Std} +% \setmonofont[Scale=MatchLowercase]{Lucida Sans Typewriter Std} +% % \setmonofont[Scale=MatchLowercase]{Lucida Typewriter Std} + +% \setmainfont{Luxi Serif} +% \setsansfont[Scale=MatchLowercase]{Luxi Sans} +% \setmonofont[Scale=MatchLowercase]{Luxi Mono} + +% \setmainfont{Nimbus Roman No9 L} +% \setsansfont[Scale=MatchLowercase]{Nimbus Sans L} +% \setmonofont[Scale=MatchLowercase]{Nimbus Mono L} + +% \setmainfont{Droid Serif} +% \setsansfont[Scale=MatchLowercase]{Droid Sans} +% \setmonofont[Scale=MatchLowercase]{Droid Sans Mono} % no bold + +% \setmonofont[Scale=MatchLowercase]{Anonymous Pro} +% \setmonofont[Scale=MatchLowercase]{Consolas} +% \setmonofont[Scale=MatchLowercase]{Courier New} +% \setmonofont[Scale=MatchLowercase]{Cousine} +% \setmonofont[Scale=MatchLowercase]{DejaVu Sans Mono} +% \setmonofont[Scale=MatchLowercase]{Fantasque Sans Mono} +% \setmonofont[Scale=MatchLowercase]{Fira Code} +% \setmonofont[Scale=MatchLowercase]{Fira Mono} +% \setmonofont[Scale=MatchLowercase]{FreeMono} +% \setmonofont[Scale=MatchLowercase]{Hack} +% \setmonofont[Scale=MatchLowercase]{Input Mono Condensed} +% \setmonofont[Scale=MatchLowercase]{Iosevka Slab} +% \setmonofont[Scale=MatchLowercase]{Iosevka} +% \setmonofont[Scale=MatchLowercase]{Liberation Mono} +% \setmonofont[Scale=MatchLowercase]{Noto Mono} +% \setmonofont[Scale=MatchLowercase]{Source Code Pro} +% \setmonofont[Scale=MatchLowercase]{SF Mono} +% \setmonofont[Scale=MatchLowercase]{Ubuntu Mono} +\setmonofont[Scale=MatchLowercase]{M+ 1m} +% % +% \setmonofont[Scale=MatchLowercase]{Letter Gothic Std} +% \setmonofont[Scale=MatchLowercase]{Lucida Sans Typewriter Std} +% \setmonofont[Scale=MatchLowercase]{Luxi Mono} + + +\setbeamercovered{transparent} +\hypersetup{colorlinks} +\usetheme{OuroPreto} + +\newenvironment{tips}{% + \textbf{Dicas:}\newline + \begin{list}{*}{% + \setlength{\topsep}{0pt}% + \setlength{\itemsep}{0pt}% + \setlength{\parsep}{0pt}% + }% + }{% + \end{list}% +} + +\usetikzlibrary{ + calc, + shapes.multipart, + chains, + arrows, + graphs, + graphdrawing, +} + +\usegdlibrary{ + layered +} + +\tikzset{ + joined/.style = { + join=by ->, + }, + cons/.style = { + draw, + rounded corners, + rectangle split, + rectangle split parts=2, + rectangle split horizontal, + joined, + }, + var/.style = { + blue, + joined, + }, + null/.style = { + fill, + circle, + inner sep=0mm, + minimum size=2mm, + joined, + }, +} + +\renewcommand{\DTcomment}[1]{\textcolor{yellow}{\hrulefill}\sffamily\textcolor{blue}{#1}} +% \renewcommand\DTstylecomment{\sffamily\color{green}\textsc} +\renewcommand\DTstyle{\ttfamily\textcolor{red}} +\setlength{\DTbaselineskip}{12pt} + +\newcommand{\semester}{2016.2} + +\newcommand{\lang}{\textsl{EPLan}} + + +\setpygmented{lang=java,tabsize=3,sty=default} +\efboxsetup{hidealllines,backgroundcolor=yellow!28} + +\mdfsetup{ + % backgroundcolor=green!10, + roundcorner=2pt, + skipabove=2pt, + skipbelow=2pt, + innerleftmargin=2pt, + innerrightmargin=2pt, + innertopmargin=.25\baselineskip, + innerbottommargin=.25\baselineskip, +} + + +% syntax configuration +\renewcommand{\syntleft}{\normalfont\slshape\hspace{0.25em}} +\renewcommand{\syntright}{\hspace{0.25em}} +\renewcommand{\ulitleft}{\normalfont\ttfamily\bfseries\frenchspacing\color{red}\hspace{0.25em}} +\renewcommand{\ulitright}{\hspace{0.25em}} + + + + +\begin{document} + +\title[compiler]{ + Compilador de \lang{} +} +\subject{Linguagens de Programação} +\author{José Romildo Malaquias} +\institute[UFOP]{ + Departamento de Computação\\ + Universidade Federal de Ouro Preto +} +\date{\semester} + +\frame{\titlepage} + +\frame{\tableofcontents} + + +\section{A estrutura do compilador} + + +\begin{frame}{Organização do compilador} + \begin{itemize} + \item Implementado na linguagem \textbf{Java}. + \item Ferramentas auxiliares: + \begin{itemize} + \item \textbf{JFlex}: gerador de analisador léxico + \item \textbf{CUP}: gerador de analisador sintático + \item \textbf{LLVM}: gerador de código + \item \textbf{Maven}: ferramenta de automação de compilação de + projetos Java + \end{itemize} + \item Usa bibliotecas externas: + \begin{itemize} + \item \textbf{commons-lang3}: complementa as classes que estão + em \pyginline|java.lang|. + + \item \textbf{jcommander}: framework Java muito pequeno que + torna trivial a análise de parâmetros de linha de comando + + \item \textbf{javacpp-presets-llvm}: interface para a biblioteca + LLVMC do projeto LLVM (infraestrutura de construçào de + compilador escrita em C++) + + \item \textbf{javaslang}: uma biblioteca funcional para Java 8+ que + fornece tipos de dados persistentes e estruturas de controle + funcionais + + \item \textbf{javaslang-render}: biblioteca de renderização para + algumas estruturas de dados fornecidas por javaslang + + \item \textbf{junit}: \emph{framework} com suporte à criação de testes + automatizados em Java + + \item \textbf{assertj}: fornece um rico conjunto de afirmações, com + mensagens de erro úteis, melhorando a legibilidade dos testes + automatizados em Java + \end{itemize} + \end{itemize} +\end{frame} + +\begin{frame}[fragile]{Maven} + \begin{itemize} + \item Usado para automatizar a compilação de projetos. + \item O projeto é configurado usando um \textbf{POM} (\emph{Project + Object Model)}, que é armazenado em um arquivo \texttt{pom.xml}. + \item O desenvolvimento pode ser feito em várias \textbf{fases}, + indicadas por \textbf{objetivos}, como: + \begin{center} + \begin{tabular}{ll} + \textit{clean} & remover arquivos gerados \\ + \textit{generate-sources} & gerar código automático \\ + \textit{process-resources} & processar recursos \\ + \textit{compile} & compilar \\ + \textit{process-test-resources} & processar recursos de teste \\ + \textit{test-compile} & testar compilação \\ + \textit{test} & testar \\ + \textit{package} & empacotar \\ + \textit{install} & instalar \\ + \textit{deploy} & implantar + \end{tabular} + \end{center} + \item Exemplo: compilar o projeto na linha de comando: +\begin{Verbatim}[frame=single] +$ mvn compile +\end{Verbatim} + \end{itemize} +\end{frame} + +\begin{frame}{Estrutura de diretórios do projeto} + \small + \noindent + \dirtree{% + .1 \lang{}-compiler. + .2 src\DTcomment{código fonte do projeto}. + .3 main\DTcomment{código fonte principal}. + .4 c\DTcomment{código fonte em C}. + .4 cup\DTcomment{código fonte para o CUP}. + .4 java\DTcomment{código fonte em Java}. + .4 jflex\DTcomment{código fonte para o JFlex}. + .3 test\DTcomment{código fonte dos testes}. + .4 java\DTcomment{código fonte dos testes em Java}. + .2 target\DTcomment{arquivos gerados automaticamente}. + .3 classes\DTcomment{classes geradas pelo compilador de Java}. + .3 generated-sources\DTcomment{código fonte gerado por ferramentas}. + .4 cup\DTcomment{código fonte gerado pelo CUP}. + .4 jflex\DTcomment{código fonte gerado pelo JFlex}. + .3 generated-test-sources\DTcomment{cód. fonte de testes gerado por ferramentas}. + .3 test-classes\DTcomment{classes dos testes geradas pelo compilador de Java}. + .2 pom.xml\DTcomment{arquivo de especificação do projeto}. + } +\end{frame} + +\section{Javaslang} + +\begin{frame}[fragile,allowframebreaks]{Javaslang} + \begin{itemize} + \item Javaslang fornece várias estruturas de dados funcionais, como: + \begin{itemize} + \item tuplas + \item listas + \item árvores + \begin{itemize} + \item Árvores serão amplamente utilizadas para exibir as + estruturas internas do compilador, incluindo as árvores + sintáticas. + \end{itemize} + \end{itemize} + \end{itemize} +\end{frame} + +\begin{frame}[fragile,allowframebreaks]{\texttt{javaslang.Tuple2}} + \footnotesize +\begin{pygmented}[] +import javaslang.Tuple; +import javaslang.Tuple2; + +public class TestJavaslang { + public static void main(String[] args) { + Tuple2 person = Tuple.of("paul", 17); + + String name = person._1; + Integer age = person._2; + + Tuple2 p = + person.map(n -> n + " jones", + a -> a + 1); + + Tuple2 q = + person.map((n, a) -> Tuple.of(n + " jones", a + 1)); + + String s = person.transform((n, a) -> n + ": " + a); + + System.out.println(s); + } +} +\end{pygmented} +\end{frame} + + + +\begin{frame}[fragile,allowframebreaks]{\texttt{javaslang.collection.List}} + \begin{itemize} + \item Exemplos de listas (simplesmente) encadeadas: +\begin{pygmented}[] +List list1 = List.empty(); +List list2 = List.of("nice"); +List list3 = List.of(11, 12, 13); +List list4 = list3.tail(); +List list5 = list4.prepend(10); +\end{pygmented} + \begin{center} + \begin{tikzpicture}[ + cons/.style={draw}, + var/.style={draw=none,blue}, + null/.style={circle,inner sep=0mm,minimum size=1mm}, + ] + + \graph [ + %layered layout, + grow right sep, + nodes={cons}, + ] { + list1[var] -> n1[null,as={}]; + list2[var] -> nice -> n2[null,as={}]; + list3[var] -> 11 -> 12 -> 13 -> n3[null,as={}]; + list4[var] -> 12; + list5[var] -> 10 -> 12; + }; + + \end{tikzpicture} + \end{center} + + \framebreak + + \item Operando com cada elemento de uma lista: +\begin{pygmented}[] +List lst = List.of(10, 20, 30); + +for (Integer x : lst) + System.out.println(x); + +lst.forEach(x -> System.out.println(x)); + +lst.forEach(System.out::println); +\end{pygmented} + + \framebreak + + \item Aplicando uma função a cada elemento da lista e coletando os + resultados em outra lista: +\begin{pygmented}[] +List a = List.of(4.0, 9.0, 25.0); +List c = a.map(Math::sqrt); +List b = a.map(x -> 2*x); +\end{pygmented} + + \framebreak + + \item Reduzindo uma lista: +\begin{pygmented}[] +List a = List.of("1", "2", "3"); +String str = a.fold("", (a1, a2) -> a1 + a2); + +List b = List.of(1, 2, 3, 4); +Integer sum = b.fold(0, (s, x) -> s + x); +\end{pygmented} + + \end{itemize} +\end{frame} + + + + +\begin{frame}[fragile,allowframebreaks]{\texttt{javaslang.collection.Tree}} +\begin{pygmented}[] +Tree tree1 = Tree.empty(); +Tree tree2 = Tree.of("nice"); +Tree tree3 = Tree.of(99, 21, 22, 23); +Tree tree4 = Tree.of(10, + Tree.of(5, Tree.of(2)), + Tree.of(7), + Tree.of(0), + Tree.of(19, Tree.of(3), Tree.of(8))); +\end{pygmented} + \begin{center} + \begin{tikzpicture}[ + cons/.style={draw}, + var/.style={draw=none,blue}, + null/.style={circle,inner sep=0mm,minimum size=1mm}, + ] + + \graph [ + layered layout, + %grow right sep, + nodes={cons}, + ] { + tree1[var] -> n1[null,as={}]; + tree2[var] -> nice; + tree3[var] -> 99 -> {21, 22, 23}; + tree4[var] -> 10 -> {5 -> 2, 7, 0, 19 -> {3, 8}}; + }; + + \end{tikzpicture} + \end{center} +\end{frame} + + +\begin{frame}[fragile,allowframebreaks]{\texttt{javaslang.render.text.PrettyPrinter}} + \small + \begin{columns}[t] + \begin{column}{.75\textwidth} +\begin{pygmented}[] +import javaslang.collection.Tree; + +import javaslang.render.text.PrettyPrinter; + +final Tree tree = + Tree.of("Ann", + Tree.of("Mary", + Tree.of("John", + Tree.of("Avila")), + Tree.of("Karen", + Tree.of("Frank")), + Tree.of("Steven\nAbbot\nBraddock")), + Tree.of("Peter", + Tree.of("Paul\nPalucci"), + Tree.of("Anthony")), + Tree.of("Christopher", + Tree.of("Samuel"))); + +final String out = PrettyPrinter.pp(tree); +\end{pygmented} + \end{column} + \begin{column}{.25\textwidth} +\begin{Verbatim} +Ann +├──Mary +│ ├──John +│ │ └──Avila +│ ├──Karen +│ │ └──Frank +│ └──Steven +│ Abbot +│ Braddock +├──Peter +│ ├──Paul +│ │ Palucci +│ └──Anthony +└──Christopher + └──Samuel +\end{Verbatim} + \end{column} + \end{columns} +\end{frame} + +\begin{frame}[fragile,allowframebreaks]{\texttt{javaslang.render.text.Boxes}} + \footnotesize +\begin{pygmented}[] +import javaslang.collection.Tree; +import javaslang.render.text.Boxes; + +final Tree tree = /* ... */ + +final String out = Boxes.box(tree).toString(); +\end{pygmented} + +\begin{verbatim} + ┌───┐ + │Ann│ + └─┬─┘ + ┌───────────────┴──────┬────────────────┐ + ┌──┴─┐ ┌──┴──┐ ┌─────┴─────┐ + │Mary│ │Peter│ │Christopher│ + └──┬─┘ └──┬──┘ └─────┬─────┘ + ┌───────┬─┴───────┐ ┌────┴────┐ │ +┌──┴─┐ ┌──┴──┐ ┌────┴───┐ ┌───┴───┐ ┌───┴───┐ ┌───┴──┐ +│John│ │Karen│ │ Steven │ │ Paul │ │Anthony│ │Samuel│ +└──┬─┘ └──┬──┘ │ Abbot │ │Palucci│ └───────┘ └──────┘ + │ │ │Braddock│ └───────┘ +┌──┴──┐ ┌──┴──┐ └────────┘ +│Avila│ │Frank│ +└─────┘ └─────┘ +\end{verbatim} +\end{frame} + + +\begin{frame}[fragile,allowframebreaks]{\texttt{javaslang.render.text.DotFile}} + \small + \begin{columns}[t] + \begin{column}{.5\textwidth} +\begin{pygmented}[] +import javaslang.collection.Tree; +import javaslang.render.dot.DotFile; + +final Tree tree = /* ... */; + +DotFile.write(tree, "tree.dot"); +\end{pygmented} + \end{column} + \begin{column}{.5\textwidth} + \begin{itemize} + \item Dot é uma linguagem para descrever grafos. + \item É necessário o pacote \textbf{graphviz}. + \end{itemize} +\begin{Verbatim}[frame=single] +$ dot -Tpng -O tree.dot +\end{Verbatim} + \end{column} + \end{columns} + \begin{center} + \makebox[0pt][r]{\texttt{tree.dot.png}} + \includegraphics[scale=.35]{images/tree.png} + \end{center} +\end{frame} + +\section{Posição no código fonte} + +\begin{frame}[fragile,allowframebreaks]{Localização no código fonte} + \begin{itemize} + \item A localização é usada para reportar erros. + \item Indica onde uma frase do programa começa e termina no código + fonte. + \item Representada pela classe \pyginline|parse.Loc| + \begin{pygmented}[lang=java] +Loc(Location left, Location right) + \end{pygmented} + \item Usa o tipo + \begin{pygmented}[lang=java] +java_cup.runtime.ComplexSymbolFactory.Location + \end{pygmented} + contendo informações como: + \begin{itemize} + \item nome da unidade de compilação (arquivo fonte) + \item número da linha + \item número da coluna + \end{itemize} + \end{itemize} +\end{frame} + +\begin{frame}[fragile,allowframebreaks]{Construção da localização} + \begin{itemize} + \item Alguns métodos de fábrica: +\begin{pygmented}[] +import java_cup.runtime.Symbol; +import java_cup.runtime.ComplexSymbolFactory.ComplexSymbol; +import java_cup.runtime.ComplexSymbolFactory.Location; + +/* ... */ + +public static Loc loc() +public static Loc loc(Location left) +public static Loc loc(Location left, Location right) +public static Loc loc(Symbol symbol) +public static Loc loc(Symbol a, Symbol b) +public static Loc loc(ComplexSymbol symbol) +public static Loc loc(ComplexSymbol a, ComplexSymbol b) +\end{pygmented} + + \item \pyginline|Symbol| e \pyginline|ComplexSymbol| representam + símbolos terminais + \end{itemize} +\end{frame} + + +\section{Gerenciamento de erros} + +\begin{frame}[fragile,allowframebreaks]{Reportagem de erro} + \begin{itemize} + \item Os erros são reportados por meio do mecanismo de + \textbf{exceções} de Java. + \item Erros no programa fonte são representados pela classe + \pyginline|error.CompileError|, subclasse de + \pyginline|RuntimeError|. + \item Erros na implementação do compilador são representados pela + classe \pyginline|error.FatalError|, também subclasse de + \pyginline|RuntimeError|. + \item A classe \pyginline|error.ErrorHelper| tem alguns métodos + estáticos que facilitam a criação de objetos de erro: +\begin{pygmented}[] +static CompilerError error(String message) +static CompilerError error(String format, Object... args) +static CompilerError error(Loc loc, String format, Object... args) + +static FatalError fatal(String format, Object... args) +\end{pygmented} + \end{itemize} +\end{frame} + + +\section{Análise léxica} + +\begin{frame}[fragile,allowframebreaks]{Símbolos terminais} + \begin{itemize} + \item Os \textbf{símbolos terminais} são definidos na gramática + livre de contexto do CUP (arquivo + \texttt{src/main/cup/parser.cup}). + \item Exemplos: +\begin{pygmented}[] +terminal String LITREAL; +terminal PLUS, MINUS, TIMES, DIV; +terminal LPAREN, RPAREN; +\end{pygmented} + + \item O CUP gera uma interface \pyginline|parse.SymbolConstants| + contendo a definição de constantes correspondentes aos terminais + que foram declarados. + + \item Quando relevante os terminais podem ter um valor semântico + associado. + + \item A classe \pyginline|ComplexSymbol| é usada para representar + os símbolos terminais, contendo as seguintes informações: + \begin{itemize} + \item classificação (como declarado na gramática) + \item lexema + \item localização (começo e fim) no código fonte + \item valor semântico + \end{itemize} + \end{itemize} +\end{frame} + +\begin{frame}[fragile,allowframebreaks]{Regras léxicas} + \begin{itemize} + \item O analisador léxico é gerado pelo \textbf{JFlex}. + \item As regras léxicas são especificadas no arquivo + \alert{\texttt{src/main/jflex/lexer.jflex}}. + \item O JFlex cria a classe \pyginline|parse.Lexer| compatível com + o CUP. + \item Os \textbf{lexemas} (palavras que formam os símbolos + léxicos) são descritos por \textbf{expressões regulares}. + \item A classificação so símbolo terminal é feita na \textbf{ação + semântica} (código em Java que faz parte da regra léxica). + \item Para facilitar a criação dos diversos símbolos léxicos + recomenda-se o uso dos \alert{métodos auxiliares} (definidos no + próprio arquivo de especificação): +\begin{pygmented}[] +private Symbol tok(int type, String lexeme, Object value) +private Symbol tok(int type, Object value) +private Symbol tok(int type) +\end{pygmented} + \end{itemize} +\end{frame} + + +\begin{frame}[fragile,allowframebreaks]{Exemplo de regras léxicas} +\begin{pygmented}[lang=text] +[ \t\f\n\r]+ { /* skip */ } + +[0-9]+ ("." [0-9]+)? { return tok(LITREAL, yytext()); } + +"+" { return tok(PLUS); } +"-" { return tok(MINUS); } +"*" { return tok(TIMES); } +"/" { return tok(DIV); } +"(" { return tok(LPAREN); } +")" { return tok(RPAREN); } + +. { throw error(Loc.loc(locLeft()), + "unexpected char '%s'", + yytext()); } +\end{pygmented} +\end{frame} + + +\section{Análise sintática} + +\begin{frame}[fragile,allowframebreaks]{Análise sintática} + \begin{itemize} + \item A especificação sintática (\textbf{gramática livre de + contexto}) é feita no arquivo + \alert{\texttt{src/main/cup/parser.cup}}. + \item Algumas opções de execução do CUP são indicadas no arquivo + \alert{\texttt{pom.xml}}. + \item O analisador sintático é gerado pelo \textbf{CUP}, que cria + a classe \pyginline|parse.Parser| e a interface + \pyginline|parse.SymbolConstants|. + \item Na gramática livre de contexto são especificados: + \begin{itemize} + \item os símbolos terminais + \item os símbols não terminais + \item o símbolo inicial + \item as regras de produção + \end{itemize} + \end{itemize} +\end{frame} + +\begin{frame}[fragile,allowframebreaks]{Ações semânticas} + \begin{itemize} + \item Quando um símbolo (terminal ou não terminal) tem um + \textbf{valor semântico}, o tipo do valor semântico é informado na + declaração do símbolo. + \item O cálculo do valor semântico do símbolo no lado esquerdo de + uma regra de produção é feito usando os valores semênticas dos + símbolos que aparecem no lado direiro da regra através de uma + \textbf{ação semântica} (código em Java). + \item Quando um \alert{nome} é associado a um símbolo no lado + direito de uma regra (por exemplo \pyginline|exp:nome|), o CUP + cria três variáveis (no exemplo \pyginline|nome|, + \pyginline|nomexleft| e \pyginline|nomexright|) no codigo gerado, + contendo o valor semântico, a localização esquerda (onde começa no + código fonte), e a localização direita (onde termina no código + fonte) do símbolo, respectivamente. + \end{itemize} +\end{frame} + + +\begin{frame}[fragile,allowframebreaks]{Exemplo de gramática para o CUP} + \scriptsize +\begin{pygmented}[] +terminal String LITREAL; +terminal PLUS, MINUS, TIMES, DIV; +terminal LPAREN, RPAREN; + +non terminal Exp exp; +non terminal Exp term; +non terminal Exp factor; + +start with exp; + +exp ::= + exp:x PLUS term:y {: RESULT = new ExpBinOp(ExpBinOp.Op.PLUS, x, y); :} +| exp:x MINUS term:y {: RESULT = new ExpBinOp(ExpBinOp.Op.MINUS, x, y); :} +| term:x {: RESULT = x; :} +; + +term ::= +| term:x TIMES factor:y {: RESULT = new ExpBinOp(ExpBinOp.Op.TIMES, x, y); :} +| term:x DIV factor:y {: RESULT = new ExpBinOp(ExpBinOp.Op.DIV, x, y); :} +| factor:x {: RESULT = x; :} +; + +factor ::= + LITREAL:x {: RESULT = new ExpReal(x); :} +| LPAREN exp:x RPAREN {: RESULT = x; :} +; +\end{pygmented} +\end{frame} + + +\section{Árvores sintáticas} + +\begin{frame}[fragile,allowframebreaks]{Árvores sintáticas} + \begin{itemize} + \item \textbf{Árvore sintática} é uma \alert{estrutura de dados + hierárquica} que representa a estrutura sintática do programa + fonte. + \item A \textbf{raiz} da árvore é o \alert{símbolo inicial} da + gramática. + \item As \textbf{folhas} são os \alert{símbolos terminais} que, + lidos da esquerda para a direita, correspondem ao programa fonte. + \item Pode ser: + \begin{itemize} + \item \textbf{concreta}: todos os símbolos na sequência de + derivação são colocados na árvore + \item \textbf{abstrata}: apenas as informações relevantes para o + entendimento da estrutura do programa são mantidos na árvore + \end{itemize} + \item A saída do \textbf{analisador sintático} é uma árvore + sintática abstrata (\alert{AST}). + \end{itemize} +\end{frame} + +\begin{frame}[fragile,allowframebreaks]{Representação} + \begin{itemize} + \item A representação das árvores sintáticas é feita através de + classes no pacote \pyginline|absyn|. + \item A classe abstrata \pyginline|absyn.AST| é superclasse de + todas as árvores sintáticas abstratas. + \item Esta classe implementa a interface \pyginline|ToTree|: +\begin{pygmented}[] +package javaslang.render; + +import javaslang.collection.Tree; + +public interface ToTree { + public abstract Tree.Node toTree(); +} +\end{pygmented} + \item O método \pyginline|toTree| converte a árvore abstrata em + uma estrutura de dados geral para árvores cujos nós armazenam + strings, útil na apresentação visual da árvore sintática. + \end{itemize} +\end{frame} + + +\begin{frame}[fragile,allowframebreaks]{Definindo as árvores abstratas} + \begin{itemize} + \item Para cada \alert{categoria sintática} (como expressões, + comandos, ou declarações) reprentada por um \textbf{símbolo não + terminal} definimos uma subclasse abstrata de + \pyginline|absyn.AST|. + \item Para cada \alert{forma} da categoria sintática para um não + terminal (como as formas de expressão: expressão constante, + operação binária, chamada de função, etc.), representada por uma + \textbf{regra de produção}, definamos uma subclasse da classe que + representa aquela forma específica da categoria sintática. + \item + Nesta classe deve-se: + \begin{itemize} + \item definir os \alert{campos} necessários para os componentes + (sub-árvores) da árvore sintática, + \item definir \alert{construtores} que inicializam estes campos + com valores passados como argumentos, + \item definir o método \pyginline|toTree|. + \end{itemize} + \end{itemize} +\end{frame} + +\begin{frame}[fragile,allowframebreaks]{A classe AST} +\begin{pygmented}[] +package absyn; + +import javaslang.render.ToTree; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +public abstract class AST implements ToTree { + + @Override + public String toString() { + return ToStringBuilder.reflectionToString( + this, + ToStringStyle.SHORT_PREFIX_STYLE); + } + +} +\end{pygmented} +\end{frame} + +\begin{frame}[fragile,allowframebreaks]{A classe Exp} +\begin{pygmented}[] +package absyn; + +public abstract class Exp extends AST { +} +\end{pygmented} +\end{frame} + +\begin{frame}[fragile,allowframebreaks]{A classe ExpReal} +\begin{pygmented}[] +package absyn; + +import javaslang.collection.Tree; + +public class ExpReal extends Exp { + + public final String value; + + public ExpReal(String value) { + this.value = value; + } + + @Override + public Tree.Node toTree() { + return Tree.of("ExpReal: " + value); + } +} +\end{pygmented} +\end{frame} + +\begin{frame}[fragile,allowframebreaks]{A classe ExpReal} + \relscale{0.78} +\begin{pygmented}[] +package absyn; + +import javaslang.collection.Tree; + +public class ExpBinOp extends Exp { + + public enum Op {PLUS, MINUS, TIMES, DIV} + + public final Op op; + public final Exp left; + public final Exp right; + + public ExpBinOp(Op op, Exp left, Exp right) { + this.op = op; + this.left = left; + this.right = right; + } + + @Override + public Tree.Node toTree() { + return Tree.of("ExpBinOp: " + op, left.toTree(), right.toTree()); + } +} +\end{pygmented} +\end{frame} + + +\section{Geração de código} + +\begin{frame}[fragile,allowframebreaks]{Representação intermediária} + \begin{itemize} + \item A árvore sintática do programa fonte é traduzida para uma + representação intermediária do código fonte. + \item Usaremos a representação intermediária do framework + \textbf{LLVM}. + \item A biblioteca Java \texttt{javacpp-presets-llvm} será usada + para criar a representação intermediária. + \item A classe \pyginline|absyn.Exp| deve ter um método abstrato + para converter uma expressão para a sua representação + intermediária. +\begin{pygmented}[] +import static org.bytedeco.javacpp.LLVM.*; +/* ... */ +public abstract LLVMValueRef translate(LLVMModuleRef module, + LLVMBuilderRef builder); +\end{pygmented} + \item Suas subclasses devem implementar este método. + \end{itemize} +\end{frame} + +\begin{frame}[fragile,allowframebreaks]{Representação intermediária de constantes} +\begin{pygmented}[] +package absyn; + +// ... +import static org.bytedeco.javacpp.LLVM.*; + +public class ExpReal extends Exp { + // ... + + @Override + public LLVMValueRef translate(LLVMModuleRef module, + LLVMBuilderRef builder) { + return LLVMConstRealOfString(LLVMDoubleType(), value); + } +} +\end{pygmented} +\end{frame} + +\begin{frame}[fragile,allowframebreaks]{Representação intermediária de operação binária} + \relscale{0.76} +\begin{pygmented}[] +package absyn; + +// ... +import static org.bytedeco.javacpp.LLVM.*; +import static error.ErrorHelper.fatal; + +public class ExpBinOp extends Exp { + // ... + + @Override + public LLVMValueRef translate(LLVMModuleRef module, LLVMBuilderRef builder) { + final LLVMValueRef v_left = left.translate(module, builder); + final LLVMValueRef v_right = right.translate(module, builder); + switch (op) { + case PLUS: return LLVMBuildFAdd(builder, v_left, v_right, "addtmp"); + case MINUS: return LLVMBuildFSub(builder, v_left, v_right, "subtmp"); + case TIMES: return LLVMBuildFMul(builder, v_left, v_right, "multmp"); + case DIV: return LLVMBuildFDiv(builder, v_left, v_right, "divtmp"); + default: fatal("unknown operator %s in binary operation", op); + return LLVMConstReal(LLVMDoubleType(), 0); + } + } +} +\end{pygmented} +\end{frame} + + + +\begin{frame}[fragile,allowframebreaks]{Atividade 1} + \begin{tcolorbox}[title=Inverso aditivo] + Implementar a operação que calcula o inverso aditivo (ou negação) + à linguagem \lang{}. Será usado o operador unário prefixo + \pyginline|-|, com precedência maior que dos operadores + aritméticos binários. + \begin{enumerate} + \item Definir uma nova classe \pyginline|absyn.ExpNegate| + \begin{itemize} + \item subclasse de \pyginline|absyn.Exp| + \item contendo um campo correspondente ao operando da negação + \item implementar o método \pyginline|toTree| + \item implementar o método \pyginline|translate| + \end{itemize} + \item Acrescentar uma regra de produção adequada na gramática da + linguagem. + \end{enumerate} + + \begin{tips} + \item Na geração de código use o método + + \href{http://bytedeco.org/javacpp-presets/llvm/apidocs/org/bytedeco/javacpp/LLVM.html#LLVMBuildFNeg-org.bytedeco.javacpp.LLVM.LLVMBuilderRef-org.bytedeco.javacpp.LLVM.LLVMValueRef-java.lang.String-}{\pyginline|LLVMBuildFNeg|} + + \href{http://llvm.org/docs/doxygen/html/group__LLVMCCoreInstructionBuilder.html#gaf748025627b03f4f2659b006b127b758}{\pyginline|LLVMBuildFNeg|} + \end{tips} +\end{tcolorbox} + + \framebreak + + Comandos úteis: +\begin{Verbatim}[frame=single] +$ cd /eplan +$ git checkout master +$ git pull upstream master +$ git checkout -b atividade1 +$ # desenvolva sua atividade +$ # faça testes +$ git status +$ git add +$ git commit -m +$ git push origin atividade1 +$ # faça um pull request no github +\end{Verbatim} +\end{frame} + + +\section{Análise semântica} + +\begin{frame}[fragile,allowframebreaks]{Anotando a localização na árvore sintática} + \begin{itemize} + \item Na análise semêntica os erros encontrados devem ser + reportados. + + \item Deve-se indicar a localização do erro no código fonte. + + \item Por isto a árvore sintática deve conter a + \textbf{localização}. + + \item O atributo \pyginline|loc| foi adicionado à classe abstrata + \pyginline|absyn.AST|. +\begin{pygmented}[] +import parse.Loc; + +public abstract class AST implements ToTree { + + // Location where the phrase was found in the source code + protected final Loc loc; + + public AST(Loc loc) { + this.loc = loc; + } + + // ... +} +\end{pygmented} + + \framebreak + + \item Um argumento correspondente foi adicionado aos construtores + das subclasses de \pyginline|absyn.AST|. Por exemplo: +\begin{pygmented}[] +public class ExpReal extends Exp { + + public final String value; + + public ExpReal(Loc loc, String value) { + super(loc); + this.value = value; + } + + // ... +} +\end{pygmented} + + \framebreak + + \item A criação das árvores sintáticas nas ações semânticas do + analisador sintático deve informar a localização. Exemplo: +\begin{pygmented}[] +factor ::= + LITREAL:x {: RESULT = new ExpReal(loc(xxleft,xxright), x); :} +; +\end{pygmented} + + \item Na regra de produção, ao indicarmos o nome do valor + semântico de um símbolo, são criadas três variáveis no analisador + gerado pelo CUP, contendo as seguintes informações: + \begin{itemize} + \item valor semântico do símbolo + \item posição onde o símbolo começou no código fonte + \item posição onde o símbolo terminou no código fonte + \end{itemize} + No exemplo estas variáveis são respectivamente \pyginline|x|, + \pyginline|xxleft|, e \pyginline|xxright|. + \end{itemize} +\end{frame} + + +\begin{frame}[fragile,allowframebreaks]{Representando os tipos da linguagem} + \begin{itemize} + \item O pacote \pyginline|type| contém classes usadas para representar + os tipos da linguagem sendo compilada. + \item A representação geral é \pyginline|Type|. + \item \pyginline|Type| é uma classe abstrata, com subclasses concretas + para representar cada uma das possibiliades: + \begin{center} + \small + \begin{tabular}{lll} \hline + \textbf{descrição} & \textbf{classe} & \textbf{objeto} \\\hline + \texttt{bool} & \pyginline|BOOL| & \pyginline|BOOL.T| \\ + \texttt{int} & \pyginline|INT| & \pyginline|INT.T| \\ + \texttt{real} & \pyginline|REAL| & \pyginline|REAL.T| \\ + \texttt{char} & \pyginline|CHAR| & \pyginline|CHAR.T| \\ + \texttt{string} & \pyginline|STRING| & \pyginline|STRING.T| \\ + \emph{array} & \pyginline|ARRAY| & \pyginline|new Array(Type element)| \\ + \emph{registro} & \pyginline|RECORD| & \pyginline|new RECORD(List> fields)| \\ + \emph{registro nulo} & \pyginline|NIL| & \pyginline|NIL.T| \\ + \texttt{unit} & \pyginline|UNIT| & \pyginline|UNIT.T| \\ + \emph{nome} & \pyginline|NAME| & \pyginline|new Name(Symbol name, Type binding)| \\\hline + \end{tabular} + \end{center} + \end{itemize} +\end{frame} + +\begin{frame}[fragile,allowframebreaks]{Algumas operações com tipos} + \begin{description} + \item[\pyginline|Type actual()|]\mbox{}\newline + \begin{itemize} + \item Retorna a representação que é de fato usada para o tipo. + \item É o próprio objeto, exceto para a classe \pyginline|NAME|, onde + é retornado \pyginline|binding.actual()|. + \end{itemize} + \framebreak + + \item[\pyginline|boolean is(Type type)|]\mbox{}\newline + \begin{itemize} + \item Verifica se o tipo que recebe a mensagem é compatível (é igual + ou pode ser convertido para) o tipo dado como argumento. + \item Retorna \pyginline|true| somente se os dois tipos forem + idênticos, exceto nos seguintes casos: + \begin{itemize} + \item o tipo do registo nulo (\texttt{nil}), \pyginline|NIL|, é + compatível com \pyginline|NIL| e com qualquer tipo registro, + \item um tipo \pyginline|NAME| é compatível com algum tipo, se + e somente se o seu atributo \pyginline|binding| for compatível + com o tipo. + \end{itemize} + \end{itemize} + \framebreak + + \item[\pyginline|boolean is(Type... types)|]\mbox{}\newline + \begin{itemize} + \item Verifica a compatibilidade com algum dos tipos dados como + argumentos. + \end{itemize} + \end{description} +\end{frame} + + +\begin{frame}[fragile,allowframebreaks]{Reportando erros comuns} + \begin{itemize} + \item A interface \pyginline|semantic.SemanticHelper| define + alguns métodos para reportar erros comumente encontrados pelo + compilador no código fonte. + + \item Um dos erros mais comuns é a inconsistência de tipos. +\begin{pygmented}[] +public interface SemanticHelper { + + static CompilerError typeMismatch(Loc loc, Type found, Type... expected) + // ... + } + + // ... +} +\end{pygmented} + \end{itemize} +\end{frame} + +\begin{frame}[fragile,allowframebreaks]{Análise semântica} + \begin{itemize} + \item O analisador semântico + \begin{itemize} + \item verifica a consistência do programa + \item calcula o tipo das expressões + \end{itemize} + \item Expressões tem um atributo \pyginline|type| cujo valor é a + representação do tipo obtido para a expressão. + \item \pyginline|type| é calculado pelo analisador semântico. +\begin{pygmented}[] +import types.Type; + +public abstract class Exp extends AST { + + // Type of the expression, calculated by the semantic analyser + public Type type; + + // Obtain the type of the expression as a string prefixed by the given text. + protected String annotateType(String text) { + final String theType = type == null ? "" : "\n<" + type + ">"; + return text + theType; + } + + // ... +} +\end{pygmented} + + \framebreak + + \item O método \pyginline|semantic| da classe + \pyginline|absyn.Exp| faz análise semântica da expressão. + \begin{itemize} + \item Usa o método auxiliar \pyginline|semantic_| para fazer a + análise semântica de fato, obtendo o tipo da expressão. + \item Coloca o tipo encontrado no atributo \pyginline|type|. + \end{itemize} +\begin{pygmented}[] +public abstract class Exp extends AST { + // ... + + // Do semantic analysis of the expression + public Type semantic() { + type = semantic_(); + return type; + } + + // Type check the expression. Should be defined in the concrete subclasses. + protected abstract Type semantic_(); +} +\end{pygmented} + + \item Subclasses de \pyginline|absyn.Exp| devem definir o método + \pyginline|semantic_| apropriadamente. + + \item Por exemplo: +\begin{pygmented}[] +public class ExpReal extends Exp { + // ... + + @Override + protected Type semantic_() { + return REAL.T; + } +} +\end{pygmented} + + \end{itemize} +\end{frame} + + + +\begin{frame}[fragile,allowframebreaks]{Atividade 2: literal inteiro} + Implementar \textbf{literais inteiros} no compilador de \lang{}. + \begin{enumerate} + \item Para desenvolver esta atividade faça o \emph{checkout} da + \alert{versão 0.17} do projeto. + + \item Definir uma nova classe \pyginline|types.INT| para + representar o tipo \texttt{int} de \lang{}. + + \item Definir uma nova classe \pyginline|absyn.ExpInt| para + representar as árvores sintáticas das constantes inteiras: + \begin{itemize} + \item subclasse de \pyginline|absyn.Exp| + \item contendo um campo correspondente ao valor da constante + \item implementar o método \pyginline|toTree| + \item implementar o método \pyginline|semantic_| + \item implementar o método \pyginline|translate| + \end{itemize} + + \item Modifique a classe \pyginline|absyn.ExpBinOp| de forma que + os operadores aritméticos aceitem operandos inteiros e + reais. Neste momento não é necessário fazer conversão implícita + dos operandos de inteiro para real. Por hora os operandos devem + apenas ser do mesmo tipo numérico. + + \item Acrescentar na gramática da linguagem: + \begin{itemize} + \item o símbolo terminal \pyginline|LITINT| + \item uma regra de produção adequada + \end{itemize} + + \item Na especificação léxica da linguagem + \begin{itemize} + \item acrescentar uma regra para o literal inteiro: + + Um literal inteiro é uma sequência não vazia de dígitos + decimais. + + \item modificar a regra do literal real para não casar com + literais inteiros + \end{itemize} + + \item Definir a função \pyginline|__eplan_print_int| (em C) na + biblioteca padrão de \lang{} para exibir um valor inteiro. O + protótipo da função deve ser: +\begin{pygmented}[] +extern void __eplan_print_int(const int32_t x) +\end{pygmented} + O tipo \pyginline|int32_t| está definido em + \texttt{inttypes.h}. Use a função \pyginline|printf| e a macro + \pyginline|PRIi32| na formatação da saída. + + \item Modificar o método \pyginline|addRuntime| na classe + \pyginline|translate.Generator| para adicionar o protótipo da função + \pyginline|__eplan_print_int| no código gerado. + + \item Modificar o método \pyginline|addPrintResult| na classe + \pyginline|codegen.Generator| para considerar o caso de valores + inteiros. Este método gera código para exibir o valor de uma + expressão na saída padrão. + \end{enumerate} + + \begin{tips} + \item Na geração de código use os métodos + + \href{http://bytedeco.org/javacpp-presets/llvm/apidocs/org/bytedeco/javacpp/LLVM.html#LLVMConstIntOfString-org.bytedeco.javacpp.LLVM.LLVMTypeRef-java.lang.String-byte-}{\pyginline|LLVMConstIntOfString|} + + \href{http://bytedeco.org/javacpp-presets/llvm/apidocs/org/bytedeco/javacpp/LLVM.html#LLVMBuildAdd-org.bytedeco.javacpp.LLVM.LLVMBuilderRef-org.bytedeco.javacpp.LLVM.LLVMValueRef-org.bytedeco.javacpp.LLVM.LLVMValueRef-java.lang.String-}{\pyginline|LLVMBuildAdd|} + + \href{http://bytedeco.org/javacpp-presets/llvm/apidocs/org/bytedeco/javacpp/LLVM.html#LLVMBuildSub-org.bytedeco.javacpp.LLVM.LLVMBuilderRef-org.bytedeco.javacpp.LLVM.LLVMValueRef-org.bytedeco.javacpp.LLVM.LLVMValueRef-java.lang.String-}{\pyginline|LLVMBuildSub|} + + \href{http://bytedeco.org/javacpp-presets/llvm/apidocs/org/bytedeco/javacpp/LLVM.html#LLVMBuildMul-org.bytedeco.javacpp.LLVM.LLVMBuilderRef-org.bytedeco.javacpp.LLVM.LLVMValueRef-org.bytedeco.javacpp.LLVM.LLVMValueRef-java.lang.String-}{\pyginline|LLVMBuildMul|} + + \href{http://bytedeco.org/javacpp-presets/llvm/apidocs/org/bytedeco/javacpp/LLVM.html#LLVMBuildSDiv-org.bytedeco.javacpp.LLVM.LLVMBuilderRef-org.bytedeco.javacpp.LLVM.LLVMValueRef-org.bytedeco.javacpp.LLVM.LLVMValueRef-java.lang.String-}{\pyginline|LLVMBuildSDiv|} +\end{tips} +\end{frame} + + + +% \section{Identificadores} + + +% \begin{frame}[fragile,allowframebreaks]{Representando identificadores} +% \begin{itemize} +% \item \textbf{Identificadores} são nomes dados a diversas entidades em +% um programa: tipos, variáveis, funções, módulos, classes, etc. +% \item Quando uma declaração é analisada, alguma informação sobre o +% identificador é armazenada em um \textbf{ambiente} (um dicionário, +% também chamado de \textbf{tabela de símbolos}), contendo por exemplo: +% \begin{itemize} +% \item tipo de uma variável +% \item tipo dos argumentos e do resultado de uma função +% \item estrutura de um tipo +% \end{itemize} +% \item Posteriormente quando o nome é usado em uma frase, esta informação +% deve ser recuperada do ambiente para analisar a frase. +% \item Poderiam ser representados por \emph{strings}. +% \item Porém \pyginline|String| não é uma boa escolha porque: +% \begin{itemize} +% \item a representação na memória é ineficiente, pois todos os +% caracteres do nome são armazenados repetidamente, uma vez para cada +% ocorrência do nome, e +% \item a pesquisa na tabela de símbolos é ineficiente, pois é baseada +% na comparação de possivelmente todos os caracteres da nome. +% \end{itemize} +% \end{itemize} +% \end{frame} + + +% \begin{frame}[fragile,allowframebreaks]{Símbolos} +% \begin{itemize} +% \item Novo tipo para representar identificadores: +% \pyginline|symbol.Symbol|. +% \item Quando o analisador léxico encontra o nome pela primeira vez, ele +% o armazena na memória. +% \item Nas demais ocorrências do nome o analisador léxico usa uma +% referência para o nome já armazenado na memória. +% \item Desta forma cada nome é alocado na memória uma única vez. +% \item A comparação dos nomes se resume à comparação de referências. +% \item Consegue-se assim uma melhor eficiência no uso da memória, e +% também na comparação de nomes, fundamental para a pesquisa na tabela de +% símbolos. +% \item Um símbolo é construído a partir de uma string usando o método +% \pyginline|symbol.Symbol symbol.Symbol.symbol(String nome)|. +% \end{itemize} +% \end{frame} + +% \begin{frame}[fragile,allowframebreaks]{Há duas classes \texttt{Symbol}} +% \begin{itemize} +% \item \pyginline|java_cup.runtime.Symbol|\newline +% Representam símbolos gramaticais (terminais e não terminais). + +% \item \pyginline|symbol.Symbol|\newline +% Representam identificadores. +% \end{itemize} +% \end{frame} + + +% \begin{frame}[fragile,allowframebreaks]{O problema dos tipos mutuamente recursivos} +% \begin{itemize} +% \item A classe \pyginline|NAME| é usada na compilação de tipos +% mutuamente recursivos. +% \item O tipo é usado na definição de si mesmo ou de outros tipos com +% dependência mútua. +% \item Exemplo: +% \begin{pygmented}[lang=text] +% let type list = { head: tree, tail: list } +% type tree = { info: int, children: list } +% in +% # ... +% \end{pygmented} +% \item Problema: ao pesquisar a ocorrência do tipo que aparece no lado +% direito da definição, ele ainda não se encontra na tabela de símbolos +% (pois está sendo compilado neste momento), gerando um erro de \emph{tipo +% indefinido}. +% \item Solução: compilação em duas etapas: +% \begin{itemize} +% \item Primeiramente é criada e inserida na tabela de símbolos uma +% instância de \pyginline|NAME| para representar o tipo. +% \item Posteriormente compila-se a definição do tipo e atualiza-se o +% atributo \pyginline|binding| deste objeto. +% \end{itemize} +% \end{itemize} +% \end{frame} + + +% \begin{frame}[fragile,allowframebreaks]{Ambiente (Tabelas de Símbolos)} +% \begin{itemize} +% \item O \textbf{ambiente} é formado por \textbf{tabelas de símbolos} +% (\textbf{dicionários} cuja chave é um símbolo). +% \item Contém informações sobre os identificadores válidos em um +% determinado ponto do programa sendo compilado: +% \begin{itemize} +% \item \textbf{variáveis}: tipo da variável +% \item \textbf{funções}: tipo dos argumentos e tipo do resultado da +% função +% \item \textbf{tipos}: estrutura do tipo +% \end{itemize} +% \item Implementação: classe \pyginline|env.Env| +% \item Há dois espaços de nomes, implementados em duas tabelas de +% símbolos: +% \begin{itemize} +% \item \textbf{variáveis e funções}: atributo \pyginline|venv|\newline +% A cada símbolo é associado uma instância da classe abstrata +% \pyginline|env.Entry|, que tem duas classes concretas: +% \begin{itemize} +% \item \pyginline|env.VarEntry(Type ty)| +% \item \pyginline|env.FunEntry(Type result, List formals)| +% \end{itemize} +% \item \textbf{tipos}: atributo \pyginline|tenv|\newline A cada símbolo +% é associado uma instância da classe abstrata \pyginline|type.Type|. +% \end{itemize} +% \end{itemize} +% \begin{center} +% \small +% \begin{tabular}{lll}\hline +% \textbf{espaço de nomes} & \textbf{atributo} & \textbf{informação associada ao símbolo} \\\hline +% variáveis e funções & \pyginline|venv| & \pyginline|Entry| \\ +% & & ~\pyginline|VarEntry(Type ty)| \\ +% & & ~\pyginline|FunEntry(Type result, List formals)| \\\hline +% tipos & \pyginline|tenv| & \pyginline|Type| \\\hline +% \end{tabular} +% \end{center} +% \end{frame} + + +% \begin{frame}[fragile,allowframebreaks]{Nomes pré-definidos} +% \begin{itemize} +% \item O construtor da classe \pyginline|Env| deve criar as tabelas de +% símbolos e incluir os nomes pré-definidos da linguagem (biblioteca +% padrão). + +% \item Tipos: +% \begin{pygmented}[lang=text] +% bool +% int +% real +% char +% string +% unit +% \end{pygmented} + +% \item Variáveis: não há nenhuma variável pré-definida. +% \framebreak + +% \item Funções: +% \begin{pygmented}[lang=text] +% function print_bool(x: bool): unit +% function not(b: bool): bool + +% function print_int(x: int): unit + +% function print_real(x: real): unit +% function round(f: real): int +% function ceil(f: real): int +% function floor(f: real): int +% function real(i: int): real + +% function print(x: string): unit +% function length(s: string): int +% function substring(s: string, start: int, length: int): string +% \end{pygmented} +% \end{itemize} +% \end{frame} + + + +% \begin{frame}[fragile,allowframebreaks]{Variáveis simples} +% \begin{center} +% \begin{synshorts} +% \small +% \begin{tabular}{r@{$\;\rightarrow\;$}l>{\textcolor{blue}\bgroup}r<{\egroup}} +% & & variável \\ +% & ":=" & atribuição \\ +% & "let" "in" & expressão de declaração \\[.9em] +% & "id" & variável simples \\[.9em] +% & & sequência de declarações \\ +% & & \\[.9em] +% & "var" "id" ":" "id" "=" & declaração de variável \\ +% & "var" "id" "=" & \\ +% \end{tabular} +% \end{synshorts +% } +% \end{center} +% \end{frame} + +% \begin{frame}[fragile,allowframebreaks]{Variáveis simples: árvores sintáticas} +% \begin{pygmented}[] +% // declarações +% Dec(Loc loc) // classe abstrata +% VarDec(Loc loc, Symbol name, Symbol type, Exp init) + +% // variáveis +% Var(Loc loc) // classe abstrata +% SimpleVar(Loc loc, Symbol name) + +% // expressões +% LetExp(Loc loc, List decs, Exp body) +% VarExp(Loc loc, Var var) +% AssignExp(Loc loc, Var var, Exp exp) +% \end{pygmented} +% \end{frame} + +\begin{frame} + \begin{center} + Fim + + % \texttt{Fim} + + % \texttt{\textbf{Fim}} + + % \textbf{\texttt{Fim}} + + % {This is big!} + + % {\smaller[3] This is big!} + \end{center} +\end{frame} + +\end{document} + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: t +%%% End: diff --git a/eplan/doc/images/tree.dot b/eplan/doc/images/tree.dot new file mode 100644 index 0000000..66bca33 --- /dev/null +++ b/eplan/doc/images/tree.dot @@ -0,0 +1,22 @@ +digraph tree { + lbl8 [label="Anthony"]; + lbl6 [label="Peter"]; + lbl7 [label="Paul +Palucci"]; + lbl0 [label="Ann"]; + lbl1 [label="Mary"]; + lbl4 [label="Karen"]; + lbl5 [label="Steven +Abbot +Braddock"]; + lbl2 [label="John"]; + lbl3 [label="Avila"]; + lbl0 -> lbl1; + lbl1 -> lbl2; + lbl2 -> lbl3; + lbl1 -> lbl4; + lbl1 -> lbl5; + lbl0 -> lbl6; + lbl6 -> lbl7; + lbl6 -> lbl8; +} diff --git a/eplan/doc/images/tree.png b/eplan/doc/images/tree.png new file mode 100644 index 0000000..d6b76a8 Binary files /dev/null and b/eplan/doc/images/tree.png differ diff --git a/eplan/driver b/eplan/driver new file mode 100755 index 0000000..39eb0fb --- /dev/null +++ b/eplan/driver @@ -0,0 +1,12 @@ +#! /bin/sh + +if [ -x nixos-version ]; then + if nixos-version &>/dev/null; then + export LD_LIBRARY_PATH=/nix/store/2vcyb5n74xh8c97rb6pnr25cdyav9x0f-gcc-5.4.0/lib64 + fi +fi + +# export LC_NUMERIC=en_US-UTF-8 + +exec java -jar target/uber-eplan-0.1-SNAPSHOT.jar $@ + diff --git a/eplan/eplan-compiler.pdf b/eplan/eplan-compiler.pdf new file mode 100644 index 0000000..d062f00 Binary files /dev/null and b/eplan/eplan-compiler.pdf differ diff --git a/eplan/images/fork.png b/eplan/images/fork.png new file mode 100644 index 0000000..e33d5c3 Binary files /dev/null and b/eplan/images/fork.png differ diff --git a/eplan/pom.xml b/eplan/pom.xml new file mode 100644 index 0000000..a16fbbb --- /dev/null +++ b/eplan/pom.xml @@ -0,0 +1,145 @@ + + 4.0.0 + + br.ufop.decom.bcc328 + eplan + 0.1-SNAPSHOT + jar + + EPlan compiler + https://github.com/romildo/eplan + + + UTF-8 + 1.8 + 1.8 + + + + + com.beust + jcommander + 1.58 + + + + com.github.vbmacher + java-cup-runtime + 11b-20160615 + + + + org.apache.commons + commons-lang3 + 3.5 + + + + io.javaslang + javaslang + 2.0.5 + + + + io.javaslang + javaslang-render + 2.0.0 + + + + junit + junit + 4.12 + test + + + + org.assertj + assertj-core + 3.6.2 + test + + + + + + + de.jflex + jflex-maven-plugin + 1.6.1 + + + + generate + + + + + + + com.github.vbmacher + cup-maven-plugin + 11b-20160615 + + + + generate + + + + + parse + Parser + SymbolConstants + true + + true + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.0.2 + + + + main.Driver + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.0.0 + + + package + + shade + + + + + + main.Driver + + + + + + + uber-${project.artifactId}-${project.version} + + + + + + + diff --git a/eplan/run b/eplan/run new file mode 100755 index 0000000..95d3b41 --- /dev/null +++ b/eplan/run @@ -0,0 +1,21 @@ +#! /bin/sh + +F=unknown +IMGVIEWER=eog + +if command -v which >/dev/null 2>&1; then + rlwrap ./driver --box-annotated-ast --dot-annotated-ast $@ +else + ./driver $@ +fi || exit $? + +if command -v dot >/dev/null 2>&1; then + echo "Generating image of AST" + echo "-------------------------------------------------" + dot -O -Tpng $F.annotated.dot + if command -v $IMGVIEWER >/dev/null 2>&1; then + echo "View image of AST" + echo "-------------------------------------------------" + $IMGVIEWER $F.annotated.dot.png &> /dev/null + fi +fi diff --git a/eplan/src/main/cup/parser.cup b/eplan/src/main/cup/parser.cup new file mode 100644 index 0000000..ef83e71 --- /dev/null +++ b/eplan/src/main/cup/parser.cup @@ -0,0 +1,121 @@ +package parse; + +import error.ErrorHelper; +import java_cup.runtime.Symbol; +import java_cup.runtime.ComplexSymbolFactory.ComplexSymbol; +import javaslang.collection.List; +import absyn.*; + +parser code {: + public Parser(Lexer lex) { + this(lex, lex.getSymbolFactory()); + } + + private Loc loc(Location left, Location right) { + return Loc.loc(left, right); + } + + /* override error routines */ + private Loc locOfInfo(Object info) { + return info instanceof ComplexSymbol ? + Loc.loc((ComplexSymbol) info) : + info instanceof Symbol ? + Loc.loc((Symbol) info) : + Loc.loc(cur_token); + } + private String lexemeOfInfo(Object info) { + return info instanceof ComplexSymbol ? + " at '" + ((ComplexSymbol) info).getName() + "'" : + ""; + + } + public void report_fatal_error(String message, Object info) { + done_parsing(); + throw ErrorHelper.error(locOfInfo(info), "%s%s%nCan't recover from previous error(s), giving up.", message, lexemeOfInfo(info)); + } + public void report_error(String message, Object info) { + throw ErrorHelper.error(locOfInfo(info), "%s%s", message, lexemeOfInfo(info)); + } +:}; + +terminal String LITINT; +terminal String LITREAL; +terminal String LITBOOL; +terminal String ID; +terminal PLUS, MINUS, TIMES, DIV, UMINUS; +terminal AND, OR; +terminal LPAREN, RPAREN; +terminal COMMA, SEMICOLON; +terminal VAR, EQ, COLON; +terminal LET, IN; + +non terminal Exp program; +non terminal Exp exp; +non terminal List exps, expsRest; +non terminal List expseq, expseqRest; +non terminal Dec dec; +non terminal List decs; +non terminal Var var; + +precedence left OR; +precedence left AND; +precedence left PLUS, MINUS; +precedence left TIMES, DIV; +precedence left UMINUS; + +start with program; + +program ::= + exp:e {: RESULT = e; :} +; + +exp ::= + exp:x PLUS exp:y {: RESULT = new ExpBinOp(loc(xxleft,yxright), ExpBinOp.Op.PLUS, x, y); :} +| exp:x MINUS exp:y {: RESULT = new ExpBinOp(loc(xxleft,yxright), ExpBinOp.Op.MINUS, x, y); :} +| exp:x TIMES exp:y {: RESULT = new ExpBinOp(loc(xxleft,yxright), ExpBinOp.Op.TIMES, x, y); :} +| exp:x DIV exp:y {: RESULT = new ExpBinOp(loc(xxleft,yxright), ExpBinOp.Op.DIV, x, y); :} +| exp:x AND exp:y {: RESULT = new ExpBinOp(loc(xxleft,yxright), ExpBinOp.Op.AND, x, y); :} +| exp:x OR exp:y {: RESULT = new ExpBinOp(loc(xxleft,yxright), ExpBinOp.Op.OR, x, y); :} +| LITINT:x {: RESULT = new ExpInt(loc(xxleft,xxright), x); :} +| LITREAL:x {: RESULT = new ExpReal(loc(xxleft,xxright), x); :} +| LITBOOL:x {: RESULT = new ExpBool(loc(xxleft,xxright), x); :} +| MINUS:m exp:x {: RESULT = new ExpNegate(loc(mxleft,xxright), x); :} %prec UMINUS +| ID:f LPAREN exps:x RPAREN:r {: RESULT = new ExpCall(loc(fxleft,rxright), f, x); :} +| var:v {: RESULT = new ExpVar(loc(vxleft,vxright), v); :} +| LET:l decs:ds IN exp:b {: RESULT = new ExpLet(loc(lxleft,bxright), ds, b); :} +| LPAREN:l expseq:es RPAREN:r {: RESULT = new ExpSeq(loc(lxleft,rxright), es); :} +; + +exps ::= + /* empty */ {: RESULT = List.empty(); :} +| exp:x expsRest:xs {: RESULT = xs.prepend(x); :} +; + +expsRest ::= + /* empty */ {: RESULT = List.empty(); :} +| COMMA exp:x expsRest:xs {: RESULT = xs.prepend(x); :} +; + +expseq ::= + /* empty */ {: RESULT = List.empty(); :} +| exp:x expseqRest:xs {: RESULT = xs.prepend(x); :} +; + +expseqRest ::= + /* empty */ {: RESULT = List.empty(); :} +| SEMICOLON exp:x expseqRest:xs {: RESULT = xs.prepend(x); :} +; + +dec ::= + VAR:v ID:x COLON ID:t EQ exp:e {: RESULT = new DecVar(loc(vxleft,exright), x, t, e); :} +| VAR:v ID:x EQ exp:e {: RESULT = new DecVar(loc(vxleft,exright), x, null, e); :} +; + +decs ::= + dec:x {: RESULT = List.of(x); :} +| dec:x decs:xs {: RESULT = xs.prepend(x); :} +; + +var ::= + ID:v {: RESULT = new VarSimple(loc(vxleft,vxright), v); :} +; diff --git a/eplan/src/main/java/absyn/AST.java b/eplan/src/main/java/absyn/AST.java new file mode 100644 index 0000000..4663cb6 --- /dev/null +++ b/eplan/src/main/java/absyn/AST.java @@ -0,0 +1,22 @@ +package absyn; + +import javaslang.render.ToTree; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import parse.Loc; + +public abstract class AST implements ToTree { + + // Location where the phrase was found in the source code + protected final Loc loc; + + public AST(Loc loc) { + this.loc = loc; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); + } + +} diff --git a/eplan/src/main/java/absyn/Dec.java b/eplan/src/main/java/absyn/Dec.java new file mode 100644 index 0000000..f29bb85 --- /dev/null +++ b/eplan/src/main/java/absyn/Dec.java @@ -0,0 +1,16 @@ +package absyn; + +import env.Env; +import parse.Loc; +import types.Type; + +public abstract class Dec extends AST { + + public Dec(Loc loc) { + super(loc); + } + + // Do semantic analysis of the declaraction + public abstract void semantic(Env env); + +} diff --git a/eplan/src/main/java/absyn/DecVar.java b/eplan/src/main/java/absyn/DecVar.java new file mode 100644 index 0000000..aff4870 --- /dev/null +++ b/eplan/src/main/java/absyn/DecVar.java @@ -0,0 +1,46 @@ +package absyn; + +import env.Env; +import javaslang.collection.List; +import javaslang.collection.Tree; +import parse.Loc; +import semantic.SemanticHelper; +import types.Type; + +public class DecVar extends Dec { + + public final String name; + public final String typeName; + public final Exp init; + + public DecVar(Loc loc, String name, String typeName, Exp init) { + super(loc); + this.name = name; + this.typeName = typeName; + this.init = init; + } + + @Override + public Tree.Node toTree() { + List> children = List.of(Tree.of(name)); + if (typeName != null) + children = children.append(Tree.of(typeName)); + children = children.append(init.toTree()); + return Tree.of("DecVar", children); + } + + @Override + public void semantic(Env env) { + Type t_init = init.semantic(env); + Type t_var = t_init; + if (typeName != null) { + Type t_typeName = env.tenv.get(typeName); + if (t_typeName == null) + throw SemanticHelper.undefined(loc, "type", typeName); + if (!t_init.is(t_typeName)) + throw SemanticHelper.typeMismatch(init.loc, t_init, t_typeName); + t_var = t_typeName; + } + env.venv.put(name, t_var); + } +} diff --git a/eplan/src/main/java/absyn/Exp.java b/eplan/src/main/java/absyn/Exp.java new file mode 100644 index 0000000..ee335f8 --- /dev/null +++ b/eplan/src/main/java/absyn/Exp.java @@ -0,0 +1,30 @@ +package absyn; + +import env.Env; +import parse.Loc; +import types.Type; + +public abstract class Exp extends AST { + + // Type of the expression, calculated by the semantic analyser + public Type type; + + public Exp(Loc loc) { + super(loc); + } + + // Obtain the type of the expression as a string prefixed by the given text. + protected String annotateType(String text) { + final String theType = type == null ? "" : "\n<" + type + ">"; + return text + theType; + } + + // Do semantic analysis of the expression + public Type semantic(Env env) { + type = semantic_(env); + return type; + } + + // Type check the expression. Should be defined in the concrete subclasses. + protected abstract Type semantic_(Env env); +} diff --git a/eplan/src/main/java/absyn/ExpBinOp.java b/eplan/src/main/java/absyn/ExpBinOp.java new file mode 100644 index 0000000..dce0d7d --- /dev/null +++ b/eplan/src/main/java/absyn/ExpBinOp.java @@ -0,0 +1,74 @@ +package absyn; + +import env.Env; +import error.ErrorHelper; +import javaslang.collection.Tree; +import parse.Loc; +import types.BOOL; +import types.INT; +import types.REAL; +import types.Type; + +import static error.ErrorHelper.fatal; +import static semantic.SemanticHelper.*; + +public class ExpBinOp extends Exp { + + public enum Op { + PLUS, MINUS, TIMES, DIV, + AND, OR + } + + public final Op op; + public final Exp left; + public final Exp right; + + public ExpBinOp(Loc loc, Op op, Exp left, Exp right) { + super(loc); + this.op = op; + this.left = left; + this.right = right; + } + + @Override + public Tree.Node toTree() { + return Tree.of(annotateType("ExpBinOp: " + op), left.toTree(), right.toTree()); + } + + @Override + protected Type semantic_(Env env) { + final Type t_left = left.semantic(env); + final Type t_right = right.semantic(env); + + switch (op) { + case PLUS: + case MINUS: + case TIMES: + case DIV: + if (!t_left.is(INT.T, REAL.T)) + throw typeMismatch(left.loc, t_left, INT.T, REAL.T); + + if (!t_right.is(INT.T, REAL.T)) + throw typeMismatch(right.loc, t_right, INT.T, REAL.T); + + if (t_left.is(REAL.T) || t_right.is(REAL.T)) + return REAL.T; + + return INT.T; + + case AND: + case OR: + if (!t_left.is(BOOL.T)) + throw typeMismatch(left.loc, t_left, BOOL.T); + + if (!t_right.is(BOOL.T)) + throw typeMismatch(right.loc, t_right, BOOL.T); + + return BOOL.T; + + default: + throw fatal("unexpected invalid operator: %s", op); + } + } + +} diff --git a/eplan/src/main/java/absyn/ExpBool.java b/eplan/src/main/java/absyn/ExpBool.java new file mode 100644 index 0000000..68d77bd --- /dev/null +++ b/eplan/src/main/java/absyn/ExpBool.java @@ -0,0 +1,27 @@ +package absyn; + +import env.Env; +import javaslang.collection.Tree; +import parse.Loc; +import types.BOOL; +import types.Type; + +public class ExpBool extends Exp { + + public final String value; + + public ExpBool(Loc loc, String value) { + super(loc); + this.value = value; + } + + @Override + public Tree.Node toTree() { + return Tree.of(annotateType("ExpBool: " + value)); + } + + @Override + protected Type semantic_(Env env) { + return BOOL.T; + } +} diff --git a/eplan/src/main/java/absyn/ExpCall.java b/eplan/src/main/java/absyn/ExpCall.java new file mode 100644 index 0000000..eaf1049 --- /dev/null +++ b/eplan/src/main/java/absyn/ExpCall.java @@ -0,0 +1,73 @@ +package absyn; + +import env.Env; +import javaslang.collection.List; +import javaslang.collection.Tree; +import parse.Loc; +import types.FUNCTION; +import types.Type; + +import static semantic.SemanticHelper.*; + + +public class ExpCall extends Exp { + + public final String function; + public final List args; + + public ExpCall(Loc loc, String function, List args) { + super(loc); + this.function = function; + this.args = args; + } + + @Override + public Tree.Node toTree() { + return Tree.of(annotateType("ExpCall"), + Tree.of(function.toString()), + Tree.of("args", + args.map(Exp::toTree))); + } + + @Override + protected Type semantic_(Env env) { + // analyse all arguments + List t_args = args.map(exp -> exp.semantic(env)); + // search the function name in the environment + Type entry = env.venv.get(function); + // check whether it was found and report error if not found + if (entry == null) + throw undefined(loc, "function", function); + // check whether it really names a function + if (!(entry instanceof FUNCTION)) + throw notAFunction(loc, function); + // it is a function, so get a more specific version of the entry + FUNCTION signature = (FUNCTION) entry; + // check the arguments: walk the list of parameters and the list of + // arguments in parallel, checking each argument + // it finishes when the end of any of the lists are reached + List parameters = signature.formals; + List arguments = args; + List t_arguments = t_args; + while (parameters.nonEmpty() && arguments.nonEmpty()) { + Exp arg = arguments.head(); + Type t_arg = t_arguments.head(); + Type t_par = parameters.head(); + // are the argument of the expected type? + if (!t_arg.is(t_par)) + throw typeMismatch(arg.loc, t_arg, t_par); + // advances to the next argument + parameters = parameters.tail(); + arguments = arguments.tail(); + t_arguments = t_arguments.tail(); + } + // at the end there may be more arguments... + if (arguments.nonEmpty()) + throw tooMuchArguments(loc, function); + // ... or more parameters + if (parameters.nonEmpty()) + throw tooFewArguments(loc, function); + return signature.result; + } + +} diff --git a/eplan/src/main/java/absyn/ExpInt.java b/eplan/src/main/java/absyn/ExpInt.java new file mode 100644 index 0000000..24d6b77 --- /dev/null +++ b/eplan/src/main/java/absyn/ExpInt.java @@ -0,0 +1,27 @@ +package absyn; + +import env.Env; +import javaslang.collection.Tree; +import parse.Loc; +import types.INT; +import types.Type; + +public class ExpInt extends Exp { + + public final String value; + + public ExpInt(Loc loc, String value) { + super(loc); + this.value = value; + } + + @Override + public Tree.Node toTree() { + return Tree.of(annotateType("ExpInt: " + value)); + } + + @Override + protected Type semantic_(Env env) { + return INT.T; + } +} diff --git a/eplan/src/main/java/absyn/ExpLet.java b/eplan/src/main/java/absyn/ExpLet.java new file mode 100644 index 0000000..d7f0a66 --- /dev/null +++ b/eplan/src/main/java/absyn/ExpLet.java @@ -0,0 +1,38 @@ +package absyn; + +import env.Env; +import javaslang.collection.List; +import javaslang.collection.Tree; +import parse.Loc; +import types.Type; + +public class ExpLet extends Exp { + + public final List decs; + public final Exp body; + + public ExpLet(Loc loc, List decs, Exp body) { + super(loc); + this.decs = decs; + this.body = body; + } + + @Override + public Tree.Node toTree() { + return Tree.of(annotateType("ExpLet"), + Tree.of("Decs", + decs.map(Dec::toTree)), + body.toTree()); + } + + @Override + protected Type semantic_(Env env) { + env.tenv.beginScope();; + env.venv.beginScope(); + decs.forEach(d -> d.semantic(env)); + Type t_body = body.semantic(env); + env.tenv.endScope(); + env.venv.endScope(); + return t_body; + } +} diff --git a/eplan/src/main/java/absyn/ExpNegate.java b/eplan/src/main/java/absyn/ExpNegate.java new file mode 100644 index 0000000..c6bd4dc --- /dev/null +++ b/eplan/src/main/java/absyn/ExpNegate.java @@ -0,0 +1,37 @@ +package absyn; + +import env.Env; +import javaslang.collection.Tree; +import parse.Loc; +import types.INT; +import types.REAL; +import types.Type; + +import static error.ErrorHelper.*; +import static semantic.SemanticHelper.*; + +public class ExpNegate extends Exp { + + public final Exp arg; + + public ExpNegate(Loc loc, Exp arg) { + super(loc); + this.arg = arg; + } + + @Override + public Tree.Node toTree() { + return Tree.of(annotateType("ExpNegate"), arg.toTree()); + } + + @Override + protected Type semantic_(Env env) { + final Type t_arg = arg.semantic(env); + + if (t_arg instanceof INT || t_arg instanceof REAL) + return t_arg; + + throw typeMismatch(arg.loc, t_arg, INT.T, REAL.T); + } + +} diff --git a/eplan/src/main/java/absyn/ExpReal.java b/eplan/src/main/java/absyn/ExpReal.java new file mode 100644 index 0000000..14e500f --- /dev/null +++ b/eplan/src/main/java/absyn/ExpReal.java @@ -0,0 +1,27 @@ +package absyn; + +import env.Env; +import javaslang.collection.Tree; +import parse.Loc; +import types.REAL; +import types.Type; + +public class ExpReal extends Exp { + + public final String value; + + public ExpReal(Loc loc, String value) { + super(loc); + this.value = value; + } + + @Override + public Tree.Node toTree() { + return Tree.of(annotateType("ExpReal: " + value)); + } + + @Override + protected Type semantic_(Env env) { + return REAL.T; + } +} diff --git a/eplan/src/main/java/absyn/ExpSeq.java b/eplan/src/main/java/absyn/ExpSeq.java new file mode 100644 index 0000000..0562914 --- /dev/null +++ b/eplan/src/main/java/absyn/ExpSeq.java @@ -0,0 +1,33 @@ +package absyn; + +import env.Env; +import javaslang.collection.List; +import javaslang.collection.Tree; +import parse.Loc; +import types.Type; +import types.UNIT; + +public class ExpSeq extends Exp { + + public final List exps; + + public ExpSeq(Loc loc, List exps) { + super(loc); + this.exps = exps; + } + + @Override + public Tree.Node toTree() { + return Tree.of(annotateType("ExpSeq"), + exps.map(Exp::toTree)); + } + + @Override + protected Type semantic_(Env env) { + Type t = UNIT.T; + for (Exp e : exps) + t = e.semantic_(env); + return t; + } + +} diff --git a/eplan/src/main/java/absyn/ExpVar.java b/eplan/src/main/java/absyn/ExpVar.java new file mode 100644 index 0000000..b445549 --- /dev/null +++ b/eplan/src/main/java/absyn/ExpVar.java @@ -0,0 +1,28 @@ +package absyn; + +import env.Env; +import javaslang.collection.Tree; +import parse.Loc; +import types.INT; +import types.Type; + +public class ExpVar extends Exp { + + public final Var variable; + + public ExpVar(Loc loc, Var variable) { + super(loc); + this.variable = variable; + } + + @Override + public Tree.Node toTree() { + return Tree.of(annotateType("ExpVar"), + variable.toTree()); + } + + @Override + protected Type semantic_(Env env) { + return variable.semantic(env); + } +} diff --git a/eplan/src/main/java/absyn/Var.java b/eplan/src/main/java/absyn/Var.java new file mode 100644 index 0000000..9dd9988 --- /dev/null +++ b/eplan/src/main/java/absyn/Var.java @@ -0,0 +1,30 @@ +package absyn; + +import env.Env; +import parse.Loc; +import types.Type; + +public abstract class Var extends AST { + + // Type of the expression, calculated by the semantic analyser + public Type type; + + public Var(Loc loc) { + super(loc); + } + + // Obtain the type of the expression as a string prefixed by the given text. + protected String annotateType(String text) { + final String theType = type == null ? "" : "\n<" + type + ">"; + return text + theType; + } + + // Do semantic analysis of the expression + public Type semantic(Env env) { + type = semantic_(env); + return type; + } + + // Type check the expression. Should be defined in the concrete subclasses. + protected abstract Type semantic_(Env env); +} diff --git a/eplan/src/main/java/absyn/VarSimple.java b/eplan/src/main/java/absyn/VarSimple.java new file mode 100644 index 0000000..3bd4efc --- /dev/null +++ b/eplan/src/main/java/absyn/VarSimple.java @@ -0,0 +1,32 @@ +package absyn; + +import env.Env; +import javaslang.collection.Tree; +import parse.Loc; +import semantic.SemanticHelper; +import types.INT; +import types.Type; + +public class VarSimple extends Var { + + public final String name; + + public VarSimple(Loc loc, String name) { + super(loc); + this.name = name; + } + + @Override + public Tree.Node toTree() { + return Tree.of(annotateType("VarSimple: " + name)); + } + + @Override + protected Type semantic_(Env env) { + Type t = env.venv.get(name); + if (t == null) + throw SemanticHelper.undefined(loc, "variable", name); + return t; + } + +} diff --git a/eplan/src/main/java/env/Env.java b/eplan/src/main/java/env/Env.java new file mode 100644 index 0000000..dcb3547 --- /dev/null +++ b/eplan/src/main/java/env/Env.java @@ -0,0 +1,41 @@ +package env; + +import types.*; + +public class Env { + + public Table tenv; + public Table venv; + + public Env() { + tenv = new Table(); + put(tenv, "unit", UNIT.T); + put(tenv, "int", INT.T); + put(tenv, "real", REAL.T); + put(tenv, "bool", BOOL.T); + + venv = new Table(); + put(venv, "print_int", new FUNCTION(UNIT.T, INT.T)); + put(venv, "print_real", new FUNCTION(UNIT.T, REAL.T)); + put(venv, "print_unit", new FUNCTION(UNIT.T, UNIT.T)); + put(venv, "print_bool", new FUNCTION(UNIT.T, BOOL.T)); + put(venv, "round", new FUNCTION(INT.T, REAL.T)); + put(venv, "ceil", new FUNCTION(INT.T, REAL.T)); + put(venv, "floor", new FUNCTION(INT.T, REAL.T)); + put(venv, "real", new FUNCTION(REAL.T, INT.T)); + put(venv, "not", new FUNCTION(BOOL.T, BOOL.T)); + } + + @Override + public String toString() { + return "Env{" + + "tenv=" + tenv + + ", venv=" + venv + + '}'; + } + + private static void put(Table table, String name, E value) { + table.put(name.intern(), value); + } + +} diff --git a/eplan/src/main/java/env/Table.java b/eplan/src/main/java/env/Table.java new file mode 100644 index 0000000..ec2e0b0 --- /dev/null +++ b/eplan/src/main/java/env/Table.java @@ -0,0 +1,83 @@ +package env; + +import java.util.IdentityHashMap; +import java.util.Map; +import java.util.Set; + +class Binder { + V value; + String prevtop; + Binder tail; + + Binder(V v, String p, Binder t) { + value = v; + prevtop = p; + tail = t; + } +} + +/** + * The Table class is similar to java.util.Dictionary, except that each key must + * be a symbol and there is a scope mechanism. + */ + +public class Table { + private Map> dict; + private String top; + private Binder marks; + + public Table() { + dict = new IdentityHashMap>(); + } + + /** + * Gets the object associated with the specified symbol in the Table. + */ + public V get(String key) { + Binder e = dict.get(key); + if (e == null) + return null; + else + return e.value; + } + + /** + * Puts the specified value into the Table, bound to the specified symbol. + */ + public void put(String key, V value) { + dict.put(key, new Binder(value, top, dict.get(key))); + top = key; + } + + /** + * Remembers the current state of the Table. + */ + public void beginScope() { + marks = new Binder(null, top, marks); + top = null; + } + + /** + * Restores the table to what it was at the most recent beginScope that has + * not already been ended. + */ + public void endScope() { + while (top != null) { + Binder e = dict.get(top); + if (e.tail != null) + dict.put(top, e.tail); + else + dict.remove(top); + top = e.prevtop; + } + top = marks.prevtop; + marks = marks.tail; + } + + /** + * Returns a Set view of the Table's symbols. + */ + public Set keySet() { + return dict.keySet(); + } +} diff --git a/eplan/src/main/java/error/CompilerError.java b/eplan/src/main/java/error/CompilerError.java new file mode 100644 index 0000000..3b20a1a --- /dev/null +++ b/eplan/src/main/java/error/CompilerError.java @@ -0,0 +1,18 @@ +package error; + +import parse.Loc; + +public class CompilerError extends RuntimeException { + + public CompilerError(String message) { + super(message); + } + + public CompilerError(String format, Object... args) { + this(String.format(format, args)); + } + + public CompilerError(Loc loc, String format, Object... args) { + this(loc + " " + String.format(format, args)); + } +} diff --git a/eplan/src/main/java/error/ErrorHelper.java b/eplan/src/main/java/error/ErrorHelper.java new file mode 100644 index 0000000..6bd33c2 --- /dev/null +++ b/eplan/src/main/java/error/ErrorHelper.java @@ -0,0 +1,23 @@ +package error; + +import parse.Loc; + +public interface ErrorHelper { + + static CompilerError error(String message) { + return new CompilerError(message); + } + + static CompilerError error(String format, Object... args) { + return error(String.format(format, args)); + } + + static CompilerError error(Loc loc, String format, Object... args) { + return error(loc + " " + String.format(format, args)); + } + + static FatalError fatal(String format, Object... args) { + return new FatalError(format, args); + } + +} diff --git a/eplan/src/main/java/error/FatalError.java b/eplan/src/main/java/error/FatalError.java new file mode 100644 index 0000000..84485d4 --- /dev/null +++ b/eplan/src/main/java/error/FatalError.java @@ -0,0 +1,13 @@ +package error; + +public class FatalError extends RuntimeException { + + public FatalError(String message) { + super("fatal: " + message); + } + + public FatalError(String format, Object... args) { + this(String.format(format, args)); + } + +} diff --git a/eplan/src/main/java/main/Driver.java b/eplan/src/main/java/main/Driver.java new file mode 100644 index 0000000..57edd8b --- /dev/null +++ b/eplan/src/main/java/main/Driver.java @@ -0,0 +1,189 @@ +package main; + +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.ArrayList; +import java.util.List; + +import absyn.AST; +import absyn.Exp; +import com.beust.jcommander.JCommander; +import com.beust.jcommander.Parameter; +import com.beust.jcommander.ParameterException; +import error.CompilerError; +import java_cup.runtime.Symbol; +import javaslang.render.dot.DotFile; +import javaslang.render.text.Boxes; +import javaslang.render.text.PrettyPrinter; +import parse.SymbolConstants; +import parse.Lexer; +import parse.Parser; + +import static error.ErrorHelper.fatal; + +// command line options +class DriverOptions { + @Parameter(description = "") + public List parameters = new ArrayList<>(); + + @Parameter(names = {"--help", "-h"}, description = "Usage help", help = true) + public boolean help = false; + + @Parameter(names = {"--lexer"}, description = "Lexical analysis") + public boolean lexer = false; + + @Parameter(names = {"--parser"}, description = "Syntax analysis") + public boolean parser = true; + + @Parameter(names = {"--pp-ast"}, description = "Pretty print syntax tree") + public boolean pp_ast = false; + + @Parameter(names = {"--pp-anotated-ast"}, description = "Pretty print annotated syntax tree") + public boolean pp_annotated_ast = false; + + @Parameter(names = {"--box-ast"}, description = "Boxed syntax tree") + public boolean box_ast = false; + + @Parameter(names = {"--box-annotated-ast"}, description = "Boxed annotated syntax tree") + public boolean box_annotated_ast = true; + + @Parameter(names = {"--dot-ast"}, description = "Generate dot file of syntax tree") + public boolean dot_ast = false; + + @Parameter(names = {"--dot-annotated-ast"}, description = "Generate dot file of annotated syntax tree") + public boolean dot_annotted_ast = true; +} + +// main +public class Driver { + + public static void main(String[] args) { + // parse command line options + final DriverOptions options = new DriverOptions(); + final JCommander jCommander = new JCommander(options); + jCommander.setProgramName("Driver"); + + try { + jCommander.parse(args); + } + catch (ParameterException e) { + System.out.println(e.getMessage()); + jCommander.usage(); + System.exit(1); + } + + if (options.help) { + jCommander.usage(); + return; + } + + Reader input = null; + String name = null; + try { + // set the input (source code) to compile + if (options.parameters.isEmpty()) { + name = "unknown"; + input = new InputStreamReader(System.in); + } + else { + name = options.parameters.get(0); + input = new FileReader(name); + } + + // do only lexical analyses + if (options.lexer) + lexicalAnalysis(name, input); + + // do only lexical analyses + if (options.parser) + syntaxAnalysis(options, name, input); + } + catch (CompilerError e) { + System.out.println(e.getMessage()); + System.exit(3); + } + catch (IOException e) { + System.out.println(e.getMessage()); + System.exit(2); + } + catch (Exception e) { + System.out.println(e.getMessage()); + e.printStackTrace(); + System.exit(3); + } + finally { + // closes the input file + if (input instanceof FileReader) + try { + input.close(); + } + catch (IOException e) { + System.out.println(e.getMessage()); + System.exit(4); + } + } + } + + public static void lexicalAnalysis(String name, Reader input) throws IOException { + final Lexer lexer = new Lexer(input, "unknown"); + Symbol tok; + do { + tok = lexer.next_token(); + System.out.printf("%-55s %-8s %s%n", + tok, + SymbolConstants.terminalNames[tok.sym], + tok.value == null ? "" : tok.value); + } while (tok.sym != SymbolConstants.EOF); + } + + public static void syntaxAnalysis(DriverOptions options, String name, Reader input) throws Exception { + final Lexer lexer = new Lexer(input, "unknown"); + final Parser parser = new Parser(lexer); + final Symbol result = parser.parse(); + //System.out.println(result); + + if (!(result.value instanceof AST)) + throw fatal("internal error: program should be an AST"); + + final AST parseTree = (AST) result.value; + if (options.pp_ast) { + System.out.println("===Abstract syntax tree:==========="); + System.out.println(); + System.out.println(PrettyPrinter.pp(parseTree.toTree())); + System.out.println(); + } + if (options.box_ast) { + System.out.println("===Abstract syntax tree:==========="); + System.out.println(); + System.out.println(Boxes.box(parseTree.toTree())); + System.out.println(); + } + if (options.dot_ast) { + DotFile.write(parseTree.toTree(), name + ".dot"); + } + + if (!(parseTree instanceof Exp)) + throw fatal("internal error: program should be an expression"); + + final Exp main = (Exp) parseTree; + main.semantic(new env.Env()); + if (options.pp_annotated_ast) { + System.out.println("===Annotated abstract syntax tree:==========="); + System.out.println(); + System.out.println(PrettyPrinter.pp(parseTree.toTree())); + System.out.println(); + } + if (options.box_annotated_ast) { + System.out.println("===Annotated abstract syntax tree:==========="); + System.out.println(); + System.out.println(Boxes.box(parseTree.toTree())); + System.out.println(); + } + if (options.dot_annotted_ast) { + DotFile.write(parseTree.toTree(), name + ".annotated.dot"); + } + } + +} diff --git a/eplan/src/main/java/main/SemantGui.java b/eplan/src/main/java/main/SemantGui.java new file mode 100644 index 0000000..0962e84 --- /dev/null +++ b/eplan/src/main/java/main/SemantGui.java @@ -0,0 +1,278 @@ +package main; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Container; +import java.awt.Font; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.GridLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.StringReader; +import javax.swing.*; +import javax.swing.tree.DefaultTreeModel; + +import error.CompilerError; +import java_cup.runtime.Symbol; +import javaslang.render.dot.DotFile; +import parse.Lexer; +import parse.Parser; +import absyn.Exp; + +@SuppressWarnings("serial") +public class SemantGui extends JFrame implements ActionListener { + private GridBagLayout layout = new GridBagLayout(); + private GridBagConstraints constraints = new GridBagConstraints(); + + private JButton openButton; + private JTextField fileField; + private JFileChooser chooser; + private JTextArea programArea; + private JTextArea logArea; + private JButton saveButton; + private JButton compileButton; + private JButton asyButton; + private JButton quitButton; + private JTree tree; + private JDialog dialog; + private JButton dialogClose; + + private Exp exp; + + public SemantGui() { + super("Panda Compiler"); + setLayout(new BorderLayout()); + + JToolBar toolBar = new JToolBar(); + toolBar.setFloatable(false); + toolBar.setRollover(true); + add(toolBar, BorderLayout.PAGE_START); + + JPanel panel_ = new JPanel(); + panel_.setLayout(new BoxLayout(panel_, BoxLayout.PAGE_AXIS)); + add(panel_, BorderLayout.CENTER); + + JLabel label = new JLabel("Source File:"); + label.setDisplayedMnemonic('F'); + toolBar.add(label); + + fileField = new JTextField(25); + label.setLabelFor(fileField); + fileField.addActionListener(this); + toolBar.add(fileField); + + openButton = new JButton("Browse"); + openButton.setMnemonic('B'); + openButton.addActionListener(this); + toolBar.add(openButton); + + toolBar.addSeparator(); + + saveButton = new JButton("Save"); + saveButton.setMnemonic('S'); + saveButton.addActionListener(this); + toolBar.add(saveButton); + + compileButton = new JButton("Compile"); + compileButton.setMnemonic('C'); + compileButton.addActionListener(this); + toolBar.add(compileButton); + + asyButton = new JButton("Show Tree"); + asyButton.setMnemonic('T'); + asyButton.addActionListener(this); + toolBar.add(asyButton); + + toolBar.addSeparator(); + + quitButton = new JButton("Quit"); + quitButton.setMnemonic('Q'); + quitButton.addActionListener(this); + toolBar.add(quitButton); + + chooser = new JFileChooser(); + + label = new JLabel("Program Source Code"); + label.setDisplayedMnemonic('P'); + panel_.add(label); + programArea = new JTextArea(12, 80); + programArea.setFont(new Font("Courier", Font.BOLD, 22)); + JScrollPane scrollPane = new JScrollPane(programArea); + label.setLabelFor(scrollPane); + panel_.add(scrollPane); + + label = new JLabel("Compiler Messages"); + label.setDisplayedMnemonicIndex(9); + panel_.add(label); + logArea = new JTextArea(5, 80); + scrollPane = new JScrollPane(logArea); + label.setLabelFor(scrollPane); + logArea.setFont(new Font("Courier", Font.BOLD, 22)); + panel_.add(scrollPane); + + JPanel panel = new JPanel(new GridLayout(1, 4)); + panel_.add(panel); + + label = new JLabel("Abstract Syntax Tree"); + label.setDisplayedMnemonicIndex(0); + panel_.add(label); + tree = new JTree(new DefaultTreeModel(null)); + tree.setFont(new Font("Courier", Font.BOLD, 22)); + scrollPane = new JScrollPane(tree); + label.setLabelFor(scrollPane); + panel_.add(scrollPane); + + pack(); + } + + private void addComponent(Container container, Component component, int row, int column, int width, int height, + int fill) { + constraints.gridx = column; + constraints.gridy = row; + constraints.gridwidth = width; + constraints.gridheight = height; + constraints.fill = fill; + constraints.weightx = 1; + constraints.weighty = 1; + constraints.insets = new Insets(2, 2, 2, 2); + layout.setConstraints(component, constraints); + container.add(component); + } + + public static void main(String args[]) throws Exception { + SemantGui application = new SemantGui(); + application.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + application.setVisible(true); + } + + private void readSource() { + try { + FileReader file = new FileReader(fileField.getText()); + StringBuilder str = new StringBuilder(); + while (true) { + int ch = file.read(); + if (ch == -1) + break; + str.append((char) ch); + } + file.close(); + programArea.setText(str.toString()); + } + catch (FileNotFoundException e1) { + JOptionPane.showMessageDialog(SemantGui.this, "File not found:\n" + fileField.getText(), + "CompError reading file", JOptionPane.ERROR_MESSAGE); + } + catch (IOException e2) { + JOptionPane.showMessageDialog(SemantGui.this, "CompError reading file:\n" + fileField.getText(), + "CompError reading file", JOptionPane.ERROR_MESSAGE); + } + } + + public void actionPerformed(ActionEvent e) { + if (e.getSource() == openButton) { + int returnVal = chooser.showOpenDialog(SemantGui.this); + if (returnVal == JFileChooser.APPROVE_OPTION) + fileField.setText(chooser.getSelectedFile().getPath()); + readSource(); + } + if (e.getSource() == saveButton) { + String filePath = fileField.getText().trim(); + if (!filePath.isEmpty()) + chooser.setSelectedFile(new File(filePath)); + int returnVal = chooser.showSaveDialog(SemantGui.this); + if (returnVal == JFileChooser.APPROVE_OPTION) { + filePath = chooser.getSelectedFile().getPath(); + try { + FileWriter file = new FileWriter(filePath); + file.write(programArea.getText()); + file.close(); + fileField.setText(filePath); + } + catch (IOException e1) { + JOptionPane.showMessageDialog(SemantGui.this, + "CompError writing file:\n" + filePath, + "CompError writing file", + JOptionPane.ERROR_MESSAGE); + } + } + } + else if (e.getSource() == fileField) + readSource(); + else if (e.getSource() == compileButton) + compileProgram(); + else if (e.getSource() == asyButton) + asyGraph(); + else if (e.getSource() == dialogClose) + dialog.dispose(); + else if (e.getSource() == quitButton) + dispose(); + } + + private void compileProgram() { + StringReader file = new StringReader(programArea.getText()); + Lexer scanner = new Lexer(file, fileField.getText()); + Parser parser = new Parser(scanner, scanner.getSymbolFactory()); + try { + logArea.setText(""); + Symbol prog = parser.parse(); + exp = (Exp) prog.value; + DefaultTreeModel treeModel = new DefaultTreeModel(javaslang.render.swing.Tree.create(exp.toTree())); + tree.setModel(treeModel); + for (int row = 0; row < tree.getRowCount(); row++) + tree.expandRow(row); + types.Type et = exp.semantic(new env.Env()); + logArea.append("===> " + et.toString() + "\n"); + } + catch (CompilerError e) { + logArea.append(e.getMessage()); + } + catch (Exception e) { + JOptionPane.showMessageDialog(SemantGui.this, + "Error compiling program\n", + "Internal error compiling program\n" + + e.getStackTrace(), + JOptionPane.ERROR_MESSAGE); + } + + } + + private void asyGraph() { + if (exp != null) + try { + String imageType = "png"; + File tempfile = File.createTempFile("parsetree-", ".asy"); + tempfile.deleteOnExit(); + DotFile.write(exp.toTree(), tempfile); + File tempfile2 = File.createTempFile("parsetree-", "." + imageType); + tempfile2.deleteOnExit(); + String cmd[] = {"dot", "-Tpng", "-o", tempfile2.getPath(), tempfile.getPath()}; + Process proc = Runtime.getRuntime().exec(cmd, null, tempfile.getParentFile()); + proc.waitFor(); + dialog = new JDialog(SemantGui.this, "parse Tree"); + dialog.setLayout(layout); + Icon icon = new ImageIcon(tempfile2.getPath()); + JLabel label = new JLabel(icon); + JScrollPane scrollPane = new JScrollPane(label); + addComponent(dialog, scrollPane, 0, 0, 1, 1, GridBagConstraints.BOTH); + dialogClose = new JButton("Close"); + dialogClose.addActionListener(this); + addComponent(dialog, dialogClose, 1, 0, 1, 1, GridBagConstraints.NONE); + dialog.pack(); + dialog.setVisible(true); + } + catch (Exception e) { + JOptionPane.showMessageDialog(SemantGui.this, + "Error creating asy graph\n", + "Cannot create asy graph\n" + + e.getStackTrace(), + JOptionPane.ERROR_MESSAGE); + } + } +} diff --git a/eplan/src/main/java/parse/Loc.java b/eplan/src/main/java/parse/Loc.java new file mode 100644 index 0000000..933bacd --- /dev/null +++ b/eplan/src/main/java/parse/Loc.java @@ -0,0 +1,64 @@ +package parse; + +import java_cup.runtime.ComplexSymbolFactory.Location; +import java_cup.runtime.ComplexSymbolFactory.ComplexSymbol; +import java_cup.runtime.Symbol; + +public class Loc { + + public final Location left; + public final Location right; + + private Loc(Location left, Location right) { + this.left = left; + this.right = right; + } + + public static Loc loc() { + return loc(new Location(-1, -1)); + } + + public static Loc loc(Location left) { + return loc(left, left); + } + + public static Loc loc(Location left, Location right) { + return new Loc(left, right); + } + + public static Loc loc(Symbol symbol) { + return loc(new Location(symbol.left, symbol.right)); + } + + public static Loc loc(Symbol a, Symbol b) { + return loc(new Location(a.left, b.right)); + } + + public static Loc loc(ComplexSymbol symbol) { + return new Loc(symbol.getLeft(), symbol.getRight()); + } + + public static Loc loc(ComplexSymbol a, ComplexSymbol b) { + return new Loc(a.getLeft(), b.getRight()); + } + + @Override + public String toString() { + if (left.getUnit().equals("unknown") && right.getUnit().equals("unknown")) + return String.format("%d/%d-%d/%d", + left.getLine(), left.getColumn(), + right.getLine(), right.getColumn()); + else if (left.getUnit().equals(right.getUnit())) + return String.format("%s:%d/%d-%d/%d", + left.getUnit(), + left.getLine(), left.getColumn(), + right.getLine(), right.getColumn()); + else + return String.format("%s:%d/%d-%s:%d/%d", + left.getUnit(), + left.getLine(), left.getColumn(), + right.getUnit(), + right.getLine(), right.getColumn()); + } + +} diff --git a/eplan/src/main/java/semantic/SemanticHelper.java b/eplan/src/main/java/semantic/SemanticHelper.java new file mode 100644 index 0000000..7009f97 --- /dev/null +++ b/eplan/src/main/java/semantic/SemanticHelper.java @@ -0,0 +1,40 @@ +package semantic; + +import error.CompilerError; + +import parse.Loc; +import types.Type; + +public interface SemanticHelper { + + static CompilerError typeMismatch(Loc loc, Type found, Type... expected) { + final StringBuilder builder = new StringBuilder(); + final int n = expected.length; + if (n > 0) { + builder.append(expected[0]); + if (n > 1) { + for (int i = 1; i < n - 2; i++) + builder.append(", ").append(expected[i]); + builder.append(" or ").append(expected[n - 1]); + } + } + return new CompilerError(loc, "type mismatch: found %s but expected %s", found, builder); + } + + static CompilerError undefined(Loc loc, String category, String name) { + return new CompilerError(loc, "undefined %s '%s'", category, name); + } + + static CompilerError notAFunction(Loc loc, String name) { + return new CompilerError(loc, "'%s' is not a function", name); + } + + static CompilerError tooFewArguments(Loc loc, String name) { + return new CompilerError(loc, "too few arguments in call to '%s'", name); + } + + static CompilerError tooMuchArguments(Loc loc, String name) { + return new CompilerError(loc, "too much arguments in call to '%s'", name); + } + +} diff --git a/eplan/src/main/java/types/BOOL.java b/eplan/src/main/java/types/BOOL.java new file mode 100644 index 0000000..cb848a2 --- /dev/null +++ b/eplan/src/main/java/types/BOOL.java @@ -0,0 +1,14 @@ +package types; + +public class BOOL extends Type { + + public static final BOOL T = new BOOL(); + + private BOOL() { + } + + @Override + public String toString() { + return "bool"; + } +} diff --git a/eplan/src/main/java/types/FUNCTION.java b/eplan/src/main/java/types/FUNCTION.java new file mode 100644 index 0000000..4324891 --- /dev/null +++ b/eplan/src/main/java/types/FUNCTION.java @@ -0,0 +1,29 @@ +package types; + +import javaslang.collection.List; + +public class FUNCTION extends Type { + + public Type result; + public List formals; + + public FUNCTION(Type result, List formals) { + this.result = result; + this.formals = formals; + } + + public FUNCTION(Type result, Type... formals) { + this(result, List.of(formals)); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("("); + formals.map(Type::toString).intersperse(","); + builder.append("->"); + builder.append(result.toString()); + return builder.toString(); + } + +} diff --git a/eplan/src/main/java/types/INT.java b/eplan/src/main/java/types/INT.java new file mode 100644 index 0000000..af4cf00 --- /dev/null +++ b/eplan/src/main/java/types/INT.java @@ -0,0 +1,14 @@ +package types; + +public class INT extends Type { + + public static final INT T = new INT(); + + private INT() { + } + + @Override + public String toString() { + return "int"; + } +} diff --git a/eplan/src/main/java/types/REAL.java b/eplan/src/main/java/types/REAL.java new file mode 100644 index 0000000..e4a7188 --- /dev/null +++ b/eplan/src/main/java/types/REAL.java @@ -0,0 +1,14 @@ +package types; + +public class REAL extends Type { + + public static final REAL T = new REAL(); + + private REAL() { + } + + @Override + public String toString() { + return "real"; + } +} diff --git a/eplan/src/main/java/types/Type.java b/eplan/src/main/java/types/Type.java new file mode 100644 index 0000000..0abb24f --- /dev/null +++ b/eplan/src/main/java/types/Type.java @@ -0,0 +1,33 @@ +package types; + +import javaslang.collection.List; +import javaslang.collection.Tree; +import javaslang.collection.Tree.Node; +import javaslang.render.ToTree; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +public abstract class Type implements ToTree { + + @Override + public Node toTree() { + return Tree.of(toString()); + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); + } + + // Verify if this type can be coerced to the given type. In + // general one type can be coerced to another type if and only if the + // types are the same. + public boolean is(Type type) { + return type == this; + } + + // Verify if this type can be coerced to any of the given types. + public boolean is(Type... types) { + return List.of(types).exists(t -> this.is(t)); + } +} diff --git a/eplan/src/main/java/types/UNIT.java b/eplan/src/main/java/types/UNIT.java new file mode 100644 index 0000000..8201ed9 --- /dev/null +++ b/eplan/src/main/java/types/UNIT.java @@ -0,0 +1,15 @@ +package types; + +public class UNIT extends Type { + + public static final UNIT T = new UNIT(); + + private UNIT() { + } + + @Override + public String toString() { + return "unit"; + } + +} diff --git a/eplan/src/main/jflex/lexer.jflex b/eplan/src/main/jflex/lexer.jflex new file mode 100644 index 0000000..0e40df5 --- /dev/null +++ b/eplan/src/main/jflex/lexer.jflex @@ -0,0 +1,99 @@ +package parse; + +import static error.ErrorHelper.error; + +import java_cup.runtime.Symbol; +import java_cup.runtime.SymbolFactory; +import java_cup.runtime.ComplexSymbolFactory.Location; +import java_cup.runtime.ComplexSymbolFactory; + +%% + +%public +%final +%class Lexer +%implements SymbolConstants +%cupsym SymbolConstants +%cup +%line +%column + +%eofval{ + return tok(EOF); +%eofval} + +%ctorarg String unitName + +%init{ + this.unit = unitName; +%init} + +%{ + private String unit; + + private ComplexSymbolFactory complexSymbolFactory = new ComplexSymbolFactory(); + + public SymbolFactory getSymbolFactory() { + return complexSymbolFactory; + } + + // auxiliary methods to construct terminal symbols at current location + + private Location locLeft() { + return new Location(unit, yyline + 1, yycolumn + 1); + } + + private Location locRight() { + return new Location(unit, yyline + 1, yycolumn + 1 + yylength()); + } + + private Symbol tok(int type, String lexeme, Object value) { + return complexSymbolFactory.newSymbol(lexeme, type, locLeft(), locRight(), value); + } + + private Symbol tok(int type, Object value) { + return tok(type, yytext(), value); + } + + private Symbol tok(int type) { + return tok(type, null); + } +%} + +litint = [0-9]+ +litfloat1 = [0-9]+ "." [0-9]* +litfloat2 = [0-9]* "." [0-9]+ +litfloat3 = ({litint} | {litfloat1} | {litfloat2}) [eE] [+-]? {litint} +litreal = {litint} | {litfloat1} | {litfloat2} | {litfloat3} +litbool = true | false + +id = [a-zA-Z][a-zA-Z0-9_]* + +%% + +[ \t\f\n\r]+ { /* skip */ } + +{litint} { return tok(LITINT, yytext()); } +{litreal} { return tok(LITREAL, yytext()); } +{litbool} { return tok(LITBOOL, yytext()); } + +var { return tok(VAR); } +let { return tok(LET); } +in { return tok(IN); } + +{id} { return tok(ID, yytext().intern()); } + +"+" { return tok(PLUS); } +"-" { return tok(MINUS); } +"*" { return tok(TIMES); } +"/" { return tok(DIV); } +"&&" { return tok(AND); } +"||" { return tok(OR); } +"(" { return tok(LPAREN); } +")" { return tok(RPAREN); } +"," { return tok(COMMA); } +";" { return tok(SEMICOLON); } +":" { return tok(COLON); } +"=" { return tok(EQ); } + +. { throw error(Loc.loc(locLeft()), "unexpected char '%s'", yytext()); } diff --git a/eplan/src/test/java/Test/SemantTest.java b/eplan/src/test/java/Test/SemantTest.java new file mode 100644 index 0000000..dd734fe --- /dev/null +++ b/eplan/src/test/java/Test/SemantTest.java @@ -0,0 +1,92 @@ +package Test; + +import absyn.Exp; +import env.Env; +import error.CompilerError; +import java_cup.runtime.Symbol; +import org.assertj.core.api.JUnitSoftAssertions; +import org.junit.Rule; +import org.junit.Test; +import parse.Lexer; +import parse.Parser; +import types.*; + +import java.io.IOException; +import java.io.StringReader; + +public class SemantTest { + + private Type runSemantic(String input) throws Exception { + Lexer lexer = new Lexer(new StringReader(input), "unknown"); + Parser parser = new Parser(lexer); + Symbol program = parser.parse(); + Exp parseTree = (Exp) program.value; + return parseTree.semantic(new Env()); + } + + private void trun(String input, Type type) { + try { + softly.assertThat(runSemantic(input)) + .as("%s", input) + .isEqualTo(type); + } + catch (Exception e) { + e.printStackTrace(); + } + } + + private void erun(String input, String message) throws IOException { + softly.assertThatThrownBy(() -> runSemantic(input)) + .as("%s", input) + .isInstanceOf(CompilerError.class) + .hasToString(message); + } + + @Rule + public final JUnitSoftAssertions softly = new JUnitSoftAssertions(); + + @Test + public void testLiterals() throws Exception { + trun("true", BOOL.T); + trun("123", INT.T); + trun("12.34", REAL.T); + } + + @Test + public void testFunctionCall() throws Exception { + erun("fat(9)", + "error.CompilerError: 1/1-1/7 undefined function 'fat'"); + erun("fat(g(), h())", + "error.CompilerError: 1/5-1/8 undefined function 'g'"); + trun("print_int(123)", + UNIT.T); + erun("print_int(true)", + "error.CompilerError: 1/11-1/15 type mismatch: found bool but expected int"); + erun("print_int(123, true, f())", + "error.CompilerError: 1/22-1/25 undefined function 'f'"); + erun("print_int()", + "error.CompilerError: 1/1-1/12 too few arguments in call to 'print_int'"); + } + + @Test + public void testSequence() throws Exception { + trun("()", UNIT.T); + trun("(true)", BOOL.T); + trun("(print_int(23); 2.3)", REAL.T); + } + + @Test + public void testSimpleVariableAndLet() throws Exception { + erun("x", + "error.CompilerError: 1/1-1/2 undefined variable 'x'"); + trun("let var x: int = 10 in x", + INT.T); + trun("let var x = 0.56 in x", + REAL.T); + erun("let var x: int = 3.4 in x", + "error.CompilerError: 1/18-1/21 type mismatch: found real but expected int"); + erun("(let var x = 5 in print_int(x); x)", + "error.CompilerError: 1/33-1/34 undefined variable 'x'"); + } + +} diff --git a/eplan/tests/test-lexer-01.eplan b/eplan/tests/test-lexer-01.eplan new file mode 100644 index 0000000..03e1a60 --- /dev/null +++ b/eplan/tests/test-lexer-01.eplan @@ -0,0 +1,3 @@ +12 * + (453.2 - 140.300) / + 2 diff --git a/src/main/cup/parser.cup b/src/main/cup/parser.cup index ef83e71..dfc1472 100644 --- a/src/main/cup/parser.cup +++ b/src/main/cup/parser.cup @@ -48,6 +48,8 @@ terminal LPAREN, RPAREN; terminal COMMA, SEMICOLON; terminal VAR, EQ, COLON; terminal LET, IN; +terminal ASSIGN; +terminal IF, THEN, ELSE; non terminal Exp program; non terminal Exp exp; @@ -62,6 +64,7 @@ precedence left AND; precedence left PLUS, MINUS; precedence left TIMES, DIV; precedence left UMINUS; +precedence nonassoc ELSE; start with program; @@ -70,20 +73,23 @@ program ::= ; exp ::= - exp:x PLUS exp:y {: RESULT = new ExpBinOp(loc(xxleft,yxright), ExpBinOp.Op.PLUS, x, y); :} -| exp:x MINUS exp:y {: RESULT = new ExpBinOp(loc(xxleft,yxright), ExpBinOp.Op.MINUS, x, y); :} -| exp:x TIMES exp:y {: RESULT = new ExpBinOp(loc(xxleft,yxright), ExpBinOp.Op.TIMES, x, y); :} -| exp:x DIV exp:y {: RESULT = new ExpBinOp(loc(xxleft,yxright), ExpBinOp.Op.DIV, x, y); :} -| exp:x AND exp:y {: RESULT = new ExpBinOp(loc(xxleft,yxright), ExpBinOp.Op.AND, x, y); :} -| exp:x OR exp:y {: RESULT = new ExpBinOp(loc(xxleft,yxright), ExpBinOp.Op.OR, x, y); :} -| LITINT:x {: RESULT = new ExpInt(loc(xxleft,xxright), x); :} -| LITREAL:x {: RESULT = new ExpReal(loc(xxleft,xxright), x); :} -| LITBOOL:x {: RESULT = new ExpBool(loc(xxleft,xxright), x); :} -| MINUS:m exp:x {: RESULT = new ExpNegate(loc(mxleft,xxright), x); :} %prec UMINUS -| ID:f LPAREN exps:x RPAREN:r {: RESULT = new ExpCall(loc(fxleft,rxright), f, x); :} -| var:v {: RESULT = new ExpVar(loc(vxleft,vxright), v); :} -| LET:l decs:ds IN exp:b {: RESULT = new ExpLet(loc(lxleft,bxright), ds, b); :} -| LPAREN:l expseq:es RPAREN:r {: RESULT = new ExpSeq(loc(lxleft,rxright), es); :} + exp:x PLUS exp:y {: RESULT = new ExpBinOp(loc(xxleft,yxright), ExpBinOp.Op.PLUS, x, y); :} +| exp:x MINUS exp:y {: RESULT = new ExpBinOp(loc(xxleft,yxright), ExpBinOp.Op.MINUS, x, y); :} +| exp:x TIMES exp:y {: RESULT = new ExpBinOp(loc(xxleft,yxright), ExpBinOp.Op.TIMES, x, y); :} +| exp:x DIV exp:y {: RESULT = new ExpBinOp(loc(xxleft,yxright), ExpBinOp.Op.DIV, x, y); :} +| exp:x AND exp:y {: RESULT = new ExpBinOp(loc(xxleft,yxright), ExpBinOp.Op.AND, x, y); :} +| exp:x OR exp:y {: RESULT = new ExpBinOp(loc(xxleft,yxright), ExpBinOp.Op.OR, x, y); :} +| LITINT:x {: RESULT = new ExpInt(loc(xxleft,xxright), x); :} +| LITREAL:x {: RESULT = new ExpReal(loc(xxleft,xxright), x); :} +| LITBOOL:x {: RESULT = new ExpBool(loc(xxleft,xxright), x); :} +| MINUS:m exp:x {: RESULT = new ExpNegate(loc(mxleft,xxright), x); :} %prec UMINUS +| ID:f LPAREN exps:x RPAREN:r {: RESULT = new ExpCall(loc(fxleft,rxright), f, x); :} +| var:v {: RESULT = new ExpVar(loc(vxleft,vxright), v); :} +| var:v ASSIGN exp:e {: RESULT = new ExpAssign(loc(vxleft,exright), v, e); :} +| LET:l decs:ds IN exp:b {: RESULT = new ExpLet(loc(lxleft,bxright), ds, b); :} +| LPAREN:l expseq:es RPAREN:r {: RESULT = new ExpSeq(loc(lxleft,rxright), es); :} +| IF:i exp:t THEN exp:a ELSE exp:b {: RESULT = new ExpIf(loc(ixleft,bxright), t, a, b); :} +| IF:i exp:t THEN exp:a {: RESULT = new ExpIf(loc(ixleft,axright), t, a, null); :} ; exps ::= @@ -118,4 +124,4 @@ decs ::= var ::= ID:v {: RESULT = new VarSimple(loc(vxleft,vxright), v); :} -; +; \ No newline at end of file diff --git a/src/main/java/absyn/DecFunction.java b/src/main/java/absyn/DecFunction.java new file mode 100644 index 0000000..614549e --- /dev/null +++ b/src/main/java/absyn/DecFunction.java @@ -0,0 +1,7 @@ +package absyn; + +/** + * Created by aluno on 3/31/17. + */ +public class DecFunction { +} diff --git a/src/main/java/absyn/DecFunctionMutual.java b/src/main/java/absyn/DecFunctionMutual.java new file mode 100644 index 0000000..4ee6ea5 --- /dev/null +++ b/src/main/java/absyn/DecFunctionMutual.java @@ -0,0 +1,7 @@ +package absyn; + +/** + * Created by aluno on 3/31/17. + */ +public class DecFunctionMutual { +} diff --git a/src/main/java/absyn/DecType.java b/src/main/java/absyn/DecType.java new file mode 100644 index 0000000..646a031 --- /dev/null +++ b/src/main/java/absyn/DecType.java @@ -0,0 +1,7 @@ +package absyn; + +/** + * Created by aluno on 3/15/17. + */ +public class DecType { +} diff --git a/src/main/java/absyn/DecTypeMutual.java b/src/main/java/absyn/DecTypeMutual.java new file mode 100644 index 0000000..52c2378 --- /dev/null +++ b/src/main/java/absyn/DecTypeMutual.java @@ -0,0 +1,7 @@ +package absyn; + +/** + * Created by aluno on 3/20/17. + */ +public class DecTypeMutual { +} diff --git a/src/main/java/absyn/ExpArray.java b/src/main/java/absyn/ExpArray.java new file mode 100644 index 0000000..015dfbc --- /dev/null +++ b/src/main/java/absyn/ExpArray.java @@ -0,0 +1,7 @@ +package absyn; + +/** + * Created by aluno on 3/31/17. + */ +public class ExpArray { +} diff --git a/src/main/java/absyn/ExpAssign.java b/src/main/java/absyn/ExpAssign.java new file mode 100644 index 0000000..25d3260 --- /dev/null +++ b/src/main/java/absyn/ExpAssign.java @@ -0,0 +1,36 @@ +package absyn; + +import env.Env; +import javaslang.collection.Tree; +import parse.Loc; +import types.Type; + +import static semantic.SemanticHelper.typeMismatch; + +public class ExpAssign extends Exp { + + public final Var var; + public final Exp exp; + + public ExpAssign(Loc loc, Var var, Exp exp) { + super(loc); + this.var = var; + this.exp = exp; + } + + @Override + public Tree.Node toTree() { + return Tree.of(annotateType("ExpAssign"), + var.toTree(), + exp.toTree()); + } + + @Override + protected Type semantic_(Env env) { + Type t_var = var.semantic(env); + Type t_exp = exp.semantic(env); + if (!t_exp.is(t_var)) + throw typeMismatch(exp.loc, t_exp, t_var); + return t_var; + } +} \ No newline at end of file diff --git a/src/main/java/absyn/ExpBreak.java b/src/main/java/absyn/ExpBreak.java new file mode 100644 index 0000000..c0ff12b --- /dev/null +++ b/src/main/java/absyn/ExpBreak.java @@ -0,0 +1,7 @@ +package absyn; + +/** + * Created by aluno on 3/31/17. + */ +public class ExpBreak { +} diff --git a/src/main/java/absyn/ExpIf.java b/src/main/java/absyn/ExpIf.java new file mode 100644 index 0000000..2ba6632 --- /dev/null +++ b/src/main/java/absyn/ExpIf.java @@ -0,0 +1,51 @@ +package absyn; + +import env.Env; +import javaslang.collection.List; +import javaslang.collection.Tree; +import parse.Loc; +import types.*; + +import static semantic.SemanticHelper.typeMismatch; + +public class ExpIf extends Exp { + + public final Exp test; + public final Exp alt1; + public final Exp alt2; + + public ExpIf(Loc loc, Exp test, Exp alt1, Exp alt2) { + super(loc); + this.test = test; + this.alt1 = alt1; + this.alt2 = alt2; + } + + @Override + public Tree.Node toTree() { + List> children = List.of(test.toTree(), alt1.toTree()); + if (alt2 != null) + children = children.append(alt2.toTree()); + return Tree.of(annotateType("ExpIf: "), children); + } + + @Override + protected Type semantic_(Env env) { + final Type t_test = test.semantic(env); + if (!t_test.is(BOOL.T)) + throw typeMismatch(test.loc, t_test, BOOL.T); + final Type t_alt1 = alt1.semantic(env); + if (alt2 == null) { + if (!t_alt1.is(UNIT.T)) + throw typeMismatch(alt1.loc, t_alt1, UNIT.T); + return UNIT.T; + } + final Type t_alt2 = alt2.semantic(env); + if (t_alt2.is(t_alt1)) + return t_alt1; + if (t_alt1.is(t_alt2)) + return t_alt2; + throw typeMismatch(alt2.loc, t_alt2, t_alt1); + } + +} \ No newline at end of file diff --git a/src/main/java/absyn/ExpRecord.java b/src/main/java/absyn/ExpRecord.java new file mode 100644 index 0000000..fe53e96 --- /dev/null +++ b/src/main/java/absyn/ExpRecord.java @@ -0,0 +1,7 @@ +package absyn; + +/** + * Created by aluno on 3/31/17. + */ +public class ExpRecord { +} diff --git a/src/main/java/absyn/ExpWhile.java b/src/main/java/absyn/ExpWhile.java new file mode 100644 index 0000000..7ce0b73 --- /dev/null +++ b/src/main/java/absyn/ExpWhile.java @@ -0,0 +1,7 @@ +package absyn; + +/** + * Created by aluno on 3/31/17. + */ +public class ExpWhile { +} diff --git a/src/main/java/absyn/Param.java b/src/main/java/absyn/Param.java new file mode 100644 index 0000000..0db9fc1 --- /dev/null +++ b/src/main/java/absyn/Param.java @@ -0,0 +1,7 @@ +package absyn; + +/** + * Created by aluno on 3/31/17. + */ +public class Param { +} diff --git a/src/main/java/absyn/ParamAssign.java b/src/main/java/absyn/ParamAssign.java new file mode 100644 index 0000000..216cece --- /dev/null +++ b/src/main/java/absyn/ParamAssign.java @@ -0,0 +1,7 @@ +package absyn; + +/** + * Created by aluno on 3/31/17. + */ +public class ParamAssign { +} diff --git a/src/main/java/absyn/Ty.java b/src/main/java/absyn/Ty.java new file mode 100644 index 0000000..0db587b --- /dev/null +++ b/src/main/java/absyn/Ty.java @@ -0,0 +1,7 @@ +package absyn; + +/** + * Created by aluno on 3/15/17. + */ +public class Ty { +} diff --git a/src/main/java/absyn/TyArray.java b/src/main/java/absyn/TyArray.java new file mode 100644 index 0000000..10b3313 --- /dev/null +++ b/src/main/java/absyn/TyArray.java @@ -0,0 +1,7 @@ +package absyn; + +/** + * Created by aluno on 3/31/17. + */ +public class TyArray { +} diff --git a/src/main/java/absyn/TyName.java b/src/main/java/absyn/TyName.java new file mode 100644 index 0000000..106588f --- /dev/null +++ b/src/main/java/absyn/TyName.java @@ -0,0 +1,7 @@ +package absyn; + +/** + * Created by aluno on 3/15/17. + */ +public class TyName { +} diff --git a/src/main/java/absyn/TyRecord.java b/src/main/java/absyn/TyRecord.java new file mode 100644 index 0000000..1ac3af4 --- /dev/null +++ b/src/main/java/absyn/TyRecord.java @@ -0,0 +1,7 @@ +package absyn; + +/** + * Created by aluno on 3/31/17. + */ +public class TyRecord { +} diff --git a/src/main/java/absyn/VarField.java b/src/main/java/absyn/VarField.java new file mode 100644 index 0000000..d2a147d --- /dev/null +++ b/src/main/java/absyn/VarField.java @@ -0,0 +1,7 @@ +package absyn; + +/** + * Created by aluno on 3/31/17. + */ +public class VarField { +} diff --git a/src/main/java/absyn/VarSubscript.java b/src/main/java/absyn/VarSubscript.java new file mode 100644 index 0000000..948712c --- /dev/null +++ b/src/main/java/absyn/VarSubscript.java @@ -0,0 +1,7 @@ +package absyn; + +/** + * Created by aluno on 3/31/17. + */ +public class VarSubscript { +} diff --git a/src/main/java/types/ARRAY.java b/src/main/java/types/ARRAY.java new file mode 100644 index 0000000..61ff7ea --- /dev/null +++ b/src/main/java/types/ARRAY.java @@ -0,0 +1,7 @@ +package types; + +/** + * Created by aluno on 3/31/17. + */ +public class ARRAY { +} diff --git a/src/main/java/types/RECORD.java b/src/main/java/types/RECORD.java new file mode 100644 index 0000000..410d6e6 --- /dev/null +++ b/src/main/java/types/RECORD.java @@ -0,0 +1,7 @@ +package types; + +/** + * Created by aluno on 3/31/17. + */ +public class RECORD { +} diff --git a/src/main/jflex/lexer.jflex b/src/main/jflex/lexer.jflex index 0e40df5..c212f6f 100644 --- a/src/main/jflex/lexer.jflex +++ b/src/main/jflex/lexer.jflex @@ -80,6 +80,9 @@ id = [a-zA-Z][a-zA-Z0-9_]* var { return tok(VAR); } let { return tok(LET); } in { return tok(IN); } +if { return tok(IF); } +then { return tok(THEN); } +else { return tok(ELSE); } {id} { return tok(ID, yytext().intern()); } @@ -95,5 +98,6 @@ in { return tok(IN); } ";" { return tok(SEMICOLON); } ":" { return tok(COLON); } "=" { return tok(EQ); } +":=" { return tok(ASSIGN); } -. { throw error(Loc.loc(locLeft()), "unexpected char '%s'", yytext()); } +. { throw error(Loc.loc(locLeft()), "unexpected char '%s'", yytext()); } \ No newline at end of file