diff --git a/doc/.gitignore b/doc/.gitignore index ed85998..679e4af 100644 --- a/doc/.gitignore +++ b/doc/.gitignore @@ -1,5 +1,6 @@ -/verslag.aux -/verslag.log -/verslag.synctex.gz -/verslag.pdf +/project-root.tex +/report.aux +/report.log +/report.synctex.gz +/report.pdf /javadoc diff --git a/doc/verslag-conclusies.tex b/doc/report-conclusion.tex similarity index 100% rename from doc/verslag-conclusies.tex rename to doc/report-conclusion.tex diff --git a/doc/report-description.tex b/doc/report-description.tex new file mode 100644 index 0000000..64e8eaf --- /dev/null +++ b/doc/report-description.tex @@ -0,0 +1,156 @@ +% Detailed language description. A systematic description of the features of your language, for each +% feature specifying +% – Syntax, including one or more examples; +% – Usage: how should the feature be used? Are there any typing or other restrictions? +% – Semantics: what does the feature do? How will it be executed? +% – Code generation: what kind of target code is generated for the feature? +% You may make use of your ANTLR grammar as a basis for this description, but note that not every +% rule necessarily corresponds to a language feature. + + +\subsection{Basic expressions} +At the very core of the language are basic arithmetic, logic and compound expressions and literals for fundamental types. + +\subsubsection{Syntax} +\begin{minted}{antlr} +program: expr EOF; + +expr + : singleExpr (COMPOUND singleExpr?)* + ; + +singleExpr + : PAROPEN expr PARCLOSE #parens + | BRAOPEN expr BRACLOSE #block + | op=(PLUS|MINUS|NOT) singleExpr #prefix1 + | lhs=singleExpr op=(MULTIPLY|DIVIDE) rhs=singleExpr #infix1 + | lhs=singleExpr op=(PLUS|MINUS) rhs=singleExpr #infix2 + | lhs=singleExpr op=(LT|LEQ|GTE|GT|EQ|NEQ) rhs=singleExpr #infix3 + | lhs=singleExpr AND rhs=singleExpr #infix4 + | lhs=singleExpr OR rhs=singleExpr #infix5 + | LITERAL10 #literalInteger + | CHAR #literalCharacter + | (TRUE|FALSE) #literalBoolean + ; +\end{minted} + +\paragraph{Examples} +\begin{minted}{boppi} +5; +4+(2*3/-1); +'c';;;; +{ + true && false; + 3 +}+4; +\end{minted} + +\subsubsection{Use} +\verb|#prefix1|, \verb|#infix1| and \verb|#infix2| restrict their operand types and result type to integers. \verb|#infix3| restricts their operand types to integers and sets its result type to boolean, except when the operator is EQ or NEQ, in which case the operands can be of any type as long as both have the same type. \verb|#infix4| and \verb|#infix5| restrict their operand types and result type to booleans. + +\subsubsection{Semantics} +A program consists of one compound expression. +COMPOUND evaluates two expressions, discarding the result of the left hand side and passing the result of the right hand. +Both \verb|#parens| and \verb|#block| contain a compound expression, however \verb|#block| introduces a deeper scope for the expression within, as clarified in the next feature. + +\subsubsection{Code generation} +The compound expression simply generates its inner expressions in order, the literals generate a \verb|loadI| instruction and the operators generate a single corresponding instruction. + + + + +\subsection{Variables} + +\subsubsection{Syntax} +\begin{minted}{antlr} +singleExpr + : ... + | DECLARE type IDENTIFIER #declare + | variable ASSIGN singleExpr #assign + | variable #variableExpr + ; + +type + : staticType=(INTTYPE | BOOLTYPE | CHARTYPE) #typeSimple + | variable #typeVariable + ; + +variable: IDENTIFIER; +\end{minted} + +\paragraph{Examples} +\begin{minted}{boppi} +var int myInt; +myInt := 4; + +var myInt otherInt; +otherInt := 4+(myInt := 2); + +var bool aBool; +aBool := { + var int otherInt; + otherInt := 12; + myInt > otherInt +}; +\end{minted} + +\subsubsection{Use} +A variable can only be assigned and used after it has been declared and within the same scope or a deeper scope than where it is declared. A variable cannot be declared if the name is already used by another variable in the same scope. + +\subsubsection{Semantics} +\verb|#declare| introduces a new variable to the current scope. This variable will be undeclared once the current scope is closed. The variable will be stored at an offset within the AR, thereby increasing the size of the AR. +\verb|#assign| evaluates the + +\subsubsection{Code generation} +%wat voor ILOC wordt er gegenereerd voor deze functie? + + + + +\subsection{Input/Output} + +\subsubsection{Syntax} +\begin{minted}{antlr} +singleExpr + : ... + | IN PAROPEN variable (LISTDELIM variable)* PARCLOSE #read + | OUT PAROPEN expr (LISTDELIM expr)* PARCLOSE #write + ; +\end{minted} + +\paragraph{Examples} +\begin{minted}{boppi} + +\end{minted} + +\subsubsection{Use} +%hoe gebruik je het? wat zijn de typerestricties? + +\subsubsection{Semantics} +%semantiek + +\subsubsection{Code generation} +%wat voor ILOC wordt er gegenereerd voor deze functie? + + + + +\subsection{} + +\subsubsection{Syntax} +\begin{minted}{antlr} +\end{minted} + +\paragraph{Examples} +\begin{minted}{boppi} +\end{minted} + +\subsubsection{Use} +%hoe gebruik je het? wat zijn de typerestricties? + +\subsubsection{Semantics} +%semantiek + +\subsubsection{Code generation} +%wat voor ILOC wordt er gegenereerd voor deze functie? + diff --git a/doc/report-grammar.tex b/doc/report-grammar.tex new file mode 100644 index 0000000..99daf0f --- /dev/null +++ b/doc/report-grammar.tex @@ -0,0 +1 @@ +\inputminted{antlr}{\projectroot src/pp/s1184725/boppi/antlr/Boppi.g4} diff --git a/doc/report-problems.tex b/doc/report-problems.tex new file mode 100644 index 0000000..480bc17 --- /dev/null +++ b/doc/report-problems.tex @@ -0,0 +1,15 @@ +% Problems and solutions. Summary of problems you encountered and how you solved them (max. two +% pages). + +\subsection{Function calls within parameters} +Each function call requires an AR. When new ARs are allocated at a static offset relative to the current AR, this may lead to problems with function calls of the form \verb|f(g(h))| or \verb|f(a, g(h))|: the AR of \verb|f| must not be overwritten during the call to \verb|g|. There are multiple solutions to this problem: +\begin{enumerate} + \item dynamically allocating the AR (requires a dynamic allocator) + \item generating different offsets for nested function calls (e.g. the AR of \verb|h| in \verb|f(g(h()))| must be allocated at \verb|ARP+f_AR+g_AR|) + \item allocating the AR after evaluating its parameters (requires a temporary storage for the parameters) +\end{enumerate} + +The third option was chosen, namely by pushing each parameter value to the stack, then allocating the AR for the outer function call and popping the values into it. + +\subsection{Register saves} +When calling a function, some registers must be saved by the caller or callee in order to not lose their value when executing the callee. The choice was made to use caller-saves and to keep track of all the registers that are required for future instructions. \ No newline at end of file diff --git a/doc/verslag-software.tex b/doc/report-software.tex similarity index 100% rename from doc/verslag-software.tex rename to doc/report-software.tex diff --git a/doc/report-summary.tex b/doc/report-summary.tex new file mode 100644 index 0000000..7749db9 --- /dev/null +++ b/doc/report-summary.tex @@ -0,0 +1,13 @@ +% Summary of the main features of your programming language (max. 1 page) +Boppi includes the following features: +\begin{enumerate} + \item basic expressions + \item conditionals + \item variables + \item functions +\end{enumerate} + + +Conditionals comprise constructions equivalent to \verb|if|, \verb|if else| and \verb|while|. +Variables have a lifetime, namely the scope (between \verb|{ }| or within conditionals and functions) in which they are declared. Variables can be used as type identifiers for other variables. +Function references can be passed around in variables, but they may not work correctly when run in a different scope. diff --git a/doc/verslag-testprogramma.tex b/doc/report-test-program.tex similarity index 100% rename from doc/verslag-testprogramma.tex rename to doc/report-test-program.tex diff --git a/doc/verslag-tests.tex b/doc/report-tests.tex similarity index 86% rename from doc/verslag-tests.tex rename to doc/report-tests.tex index 27203a4..faca1dd 100644 --- a/doc/verslag-tests.tex +++ b/doc/report-tests.tex @@ -4,3 +4,5 @@ % syntactic, semantic or run-time errors. % All tests should be provided as part of the zip-file. One test programshould be included as an appendix % in the report (see below). + +\inputminted{java}{\projectroot src/pp/s1184725/boppi/test/AllTests.java} diff --git a/doc/verslag-walker.tex b/doc/report-walker.tex similarity index 100% rename from doc/verslag-walker.tex rename to doc/report-walker.tex diff --git a/doc/report.tex b/doc/report.tex new file mode 100644 index 0000000..ee636b5 --- /dev/null +++ b/doc/report.tex @@ -0,0 +1,54 @@ +\documentclass[a4paper]{article} +\usepackage[margin=1in]{geometry} +\usepackage{graphicx} +\usepackage{pdfpages} +\usepackage[dutch]{babel} +\usepackage{amsmath} +\usepackage{amsfonts} +\usepackage[scientific-notation=true,round-precision=5,round-mode=figures]{siunitx} +\usepackage{minted} + +\begin{document} +\title{Boppi Report} +\maketitle +\vfill +\author{Frank Wibbelink (s1184725)} +\newpage + +\tableofcontents +\newpage + +\input{project-root} +\setminted{breaklines} +\setminted{frame=lines} + +\section{Summary} +\input{report-summary} + +\section{Problems and solutions} +\input{report-problems} + +\section{Language description} +\input{report-description} + +\section{Software description} +\input{report-software} + +\section{Tests} +\input{report-tests} + +\section{Conclusions} +\input{report-conclusion} + +\newpage +\appendix +\section{ANTLR grammars} +\input{report-grammar} + +\section{ANTLR walkers} +\input{report-walker} + +\section{Test programs} +\input{report-test-program} + +\end{document} diff --git a/doc/verslag-beschrijving.tex b/doc/verslag-beschrijving.tex deleted file mode 100644 index 0ea1898..0000000 --- a/doc/verslag-beschrijving.tex +++ /dev/null @@ -1,25 +0,0 @@ -% Detailed language description. A systematic description of the features of your language, for each -% feature specifying -% – Syntax, including one or more examples; -% – Usage: how should the feature be used? Are there any typing or other restrictions? -% – Semantics: what does the feature do? How will it be executed? -% – Code generation: what kind of target code is generated for the feature? -% You may make use of your ANTLR grammar as a basis for this description, but note that not every -% rule necessarily corresponds to a language feature. - - -\subsection{} - -\subsubsection{Syntax} -\begin{lstlisting}[style=ANTLR] -\end{lstlisting} - -\subsubsection{Gebruik} -%hoe gebruik je het? wat zijn de typerestricties? - -\subsubsection{Semantiek} -%semantiek - -\subsubsection{Codegeneratie} -%wat voor ILOC wordt er gegenereerd voor deze functie? - diff --git a/doc/verslag-grammatica.tex b/doc/verslag-grammatica.tex deleted file mode 100644 index d5cdd7d..0000000 --- a/doc/verslag-grammatica.tex +++ /dev/null @@ -1,2 +0,0 @@ -\begin{lstlisting}[style=ANTLR] -\end{lstlisting} diff --git a/doc/verslag-problemen.tex b/doc/verslag-problemen.tex deleted file mode 100644 index e64d307..0000000 --- a/doc/verslag-problemen.tex +++ /dev/null @@ -1,2 +0,0 @@ -% Problems and solutions. Summary of problems you encountered and how you solved them (max. two -% pages). diff --git a/doc/verslag-samenvatting.tex b/doc/verslag-samenvatting.tex deleted file mode 100644 index 03cfa99..0000000 --- a/doc/verslag-samenvatting.tex +++ /dev/null @@ -1 +0,0 @@ -% Summary of the main features of your programming language (max. 1 page) diff --git a/doc/verslag.tex b/doc/verslag.tex deleted file mode 100644 index ca600ca..0000000 --- a/doc/verslag.tex +++ /dev/null @@ -1,72 +0,0 @@ -\documentclass[a4paper]{article} -\usepackage[margin=1in]{geometry} -\usepackage{graphicx} -\usepackage{pdfpages} -\usepackage[dutch]{babel} -\usepackage{amsmath} -\usepackage{amsfonts} -\usepackage[scientific-notation=true,round-precision=5,round-mode=figures]{siunitx} -\usepackage{listings}% http://ctan.org/pkg/listings -\lstset{ - basicstyle=\ttfamily, - mathescape, - frame=single -} - -\lstdefinestyle{ANTLR}{ - literate=*, - basicstyle=\small\ttfamily\color{black}, - breaklines=true, - morestring=[b][\color{blue}\ttfamily]', - moredelim=*[s][\color{black}\ttfamily]{options}{\}}, - moredelim=[s][\color{blue}\ttfamily]{\[}{\]}, - moredelim=[s][\color{magenta}\ttfamily]{<}{>}, - commentstyle={\color{gray}\itshape}, - morecomment=[l]{//}, - morecomment=[l][\color{gray}]\#, - emph={ - grammar,skip; - },emphstyle={\color{magenta}\ttfamily}, - alsoletter={:,|,;,->,.}, - morekeywords={:,|,;,->,.}, - keywordstyle={\color{red}\bfseries}, -} - -\begin{document} -\title{Verslag Vertalterbouwproject} -\maketitle -\vfill -\author{Frank Wibbelink (s1184725)} -\newpage - - -\section{Samenvatting} -\input{verslag-samenvatting.tex} - -\section{Problemen en oplossingen} -\input{verslag-problemen.tex} - -\section{Beschrijving van de taal} -\input{verslag-beschrijving.tex} - -\section{Programmabeschrijving} -\input{verslag-software.tex} - -\section{Tests} -\input{verslag-tests.tex} - -\section{Conclusies} -\input{verslag-conclusies.tex} - -\newpage -\appendix -\section{ANTLR-grammatica's} -\input{verslag-grammatica.tex} - -\section{ANTLR-walkers} -\input{verslag-walker.tex} - -\section{Testprogramma} -\input{verslag-testprogramma.tex} - -\end{document} diff --git a/src/pp/s1184725/boppi/Annotations.java b/src/pp/s1184725/boppi/Annotations.java index aa44f49..5d610cc 100644 --- a/src/pp/s1184725/boppi/Annotations.java +++ b/src/pp/s1184725/boppi/Annotations.java @@ -31,6 +31,10 @@ public class Annotations { * A stack with the current function scope on top. */ public Stack> currentFunction; + /** + * A symbol table + */ + public CachingSymbolTable symbols; /** * Creates a new annotations object with empty maps. @@ -41,5 +45,6 @@ public class Annotations { function = new ParseTreeProperty<>(); variables = new ParseTreeProperty<>(); currentFunction = new Stack<>(); + symbols = new CachingSymbolTable<>(); } } diff --git a/src/pp/s1184725/boppi/BoppiChecker.java b/src/pp/s1184725/boppi/BoppiChecker.java index bc8d939..a0b5aed 100644 --- a/src/pp/s1184725/boppi/BoppiChecker.java +++ b/src/pp/s1184725/boppi/BoppiChecker.java @@ -17,7 +17,6 @@ import pp.s1184725.boppi.antlr.BoppiParser.*; * */ public class BoppiChecker extends BoppiBaseVisitor { - private CachingSymbolTable symbols; private Annotations an; private Logger log; @@ -32,17 +31,47 @@ public class BoppiChecker extends BoppiBaseVisitor { * @return the generated annotations */ public static Annotations checkProgram(ParseTree ast, Logger logger) { - BoppiChecker checker = new BoppiChecker(logger); + BoppiChecker checker = new BoppiChecker(logger, new Annotations()); checker.visit(ast); return checker.an; } - protected BoppiChecker(Logger logger) { - symbols = new CachingSymbolTable<>(); - an = new Annotations(); + protected BoppiChecker(Logger logger, Annotations annotations) { + an = annotations; log = logger; } + /** + * Checks whether two types are compatible and log an error if they do not. + * + * @param type1 + * the left hand or actual type + * @param type2 + * the right hand or desired type + * @param node + * the context used for logging + */ + private void checkConstraint(Type type1, Type type2, ParserRuleContext node) { + if (!type2.equals(type1)) + log.severe(getError(node, "Could not match type %s with type %s.", type1.toString(), type2.toString())); + } + + /** + * Returns an error message for a given parse tree node. + * + * @param ctx + * the parse tree node at which the error occurred + * @param message + * the error message + * @param args + * arguments for the message, see {@link String#format} + */ + private String getError(ParserRuleContext node, String message, Object... args) { + int line = node.getStart().getLine(); + int column = node.getStart().getCharPositionInLine(); + return String.format("Line %d:%d - %s", line, column, String.format(message, args)); + } + @Override public Type visit(ParseTree tree) { Type type = super.visit(tree); @@ -60,14 +89,9 @@ public class BoppiChecker extends BoppiBaseVisitor { return expr; } - @Override - public Type visitBool(BoolContext ctx) { - return SimpleType.BOOL; - } - @Override public Type visitBlock(BlockContext ctx) { - return symbols.withScope(() -> visit(ctx.expr())); + return an.symbols.withScope(() -> visit(ctx.expr())); } @Override @@ -90,23 +114,10 @@ public class BoppiChecker extends BoppiBaseVisitor { } } - @Override - public Type visitChar(CharContext ctx) { - return SimpleType.CHAR; - } - - @Override - public Type visitExpr(ExprContext ctx) { - if (ctx.singleExpr(ctx.singleExpr().size() - 1) instanceof DeclareContext) - log.severe(getError(ctx, "Compound expression ends with declaration.")); - - return ctx.singleExpr().stream().map(this::visit).reduce((__, snd) -> snd).get(); - } - @Override public Type visitDeclare(DeclareContext ctx) { try { - Variable var = symbols.put(ctx.IDENTIFIER().getText(), visit(ctx.type())); + Variable var = an.symbols.put(ctx.IDENTIFIER().getText(), visit(ctx.type())); an.variables.put(ctx, var); if (var.getType() instanceof TupleType) @@ -126,23 +137,23 @@ public class BoppiChecker extends BoppiBaseVisitor { } @Override - public Type visitFuncDeclare(FuncDeclareContext ctx) { + public Type visitDeclareFunction(DeclareFunctionContext ctx) { try { TupleType parameterTypes = new TupleType( ctx.type().stream().skip(1).map(this::visit).collect(Collectors.toList())); FunctionType type = new FunctionType(visit(ctx.result), parameterTypes); - Variable func = symbols.put(ctx.name.getText(), type); + Variable func = an.symbols.put(ctx.name.getText(), type); an.variables.put(ctx, func); an.currentFunction.push(func); - type.setLocalDataSize(symbols.withFunctionScope(() -> { + type.setLocalDataSize(an.symbols.withFunctionScope(() -> { for (int i = 1; i < ctx.type().size(); i++) try { - symbols.put(ctx.IDENTIFIER(i).getText(), an.types.get(ctx.type(i))); + an.symbols.put(ctx.IDENTIFIER(i).getText(), an.types.get(ctx.type(i))); } catch (Exception e) { log.severe(getError(ctx, e.getMessage())); } - checkConstraint(symbols.withScope(() -> visit(ctx.body)), an.types.get(ctx.result), ctx); + checkConstraint(an.symbols.withScope(() -> visit(ctx.body)), an.types.get(ctx.result), ctx); })); } catch (EmptyStackException e) { log.severe(getError(ctx, "Declaring function outside a program.")); @@ -153,17 +164,25 @@ public class BoppiChecker extends BoppiBaseVisitor { return SimpleType.VOID; } + @Override + public Type visitExpr(ExprContext ctx) { + if (ctx.singleExpr(ctx.singleExpr().size() - 1) instanceof DeclareContext) + log.severe(getError(ctx, "Compound expression ends with declaration.")); + + return ctx.singleExpr().stream().map(this::visit).reduce((__, snd) -> snd).get(); + } + @Override public Type visitIf(IfContext ctx) { - return symbols.withScope(() -> { + return an.symbols.withScope(() -> { visit(ctx.cond); if (ctx.onFalse != null) { - Type trueType = symbols.withScope(() -> visit(ctx.onTrue)); - Type falseType = symbols.withScope(() -> visit(ctx.onFalse)); + Type trueType = an.symbols.withScope(() -> visit(ctx.onTrue)); + Type falseType = an.symbols.withScope(() -> visit(ctx.onFalse)); return (trueType.equals(falseType)) ? trueType : SimpleType.VOID; } else { - symbols.withScope(() -> visit(ctx.onTrue)); + an.symbols.withScope(() -> visit(ctx.onTrue)); return SimpleType.VOID; } }); @@ -219,7 +238,17 @@ public class BoppiChecker extends BoppiBaseVisitor { } @Override - public Type visitNumber(NumberContext ctx) { + public Type visitLiteralBoolean(LiteralBooleanContext ctx) { + return SimpleType.BOOL; + } + + @Override + public Type visitLiteralCharacter(LiteralCharacterContext ctx) { + return SimpleType.CHAR; + } + + @Override + public Type visitLiteralInteger(LiteralIntegerContext ctx) { return SimpleType.INT; } @@ -250,7 +279,7 @@ public class BoppiChecker extends BoppiBaseVisitor { public Type visitProgram(ProgramContext ctx) { FunctionType main = new FunctionType(TupleType.UNIT, TupleType.UNIT); an.currentFunction.push(new Variable(main, 0, 0)); - main.setLocalDataSize(symbols.withFunctionScope(() -> super.visitProgram(ctx))); + main.setLocalDataSize(an.symbols.withFunctionScope(() -> super.visitProgram(ctx))); return null; } @@ -284,15 +313,10 @@ public class BoppiChecker extends BoppiBaseVisitor { return visit(ctx.variable()); } - @Override - public Type visitVar(VarContext ctx) { - return visit(ctx.variable()); - } - @Override public Type visitVariable(VariableContext ctx) { try { - Variable var = symbols.get(ctx.getText()); + Variable var = an.symbols.get(ctx.getText()); an.variables.put(ctx, var); return var.getType(); } catch (EmptyStackException e) { @@ -304,11 +328,16 @@ public class BoppiChecker extends BoppiBaseVisitor { return SimpleType.VOID; } + @Override + public Type visitVariableExpr(VariableExprContext ctx) { + return visit(ctx.variable()); + } + @Override public Type visitWhile(WhileContext ctx) { - return symbols.withScope(() -> { + return an.symbols.withScope(() -> { checkConstraint(visit(ctx.cond), SimpleType.BOOL, ctx.cond); - symbols.withScope(() -> visit(ctx.onTrue)); + an.symbols.withScope(() -> visit(ctx.onTrue)); return SimpleType.VOID; }); } @@ -329,35 +358,4 @@ public class BoppiChecker extends BoppiBaseVisitor { return SimpleType.VOID; } } - - /** - * Checks whether two types are compatible and log an error if they do not. - * - * @param type1 - * the left hand or actual type - * @param type2 - * the right hand or desired type - * @param node - * the context used for logging - */ - private void checkConstraint(Type type1, Type type2, ParserRuleContext node) { - if (!type2.equals(type1)) - log.severe(getError(node, "Could not match type %s with type %s.", type1.toString(), type2.toString())); - } - - /** - * Returns an error message for a given parse tree node. - * - * @param ctx - * the parse tree node at which the error occurred - * @param message - * the error message - * @param args - * arguments for the message, see {@link String#format} - */ - private String getError(ParserRuleContext node, String message, Object... args) { - int line = node.getStart().getLine(); - int column = node.getStart().getCharPositionInLine(); - return String.format("Line %d:%d - %s", line, column, String.format(message, args)); - } } diff --git a/src/pp/s1184725/boppi/BoppiGenerator.java b/src/pp/s1184725/boppi/BoppiGenerator.java index 4aa9032..c95b937 100644 --- a/src/pp/s1184725/boppi/BoppiGenerator.java +++ b/src/pp/s1184725/boppi/BoppiGenerator.java @@ -89,10 +89,10 @@ public class BoppiGenerator extends BoppiBaseVisitor { reg = new Reg("__" + (regInUse.size() + 1)); regInUse.add(reg); - if (regInUse.size() == 11) + if (regInUse.size() == RECOMMENDED_REGISTER_COUNT + 1) logger.warning( - String.format("Using more than 10 registers at %d:%d. Consider rebalancing your expressions.", - ctx.start.getLine(), ctx.start.getCharPositionInLine())); + String.format("Using more than %d registers at %d:%d. Consider rebalancing your expressions.", + RECOMMENDED_REGISTER_COUNT, ctx.start.getLine(), ctx.start.getCharPositionInLine())); } else { if (regFree.remove(an.registers.get(ctx))) reg = an.registers.get(ctx); @@ -184,42 +184,20 @@ public class BoppiGenerator extends BoppiBaseVisitor { return emit(comment, (Label) null, opCode, args); } - @Override - public Void visitBlock(BlockContext ctx) { - super.visitBlock(ctx); - copyReg(ctx.expr(), ctx); - return null; - } - - @Override - public Void visitDeclare(DeclareContext ctx) { - return null; - } - - @Override - public Void visitParens(ParensContext ctx) { - super.visitParens(ctx); - copyReg(ctx.expr(), ctx); - return null; - } - - @Override - public Void visitProgram(ProgramContext ctx) { - emit("initialise temp ARP", OpCode.loadI, ZERO, tempArp); - - return visitChildren(ctx); - } - - @Override - public Void visitExpr(ExprContext ctx) { - for (int i = 0; i < ctx.singleExpr().size() - 1; i++) { - visit(ctx.singleExpr(i)); - freeReg(ctx.singleExpr(i)); - } - ParserRuleContext last = ctx.singleExpr(ctx.singleExpr().size() - 1); - visit(last); - copyReg(last, ctx); - return null; + /** + * Returns an error message for a given parse tree node. + * + * @param ctx + * the parse tree node at which the error occurred + * @param message + * the error message + * @param args + * arguments for the message, see {@link String#format} + */ + private String getError(ParserRuleContext node, String message, Object... args) { + int line = node.getStart().getLine(); + int column = node.getStart().getCharPositionInLine(); + return String.format("Line %d:%d - %s", line, column, String.format(message, args)); } @Override @@ -237,6 +215,13 @@ public class BoppiGenerator extends BoppiBaseVisitor { return null; } + @Override + public Void visitBlock(BlockContext ctx) { + super.visitBlock(ctx); + copyReg(ctx.expr(), ctx); + return null; + } + @Override public Void visitCall(CallContext ctx) { String base = "call " + ctx.variable().getText() + " - "; @@ -306,7 +291,12 @@ public class BoppiGenerator extends BoppiBaseVisitor { } @Override - public Void visitFuncDeclare(FuncDeclareContext ctx) { + public Void visitDeclare(DeclareContext ctx) { + return null; + } + + @Override + public Void visitDeclareFunction(DeclareFunctionContext ctx) { String base = "define " + ctx.IDENTIFIER(0).getText() + " - "; Label skip = new Label("s" + ctx.hashCode()); emit(base + "jump over body", OpCode.jumpI, skip); @@ -325,14 +315,52 @@ public class BoppiGenerator extends BoppiBaseVisitor { } @Override - public Void visitBool(BoolContext ctx) { - emit(ctx.getText(), OpCode.loadI, new Num(ctx.TRUE() != null ? 1 : 0), makeReg(ctx)); + public Void visitExpr(ExprContext ctx) { + for (int i = 0; i < ctx.singleExpr().size() - 1; i++) { + visit(ctx.singleExpr(i)); + freeReg(ctx.singleExpr(i)); + } + ParserRuleContext last = ctx.singleExpr(ctx.singleExpr().size() - 1); + visit(last); + copyReg(last, ctx); return null; } @Override - public Void visitNumber(NumberContext ctx) { - emit(ctx.getText(), OpCode.loadI, new Num(Integer.parseInt(ctx.LITERAL10().getText())), makeReg(ctx)); + public Void visitIf(IfContext ctx) { + Label toTrue = new Label("if_t" + ctx.hashCode()); + Label toFalse = new Label("if_f" + ctx.hashCode()); + Label toEnd = new Label("if_e" + ctx.hashCode()); + + visit(ctx.cond); + if (ctx.onFalse == null) { + emit("", OpCode.cbr, getReg(ctx.cond), toTrue, toEnd); + freeReg(ctx.cond); + + emit("", toTrue, OpCode.nop); + visit(ctx.onTrue); + freeReg(ctx.onTrue); + } else { + emit("", OpCode.cbr, getReg(ctx.cond), toTrue, toFalse); + freeReg(ctx.cond); + + emit("", toTrue, OpCode.nop); + visit(ctx.onTrue); + freeReg(ctx.onTrue); + if (an.types.get(ctx) != SimpleType.VOID) + emit("result", OpCode.i2i, getReg(ctx.onTrue), makeReg(ctx)); + emit("", OpCode.jumpI, toEnd); + freeReg(ctx); + + emit("", toFalse, OpCode.nop); + visit(ctx.onFalse); + freeReg(ctx.onFalse); + if (an.types.get(ctx) != SimpleType.VOID) + emit("result", OpCode.i2i, getReg(ctx.onFalse), makeReg(ctx)); + + } + emit("end target", toEnd, OpCode.nop); + return null; } @@ -381,6 +409,32 @@ public class BoppiGenerator extends BoppiBaseVisitor { return null; } + @Override + public Void visitLiteralBoolean(LiteralBooleanContext ctx) { + emit(ctx.getText(), OpCode.loadI, new Num(ctx.TRUE() != null ? 1 : 0), makeReg(ctx)); + return null; + } + + @Override + public Void visitLiteralCharacter(LiteralCharacterContext ctx) { + emit(ctx.getText(), OpCode.loadI, new Num(ctx.CHAR().getText().codePointAt(1)), makeReg(ctx)); + emit(ctx.getText(), OpCode.i2c, getReg(ctx), getReg(ctx)); + return null; + } + + @Override + public Void visitLiteralInteger(LiteralIntegerContext ctx) { + emit(ctx.getText(), OpCode.loadI, new Num(Integer.parseInt(ctx.LITERAL10().getText())), makeReg(ctx)); + return null; + } + + @Override + public Void visitParens(ParensContext ctx) { + super.visitParens(ctx); + copyReg(ctx.expr(), ctx); + return null; + } + @Override public Void visitPrefix1(Prefix1Context ctx) { visitChildren(ctx); @@ -389,27 +443,20 @@ public class BoppiGenerator extends BoppiBaseVisitor { switch (ctx.op.getType()) { case BoppiLexer.MINUS: emit("unary -", OpCode.rsubI, getReg(ctx), ZERO, getReg(ctx)); + break; + + case BoppiLexer.NOT: + emit("not", OpCode.xorI, getReg(ctx), new Num(1), getReg(ctx)); + break; } return null; } @Override - public Void visitVar(VarContext ctx) { - if (SimpleType.CHAR.equals(an.types.get(ctx))) - emit("var " + ctx.variable().getText(), OpCode.cloadAI, getVar(ctx.variable()), - new Num(an.variables.get(ctx.variable()).getOffset()), makeReg(ctx)); - else - emit("var " + ctx.variable().getText(), OpCode.loadAI, getVar(ctx.variable()), - new Num(an.variables.get(ctx.variable()).getOffset()), makeReg(ctx)); + public Void visitProgram(ProgramContext ctx) { + emit("initialise temp ARP", OpCode.loadI, ZERO, tempArp); - return null; - } - - @Override - public Void visitChar(CharContext ctx) { - emit(ctx.getText(), OpCode.loadI, new Num(ctx.CHAR().getText().codePointAt(1)), makeReg(ctx)); - emit(ctx.getText(), OpCode.i2c, getReg(ctx), getReg(ctx)); - return null; + return visitChildren(ctx); } @Override @@ -494,39 +541,13 @@ public class BoppiGenerator extends BoppiBaseVisitor { } @Override - public Void visitIf(IfContext ctx) { - Label toTrue = new Label("if_t" + ctx.hashCode()); - Label toFalse = new Label("if_f" + ctx.hashCode()); - Label toEnd = new Label("if_e" + ctx.hashCode()); - - visit(ctx.cond); - if (ctx.onFalse == null) { - emit("", OpCode.cbr, getReg(ctx.cond), toTrue, toEnd); - freeReg(ctx.cond); - - emit("", toTrue, OpCode.nop); - visit(ctx.onTrue); - freeReg(ctx.onTrue); - } else { - emit("", OpCode.cbr, getReg(ctx.cond), toTrue, toFalse); - freeReg(ctx.cond); - - emit("", toTrue, OpCode.nop); - visit(ctx.onTrue); - freeReg(ctx.onTrue); - if (an.types.get(ctx) != SimpleType.VOID) - emit("result", OpCode.i2i, getReg(ctx.onTrue), makeReg(ctx)); - emit("", OpCode.jumpI, toEnd); - freeReg(ctx); - - emit("", toFalse, OpCode.nop); - visit(ctx.onFalse); - freeReg(ctx.onFalse); - if (an.types.get(ctx) != SimpleType.VOID) - emit("result", OpCode.i2i, getReg(ctx.onFalse), makeReg(ctx)); - - } - emit("end target", toEnd, OpCode.nop); + public Void visitVariableExpr(VariableExprContext ctx) { + if (SimpleType.CHAR.equals(an.types.get(ctx))) + emit("var " + ctx.variable().getText(), OpCode.cloadAI, getVar(ctx.variable()), + new Num(an.variables.get(ctx.variable()).getOffset()), makeReg(ctx)); + else + emit("var " + ctx.variable().getText(), OpCode.loadAI, getVar(ctx.variable()), + new Num(an.variables.get(ctx.variable()).getOffset()), makeReg(ctx)); return null; } @@ -588,20 +609,4 @@ public class BoppiGenerator extends BoppiBaseVisitor { } return null; } - - /** - * Returns an error message for a given parse tree node. - * - * @param ctx - * the parse tree node at which the error occurred - * @param message - * the error message - * @param args - * arguments for the message, see {@link String#format} - */ - private String getError(ParserRuleContext node, String message, Object... args) { - int line = node.getStart().getLine(); - int column = node.getStart().getCharPositionInLine(); - return String.format("Line %d:%d - %s", line, column, String.format(message, args)); - } } diff --git a/src/pp/s1184725/boppi/antlr/.gitignore b/src/pp/s1184725/boppi/antlr/.gitignore new file mode 100644 index 0000000..0d2b878 --- /dev/null +++ b/src/pp/s1184725/boppi/antlr/.gitignore @@ -0,0 +1,6 @@ +/Boppi.tokens +/BoppiBaseVisitor.java +/BoppiLexer.java +/BoppiLexer.tokens +/BoppiParser.java +/BoppiVisitor.java diff --git a/src/pp/s1184725/boppi/antlr/Boppi.g4 b/src/pp/s1184725/boppi/antlr/Boppi.g4 index 5b206a7..b2130d9 100644 --- a/src/pp/s1184725/boppi/antlr/Boppi.g4 +++ b/src/pp/s1184725/boppi/antlr/Boppi.g4 @@ -3,31 +3,31 @@ grammar Boppi; program: expr EOF; expr - : singleExpr (COMPOUND singleExpr?)* - ; + : singleExpr (COMPOUND singleExpr?)* + ; singleExpr - : PAROPEN expr PARCLOSE #parens - | BRAOPEN expr BRACLOSE #block - | IN PAROPEN variable (LISTDELIM variable)* PARCLOSE #read - | OUT PAROPEN expr (LISTDELIM expr)* PARCLOSE #write - | variable PAROPEN (expr (LISTDELIM expr)*)? PARCLOSE #call - | IFOPEN cond=expr IFTRUE onTrue=expr (IFFALSE onFalse=expr)? IFCLOSE #if - | WHILEOPEN cond=expr WHILETRUE onTrue=expr WHILECLOSE #while - | op=(PLUS|MINUS|NOT) singleExpr #prefix1 - | lhs=singleExpr op=(MULTIPLY|DIVIDE|MODULO) rhs=singleExpr #infix1 - | lhs=singleExpr op=(PLUS|MINUS) rhs=singleExpr #infix2 - | lhs=singleExpr op=(LT|LEQ|GTE|GT|EQ|NEQ) rhs=singleExpr #infix3 - | lhs=singleExpr AND rhs=singleExpr #infix4 - | lhs=singleExpr OR rhs=singleExpr #infix5 - | DECLARE type IDENTIFIER #declare - | FUNCTION result=type name=IDENTIFIER PAROPEN (type IDENTIFIER (LISTDELIM type IDENTIFIER)*)? PARCLOSE body=singleExpr #funcDeclare - | variable ASSIGN singleExpr #assign - | variable #var - | LITERAL10 #number - | CHAR #char - | (TRUE|FALSE) #bool - ; + : PAROPEN expr PARCLOSE #parens + | BRAOPEN expr BRACLOSE #block + | IN PAROPEN variable (LISTDELIM variable)* PARCLOSE #read + | OUT PAROPEN expr (LISTDELIM expr)* PARCLOSE #write + | IFOPEN cond=expr IFTRUE onTrue=expr (IFFALSE onFalse=expr)? IFCLOSE #if + | WHILEOPEN cond=expr WHILETRUE onTrue=expr WHILECLOSE #while + | op=(PLUS|MINUS|NOT) singleExpr #prefix1 + | lhs=singleExpr op=(MULTIPLY|DIVIDE) rhs=singleExpr #infix1 + | lhs=singleExpr op=(PLUS|MINUS) rhs=singleExpr #infix2 + | lhs=singleExpr op=(LT|LEQ|GTE|GT|EQ|NEQ) rhs=singleExpr #infix3 + | lhs=singleExpr AND rhs=singleExpr #infix4 + | lhs=singleExpr OR rhs=singleExpr #infix5 + | DECLARE type IDENTIFIER #declare + | variable ASSIGN singleExpr #assign + | variable #variableExpr + | LITERAL10 #literalInteger + | CHAR #literalCharacter + | (TRUE|FALSE) #literalBoolean + | FUNCTION result=type name=IDENTIFIER PAROPEN (type IDENTIFIER (LISTDELIM type IDENTIFIER)*)? PARCLOSE body=singleExpr #declareFunction + | variable PAROPEN (expr (LISTDELIM expr)*)? PARCLOSE #call + ; type : type ARROW type #typeFunction @@ -35,6 +35,7 @@ type | PAROPEN (type (LISTDELIM type )*)? PARCLOSE #typeTuple | variable #typeVariable ; + variable: IDENTIFIER; PAROPEN: '('; @@ -56,7 +57,6 @@ MINUS: '-'; NOT: '!'; MULTIPLY: '*'; DIVIDE: '/'; -MODULO: '%'; LEQ: '<='; GTE: '>='; NEQ: '<>'; diff --git a/util/PygmentBoppiLexer.py b/util/PygmentBoppiLexer.py new file mode 100644 index 0000000..67d478d --- /dev/null +++ b/util/PygmentBoppiLexer.py @@ -0,0 +1,26 @@ +from pygments.lexer import RegexLexer +from pygments.token import * + +__all__ = ['BoppiLexer'] + +class BoppiLexer(RegexLexer): + name = 'Boppi' + aliases = ['boppi'] + filenames = ['*.boppi'] + + tokens = { + 'root': [ + (r'/\*.*?\*/', Comment), + (r'//.*?$', Comment), + (r'(read|print|if|then|else|fi|while|do|od)\b', Keyword), + (r'(true|false)\b', Keyword.Constant), + (r'(var|function)\b', Keyword.Declaration), + (r'(int|bool|char)\b', Keyword.Type), + (r'\(|\)|\{|\}|;|,', Punctuation), + (r'\+|-|!|\*|/|<=|>=|<>|==|<|>|&&|\|\||->|:=', Operator), + (r'[A-Za-z_][A-Za-z0-9_]*', Name.Variable), + (r'0|[1-9][0-9]*', Number.Integer), + (r'\'.\'', String.Char), + (r'.+?', Text) + ] + }