reordered methods, started report
This commit is contained in:
parent
a1257c237b
commit
610611e212
|
@ -1,5 +1,6 @@
|
||||||
/verslag.aux
|
/project-root.tex
|
||||||
/verslag.log
|
/report.aux
|
||||||
/verslag.synctex.gz
|
/report.log
|
||||||
/verslag.pdf
|
/report.synctex.gz
|
||||||
|
/report.pdf
|
||||||
/javadoc
|
/javadoc
|
||||||
|
|
|
@ -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
|
||||||
|
| <assoc=right> 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?
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
\inputminted{antlr}{\projectroot src/pp/s1184725/boppi/antlr/Boppi.g4}
|
|
@ -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.
|
|
@ -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.
|
|
@ -4,3 +4,5 @@
|
||||||
% syntactic, semantic or run-time errors.
|
% 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
|
% All tests should be provided as part of the zip-file. One test programshould be included as an appendix
|
||||||
% in the report (see below).
|
% in the report (see below).
|
||||||
|
|
||||||
|
\inputminted{java}{\projectroot src/pp/s1184725/boppi/test/AllTests.java}
|
|
@ -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}
|
|
@ -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?
|
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
\begin{lstlisting}[style=ANTLR]
|
|
||||||
\end{lstlisting}
|
|
|
@ -1,2 +0,0 @@
|
||||||
% Problems and solutions. Summary of problems you encountered and how you solved them (max. two
|
|
||||||
% pages).
|
|
|
@ -1 +0,0 @@
|
||||||
% Summary of the main features of your programming language (max. 1 page)
|
|
|
@ -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}
|
|
|
@ -31,6 +31,10 @@ public class Annotations {
|
||||||
* A stack with the current function scope on top.
|
* A stack with the current function scope on top.
|
||||||
*/
|
*/
|
||||||
public Stack<Variable<Type>> currentFunction;
|
public Stack<Variable<Type>> currentFunction;
|
||||||
|
/**
|
||||||
|
* A symbol table
|
||||||
|
*/
|
||||||
|
public CachingSymbolTable<Type> symbols;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new annotations object with empty maps.
|
* Creates a new annotations object with empty maps.
|
||||||
|
@ -41,5 +45,6 @@ public class Annotations {
|
||||||
function = new ParseTreeProperty<>();
|
function = new ParseTreeProperty<>();
|
||||||
variables = new ParseTreeProperty<>();
|
variables = new ParseTreeProperty<>();
|
||||||
currentFunction = new Stack<>();
|
currentFunction = new Stack<>();
|
||||||
|
symbols = new CachingSymbolTable<>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ import pp.s1184725.boppi.antlr.BoppiParser.*;
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class BoppiChecker extends BoppiBaseVisitor<Type> {
|
public class BoppiChecker extends BoppiBaseVisitor<Type> {
|
||||||
private CachingSymbolTable<Type> symbols;
|
|
||||||
private Annotations an;
|
private Annotations an;
|
||||||
private Logger log;
|
private Logger log;
|
||||||
|
|
||||||
|
@ -32,17 +31,47 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
|
||||||
* @return the generated annotations
|
* @return the generated annotations
|
||||||
*/
|
*/
|
||||||
public static Annotations checkProgram(ParseTree ast, Logger logger) {
|
public static Annotations checkProgram(ParseTree ast, Logger logger) {
|
||||||
BoppiChecker checker = new BoppiChecker(logger);
|
BoppiChecker checker = new BoppiChecker(logger, new Annotations());
|
||||||
checker.visit(ast);
|
checker.visit(ast);
|
||||||
return checker.an;
|
return checker.an;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected BoppiChecker(Logger logger) {
|
protected BoppiChecker(Logger logger, Annotations annotations) {
|
||||||
symbols = new CachingSymbolTable<>();
|
an = annotations;
|
||||||
an = new Annotations();
|
|
||||||
log = logger;
|
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
|
@Override
|
||||||
public Type visit(ParseTree tree) {
|
public Type visit(ParseTree tree) {
|
||||||
Type type = super.visit(tree);
|
Type type = super.visit(tree);
|
||||||
|
@ -60,14 +89,9 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Type visitBool(BoolContext ctx) {
|
|
||||||
return SimpleType.BOOL;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type visitBlock(BlockContext ctx) {
|
public Type visitBlock(BlockContext ctx) {
|
||||||
return symbols.withScope(() -> visit(ctx.expr()));
|
return an.symbols.withScope(() -> visit(ctx.expr()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -90,23 +114,10 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@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
|
@Override
|
||||||
public Type visitDeclare(DeclareContext ctx) {
|
public Type visitDeclare(DeclareContext ctx) {
|
||||||
try {
|
try {
|
||||||
Variable<Type> var = symbols.put(ctx.IDENTIFIER().getText(), visit(ctx.type()));
|
Variable<Type> var = an.symbols.put(ctx.IDENTIFIER().getText(), visit(ctx.type()));
|
||||||
an.variables.put(ctx, var);
|
an.variables.put(ctx, var);
|
||||||
|
|
||||||
if (var.getType() instanceof TupleType)
|
if (var.getType() instanceof TupleType)
|
||||||
|
@ -126,23 +137,23 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type visitFuncDeclare(FuncDeclareContext ctx) {
|
public Type visitDeclareFunction(DeclareFunctionContext ctx) {
|
||||||
try {
|
try {
|
||||||
TupleType parameterTypes = new TupleType(
|
TupleType parameterTypes = new TupleType(
|
||||||
ctx.type().stream().skip(1).map(this::visit).collect(Collectors.toList()));
|
ctx.type().stream().skip(1).map(this::visit).collect(Collectors.toList()));
|
||||||
FunctionType type = new FunctionType(visit(ctx.result), parameterTypes);
|
FunctionType type = new FunctionType(visit(ctx.result), parameterTypes);
|
||||||
Variable<Type> func = symbols.put(ctx.name.getText(), type);
|
Variable<Type> func = an.symbols.put(ctx.name.getText(), type);
|
||||||
an.variables.put(ctx, func);
|
an.variables.put(ctx, func);
|
||||||
an.currentFunction.push(func);
|
an.currentFunction.push(func);
|
||||||
|
|
||||||
type.setLocalDataSize(symbols.withFunctionScope(() -> {
|
type.setLocalDataSize(an.symbols.withFunctionScope(() -> {
|
||||||
for (int i = 1; i < ctx.type().size(); i++)
|
for (int i = 1; i < ctx.type().size(); i++)
|
||||||
try {
|
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) {
|
} catch (Exception e) {
|
||||||
log.severe(getError(ctx, e.getMessage()));
|
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) {
|
} catch (EmptyStackException e) {
|
||||||
log.severe(getError(ctx, "Declaring function outside a program."));
|
log.severe(getError(ctx, "Declaring function outside a program."));
|
||||||
|
@ -153,17 +164,25 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
|
||||||
return SimpleType.VOID;
|
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
|
@Override
|
||||||
public Type visitIf(IfContext ctx) {
|
public Type visitIf(IfContext ctx) {
|
||||||
return symbols.withScope(() -> {
|
return an.symbols.withScope(() -> {
|
||||||
visit(ctx.cond);
|
visit(ctx.cond);
|
||||||
if (ctx.onFalse != null) {
|
if (ctx.onFalse != null) {
|
||||||
Type trueType = symbols.withScope(() -> visit(ctx.onTrue));
|
Type trueType = an.symbols.withScope(() -> visit(ctx.onTrue));
|
||||||
Type falseType = symbols.withScope(() -> visit(ctx.onFalse));
|
Type falseType = an.symbols.withScope(() -> visit(ctx.onFalse));
|
||||||
|
|
||||||
return (trueType.equals(falseType)) ? trueType : SimpleType.VOID;
|
return (trueType.equals(falseType)) ? trueType : SimpleType.VOID;
|
||||||
} else {
|
} else {
|
||||||
symbols.withScope(() -> visit(ctx.onTrue));
|
an.symbols.withScope(() -> visit(ctx.onTrue));
|
||||||
return SimpleType.VOID;
|
return SimpleType.VOID;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -219,7 +238,17 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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;
|
return SimpleType.INT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,7 +279,7 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
|
||||||
public Type visitProgram(ProgramContext ctx) {
|
public Type visitProgram(ProgramContext ctx) {
|
||||||
FunctionType main = new FunctionType(TupleType.UNIT, TupleType.UNIT);
|
FunctionType main = new FunctionType(TupleType.UNIT, TupleType.UNIT);
|
||||||
an.currentFunction.push(new Variable<Type>(main, 0, 0));
|
an.currentFunction.push(new Variable<Type>(main, 0, 0));
|
||||||
main.setLocalDataSize(symbols.withFunctionScope(() -> super.visitProgram(ctx)));
|
main.setLocalDataSize(an.symbols.withFunctionScope(() -> super.visitProgram(ctx)));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,15 +313,10 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
|
||||||
return visit(ctx.variable());
|
return visit(ctx.variable());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Type visitVar(VarContext ctx) {
|
|
||||||
return visit(ctx.variable());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type visitVariable(VariableContext ctx) {
|
public Type visitVariable(VariableContext ctx) {
|
||||||
try {
|
try {
|
||||||
Variable<Type> var = symbols.get(ctx.getText());
|
Variable<Type> var = an.symbols.get(ctx.getText());
|
||||||
an.variables.put(ctx, var);
|
an.variables.put(ctx, var);
|
||||||
return var.getType();
|
return var.getType();
|
||||||
} catch (EmptyStackException e) {
|
} catch (EmptyStackException e) {
|
||||||
|
@ -304,11 +328,16 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
|
||||||
return SimpleType.VOID;
|
return SimpleType.VOID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type visitVariableExpr(VariableExprContext ctx) {
|
||||||
|
return visit(ctx.variable());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type visitWhile(WhileContext ctx) {
|
public Type visitWhile(WhileContext ctx) {
|
||||||
return symbols.withScope(() -> {
|
return an.symbols.withScope(() -> {
|
||||||
checkConstraint(visit(ctx.cond), SimpleType.BOOL, ctx.cond);
|
checkConstraint(visit(ctx.cond), SimpleType.BOOL, ctx.cond);
|
||||||
symbols.withScope(() -> visit(ctx.onTrue));
|
an.symbols.withScope(() -> visit(ctx.onTrue));
|
||||||
return SimpleType.VOID;
|
return SimpleType.VOID;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -329,35 +358,4 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
|
||||||
return SimpleType.VOID;
|
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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,10 +89,10 @@ public class BoppiGenerator extends BoppiBaseVisitor<Void> {
|
||||||
reg = new Reg("__" + (regInUse.size() + 1));
|
reg = new Reg("__" + (regInUse.size() + 1));
|
||||||
regInUse.add(reg);
|
regInUse.add(reg);
|
||||||
|
|
||||||
if (regInUse.size() == 11)
|
if (regInUse.size() == RECOMMENDED_REGISTER_COUNT + 1)
|
||||||
logger.warning(
|
logger.warning(
|
||||||
String.format("Using more than 10 registers at %d:%d. Consider rebalancing your expressions.",
|
String.format("Using more than %d registers at %d:%d. Consider rebalancing your expressions.",
|
||||||
ctx.start.getLine(), ctx.start.getCharPositionInLine()));
|
RECOMMENDED_REGISTER_COUNT, ctx.start.getLine(), ctx.start.getCharPositionInLine()));
|
||||||
} else {
|
} else {
|
||||||
if (regFree.remove(an.registers.get(ctx)))
|
if (regFree.remove(an.registers.get(ctx)))
|
||||||
reg = an.registers.get(ctx);
|
reg = an.registers.get(ctx);
|
||||||
|
@ -184,42 +184,20 @@ public class BoppiGenerator extends BoppiBaseVisitor<Void> {
|
||||||
return emit(comment, (Label) null, opCode, args);
|
return emit(comment, (Label) null, opCode, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public Void visitBlock(BlockContext ctx) {
|
* Returns an error message for a given parse tree node.
|
||||||
super.visitBlock(ctx);
|
*
|
||||||
copyReg(ctx.expr(), ctx);
|
* @param ctx
|
||||||
return null;
|
* the parse tree node at which the error occurred
|
||||||
}
|
* @param message
|
||||||
|
* the error message
|
||||||
@Override
|
* @param args
|
||||||
public Void visitDeclare(DeclareContext ctx) {
|
* arguments for the message, see {@link String#format}
|
||||||
return null;
|
*/
|
||||||
}
|
private String getError(ParserRuleContext node, String message, Object... args) {
|
||||||
|
int line = node.getStart().getLine();
|
||||||
@Override
|
int column = node.getStart().getCharPositionInLine();
|
||||||
public Void visitParens(ParensContext ctx) {
|
return String.format("Line %d:%d - %s", line, column, String.format(message, args));
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -237,6 +215,13 @@ public class BoppiGenerator extends BoppiBaseVisitor<Void> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Void visitBlock(BlockContext ctx) {
|
||||||
|
super.visitBlock(ctx);
|
||||||
|
copyReg(ctx.expr(), ctx);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Void visitCall(CallContext ctx) {
|
public Void visitCall(CallContext ctx) {
|
||||||
String base = "call " + ctx.variable().getText() + " - ";
|
String base = "call " + ctx.variable().getText() + " - ";
|
||||||
|
@ -306,7 +291,12 @@ public class BoppiGenerator extends BoppiBaseVisitor<Void> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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() + " - ";
|
String base = "define " + ctx.IDENTIFIER(0).getText() + " - ";
|
||||||
Label skip = new Label("s" + ctx.hashCode());
|
Label skip = new Label("s" + ctx.hashCode());
|
||||||
emit(base + "jump over body", OpCode.jumpI, skip);
|
emit(base + "jump over body", OpCode.jumpI, skip);
|
||||||
|
@ -325,14 +315,52 @@ public class BoppiGenerator extends BoppiBaseVisitor<Void> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Void visitBool(BoolContext ctx) {
|
public Void visitExpr(ExprContext ctx) {
|
||||||
emit(ctx.getText(), OpCode.loadI, new Num(ctx.TRUE() != null ? 1 : 0), makeReg(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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Void visitNumber(NumberContext ctx) {
|
public Void visitIf(IfContext ctx) {
|
||||||
emit(ctx.getText(), OpCode.loadI, new Num(Integer.parseInt(ctx.LITERAL10().getText())), makeReg(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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,6 +409,32 @@ public class BoppiGenerator extends BoppiBaseVisitor<Void> {
|
||||||
return null;
|
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
|
@Override
|
||||||
public Void visitPrefix1(Prefix1Context ctx) {
|
public Void visitPrefix1(Prefix1Context ctx) {
|
||||||
visitChildren(ctx);
|
visitChildren(ctx);
|
||||||
|
@ -389,27 +443,20 @@ public class BoppiGenerator extends BoppiBaseVisitor<Void> {
|
||||||
switch (ctx.op.getType()) {
|
switch (ctx.op.getType()) {
|
||||||
case BoppiLexer.MINUS:
|
case BoppiLexer.MINUS:
|
||||||
emit("unary -", OpCode.rsubI, getReg(ctx), ZERO, getReg(ctx));
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Void visitVar(VarContext ctx) {
|
public Void visitProgram(ProgramContext ctx) {
|
||||||
if (SimpleType.CHAR.equals(an.types.get(ctx)))
|
emit("initialise temp ARP", OpCode.loadI, ZERO, tempArp);
|
||||||
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;
|
return visitChildren(ctx);
|
||||||
}
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -494,39 +541,13 @@ public class BoppiGenerator extends BoppiBaseVisitor<Void> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Void visitIf(IfContext ctx) {
|
public Void visitVariableExpr(VariableExprContext ctx) {
|
||||||
Label toTrue = new Label("if_t" + ctx.hashCode());
|
if (SimpleType.CHAR.equals(an.types.get(ctx)))
|
||||||
Label toFalse = new Label("if_f" + ctx.hashCode());
|
emit("var " + ctx.variable().getText(), OpCode.cloadAI, getVar(ctx.variable()),
|
||||||
Label toEnd = new Label("if_e" + ctx.hashCode());
|
new Num(an.variables.get(ctx.variable()).getOffset()), makeReg(ctx));
|
||||||
|
else
|
||||||
visit(ctx.cond);
|
emit("var " + ctx.variable().getText(), OpCode.loadAI, getVar(ctx.variable()),
|
||||||
if (ctx.onFalse == null) {
|
new Num(an.variables.get(ctx.variable()).getOffset()), makeReg(ctx));
|
||||||
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -588,20 +609,4 @@ public class BoppiGenerator extends BoppiBaseVisitor<Void> {
|
||||||
}
|
}
|
||||||
return null;
|
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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
/Boppi.tokens
|
||||||
|
/BoppiBaseVisitor.java
|
||||||
|
/BoppiLexer.java
|
||||||
|
/BoppiLexer.tokens
|
||||||
|
/BoppiParser.java
|
||||||
|
/BoppiVisitor.java
|
|
@ -11,22 +11,22 @@ singleExpr
|
||||||
| BRAOPEN expr BRACLOSE #block
|
| BRAOPEN expr BRACLOSE #block
|
||||||
| IN PAROPEN variable (LISTDELIM variable)* PARCLOSE #read
|
| IN PAROPEN variable (LISTDELIM variable)* PARCLOSE #read
|
||||||
| OUT PAROPEN expr (LISTDELIM expr)* PARCLOSE #write
|
| 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
|
| IFOPEN cond=expr IFTRUE onTrue=expr (IFFALSE onFalse=expr)? IFCLOSE #if
|
||||||
| WHILEOPEN cond=expr WHILETRUE onTrue=expr WHILECLOSE #while
|
| WHILEOPEN cond=expr WHILETRUE onTrue=expr WHILECLOSE #while
|
||||||
| op=(PLUS|MINUS|NOT) singleExpr #prefix1
|
| op=(PLUS|MINUS|NOT) singleExpr #prefix1
|
||||||
| lhs=singleExpr op=(MULTIPLY|DIVIDE|MODULO) rhs=singleExpr #infix1
|
| lhs=singleExpr op=(MULTIPLY|DIVIDE) rhs=singleExpr #infix1
|
||||||
| lhs=singleExpr op=(PLUS|MINUS) rhs=singleExpr #infix2
|
| lhs=singleExpr op=(PLUS|MINUS) rhs=singleExpr #infix2
|
||||||
| lhs=singleExpr op=(LT|LEQ|GTE|GT|EQ|NEQ) rhs=singleExpr #infix3
|
| lhs=singleExpr op=(LT|LEQ|GTE|GT|EQ|NEQ) rhs=singleExpr #infix3
|
||||||
| lhs=singleExpr AND rhs=singleExpr #infix4
|
| lhs=singleExpr AND rhs=singleExpr #infix4
|
||||||
| lhs=singleExpr OR rhs=singleExpr #infix5
|
| lhs=singleExpr OR rhs=singleExpr #infix5
|
||||||
| DECLARE type IDENTIFIER #declare
|
| DECLARE type IDENTIFIER #declare
|
||||||
| FUNCTION result=type name=IDENTIFIER PAROPEN (type IDENTIFIER (LISTDELIM type IDENTIFIER)*)? PARCLOSE body=singleExpr #funcDeclare
|
|
||||||
| <assoc=right> variable ASSIGN singleExpr #assign
|
| <assoc=right> variable ASSIGN singleExpr #assign
|
||||||
| variable #var
|
| variable #variableExpr
|
||||||
| LITERAL10 #number
|
| LITERAL10 #literalInteger
|
||||||
| CHAR #char
|
| CHAR #literalCharacter
|
||||||
| (TRUE|FALSE) #bool
|
| (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
|
||||||
|
@ -35,6 +35,7 @@ type
|
||||||
| PAROPEN (type (LISTDELIM type )*)? PARCLOSE #typeTuple
|
| PAROPEN (type (LISTDELIM type )*)? PARCLOSE #typeTuple
|
||||||
| variable #typeVariable
|
| variable #typeVariable
|
||||||
;
|
;
|
||||||
|
|
||||||
variable: IDENTIFIER;
|
variable: IDENTIFIER;
|
||||||
|
|
||||||
PAROPEN: '(';
|
PAROPEN: '(';
|
||||||
|
@ -56,7 +57,6 @@ MINUS: '-';
|
||||||
NOT: '!';
|
NOT: '!';
|
||||||
MULTIPLY: '*';
|
MULTIPLY: '*';
|
||||||
DIVIDE: '/';
|
DIVIDE: '/';
|
||||||
MODULO: '%';
|
|
||||||
LEQ: '<=';
|
LEQ: '<=';
|
||||||
GTE: '>=';
|
GTE: '>=';
|
||||||
NEQ: '<>';
|
NEQ: '<>';
|
||||||
|
|
|
@ -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)
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in New Issue