reordered methods, started report

This commit is contained in:
User 2017-07-21 11:38:24 +02:00
parent a1257c237b
commit 610611e212
22 changed files with 493 additions and 313 deletions

9
doc/.gitignore vendored
View File

@ -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

156
doc/report-description.tex Normal file
View File

@ -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?

1
doc/report-grammar.tex Normal file
View File

@ -0,0 +1 @@
\inputminted{antlr}{\projectroot src/pp/s1184725/boppi/antlr/Boppi.g4}

15
doc/report-problems.tex Normal file
View File

@ -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.

13
doc/report-summary.tex Normal file
View File

@ -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.

View File

@ -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}

54
doc/report.tex Normal file
View File

@ -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}

View File

@ -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?

View File

@ -1,2 +0,0 @@
\begin{lstlisting}[style=ANTLR]
\end{lstlisting}

View File

@ -1,2 +0,0 @@
% Problems and solutions. Summary of problems you encountered and how you solved them (max. two
% pages).

View File

@ -1 +0,0 @@
% Summary of the main features of your programming language (max. 1 page)

View File

@ -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}

View File

@ -31,6 +31,10 @@ public class Annotations {
* A stack with the current function scope on top.
*/
public Stack<Variable<Type>> currentFunction;
/**
* A symbol table
*/
public CachingSymbolTable<Type> 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<>();
}
}

View File

@ -17,7 +17,6 @@ import pp.s1184725.boppi.antlr.BoppiParser.*;
*
*/
public class BoppiChecker extends BoppiBaseVisitor<Type> {
private CachingSymbolTable<Type> symbols;
private Annotations an;
private Logger log;
@ -32,17 +31,47 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
* @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<Type> {
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<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
public Type visitDeclare(DeclareContext ctx) {
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);
if (var.getType() instanceof TupleType)
@ -126,23 +137,23 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
}
@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<Type> func = symbols.put(ctx.name.getText(), type);
Variable<Type> 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<Type> {
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<Type> {
}
@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<Type> {
public Type visitProgram(ProgramContext ctx) {
FunctionType main = new FunctionType(TupleType.UNIT, TupleType.UNIT);
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;
}
@ -284,15 +313,10 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
return visit(ctx.variable());
}
@Override
public Type visitVar(VarContext ctx) {
return visit(ctx.variable());
}
@Override
public Type visitVariable(VariableContext ctx) {
try {
Variable<Type> var = symbols.get(ctx.getText());
Variable<Type> 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<Type> {
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<Type> {
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));
}
}

View File

@ -89,10 +89,10 @@ public class BoppiGenerator extends BoppiBaseVisitor<Void> {
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<Void> {
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<Void> {
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<Void> {
}
@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<Void> {
}
@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<Void> {
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<Void> {
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<Void> {
}
@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<Void> {
}
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));
}
}

View File

@ -0,0 +1,6 @@
/Boppi.tokens
/BoppiBaseVisitor.java
/BoppiLexer.java
/BoppiLexer.tokens
/BoppiParser.java
/BoppiVisitor.java

View File

@ -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
| <assoc=right> 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
| <assoc=right> 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: '<>';

26
util/PygmentBoppiLexer.py Normal file
View File

@ -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)
]
}