download 2021 site
This commit is contained in:
commit
dd6e4afb13
138 changed files with 37730 additions and 0 deletions
659
lab/8.html
Normal file
659
lab/8.html
Normal file
|
|
@ -0,0 +1,659 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="Eelco Visser">
|
||||
<meta name="generator" content="Jekyll v3.8.5">
|
||||
<title>Lab 8: Simple Transformation</title>
|
||||
<!-- <base href="/2021"> -->
|
||||
|
||||
<!--link rel="canonical" href="https://getbootstrap.com/docs/4.3/examples/starter-template/"-->
|
||||
|
||||
<link rel="icon" href="../img/logo/pl_ico2_2B3_icon.ico" type="image/x-icon">
|
||||
|
||||
<!-- Bootstrap core CSS -->
|
||||
<!--link href="https://getbootstrap.com/docs/4.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"-->
|
||||
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
|
||||
|
||||
|
||||
|
||||
<style>
|
||||
.bd-placeholder-img {
|
||||
font-size: 1.125rem;
|
||||
text-anchor: middle;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.bd-placeholder-img-lg {
|
||||
font-size: 3.5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<!-- Custom styles for this template -->
|
||||
<link href="../css/main.css" rel="stylesheet">
|
||||
<link href="../css/borders-responsive.css" rel="stylesheet">
|
||||
|
||||
<link rel="stylesheet" href="../css/pl.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
|
||||
|
||||
<a class="navbar-brand" href="../index.html">
|
||||
TU Delft | CS4200
|
||||
</a>
|
||||
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault" aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarsExampleDefault">
|
||||
<ul class="navbar-nav mr-auto">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="../lectures/index.html" tabindex="-1" aria-disabled="true">Lectures</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="../homework/index.html" tabindex="-1" aria-disabled="true">Homework</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="../project/index.html" tabindex="-1" aria-disabled="true">Project</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="../news/index.html" tabindex="-1" aria-disabled="true">News</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="../blog/index.html" tabindex="-1" aria-disabled="true">Blog</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12 col-lg-12 col-xl-12">
|
||||
|
||||
|
||||
<div class="mt-3 mb-3 pt-3 pb-3 text-dark border-top border-bottom border-grey">
|
||||
<div class="text-dark font-weight-bold">
|
||||
<h1>
|
||||
|
||||
Lab 8: Simple Transformation
|
||||
|
||||
</h1>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
|
||||
</div>
|
||||
<div>
|
||||
|
||||
|
||||
Project
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
<div>
|
||||
November 12, 2021
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12 col-lg-9 border-lg-right border-grey">
|
||||
<p>During this lab get to know Stratego by making the <a href="https://weblab.tudelft.nl/cs4200/2021-2022/assignment/88076/view">homework assignments for Week 1</a> either in Spoofax 3 or in WebLab.</p>
|
||||
|
||||
<!--
|
||||
|
||||
This lab is your first encounter with Stratego.
|
||||
You add an outline view and a desugaring transformation to an initial editor provided by us.
|
||||
|
||||
|
||||
### Objectives
|
||||
|
||||
1. Specify rewrite rules `to-outline-label` which map AST nodes to labels in an outline view. You should include:
|
||||
* classes (class name and, if available, parent class name),
|
||||
* fields (field name and type),
|
||||
* methods (method name, parameter types, return type) and
|
||||
* local variables (variable name and type).
|
||||
2. Define rewrite rules `desugar` which desugar
|
||||
* unary expressions into terms of the form `UnExp(op, exp)`,
|
||||
* binary expressions into terms of the form `BinExp(op, exp1, exp2)` and
|
||||
3. Integrate `desugar` into a strategy `desugar-all` which desugars subtrees in an AST.
|
||||
|
||||
For grading, it is required to comply with all constructor names, rule names, and strategy names literally.
|
||||
|
||||
<!-- ### Submission
|
||||
|
||||
You need to submit your ChocoPy project with a merge request against branch `assignment-3-submission` on GitLab.
|
||||
The [Git documentation](/project/lab1d) explains how to file such a request. -->
|
||||
|
||||
<!-- ### Early Feedback
|
||||
|
||||
We provide early feedback for the correctness of your outline and desugarings.
|
||||
This feedback is a score out of 90 points; the strategy choice (10 points) is graded manually.
|
||||
You have 3 early feedback attempts. -->
|
||||
|
||||
<!--
|
||||
### Preliminaries
|
||||
|
||||
#### GitLab Repository
|
||||
|
||||
We provide you with a template for this assignment.
|
||||
See the [Git documentation](/2021/lab/1d) on how to check out this branch.
|
||||
Make sure to read the `README.md` in the root of the repository, as it contains further instructions that are specific to this template.
|
||||
|
||||
#### Anatomy of a Spoofax Project
|
||||
|
||||
Until now, you mainly worked on files in the `syntax` folder of your project.
|
||||
During this lab you will also edit files in the `trans` folder.
|
||||
So, this is a good point to talk about the general structure of a Spoofax project.
|
||||
Note: this general structure does not apply to the template we've given you for the project, considering it contains a compiled version of the front-end already.
|
||||
|
||||
First of all, every Spoofax project may be part of an Eclipse plug-in project.
|
||||
This allows you to deploy your editor as a plugin using the Eclipse update site mechanism.
|
||||
Users do not need to have Spoofax installed for using your editor.
|
||||
|
||||
The actual language definition is spread over three folders:
|
||||
|
||||
* `syntax` contains all syntax definition files, including the main file `chocopy.sdf3`.
|
||||
* `trans` contains all transformation files, including the main file `chocopy.str`.
|
||||
* `editor` contains editor service definition files, including the main file `Main.esv`.
|
||||
|
||||
In the `src-gen` folder, you will find files which were generated from your syntax definition.
|
||||
For each file `<name>.sdf3`, there are generated files:
|
||||
|
||||
* `syntax/<name>.sdf`: an SDF2 definition which is equivalent to the SDF3 definition.
|
||||
* `completions/<name>-cp.str`: abstract placeholder expansions for syntactic code completion derived from SDF3 templates.
|
||||
* `completions/colorer/<name>-cc-esv.esv`: instructions to color placeholders for syntactic completions in gray.
|
||||
* `pp/<name>-pp.str`: pretty-printing strategies derived from SDF3 templates.
|
||||
* `signatures/<name>-sig.str`: signatures derived from SDF3 templates.
|
||||
|
||||
You can find more generated files in `src-gen` and `target/metaborg` folders:
|
||||
|
||||
* `src-gen/syntax/ChocoPy.def`: your complete syntax definition in SDF2.
|
||||
* `src-gen/syntax/ChocoPy-Permissive.def`: a permissive version of the syntax definition, which supports error recovery.
|
||||
* `target/metaborg/ChocoPy.tbl`: the parse table of your language.
|
||||
* `src-gen/pp/ChocoPy-parenthesize.str`: strategies to add parentheses to an AST according to the priorities of your language.
|
||||
* `target/metaborg/stratego.ctree` and/or `target/metaborg/stratego.jar`: compiled Stratego code of your language.
|
||||
|
||||
#### Initial Editor Project
|
||||
|
||||
We provide you with an initial ChocoPy project in the branch `milestone-3-template`.
|
||||
This project is a common starting point for all of you.
|
||||
It includes:
|
||||
|
||||
* A compiled version of a front-end ChocoPy project.
|
||||
* corresponding signatures `reference/src-gen/signatures/*-sig.str`,
|
||||
* pretty-printing definitions `reference/src-gen/pp/*-pp.str`, and a
|
||||
* content-completion definitions `reference/src-gen/completion/*-cp.str` (errors in the completion files can be ignored).
|
||||
|
||||
During the building of the project the contents of `reference` will be copied over to `src-gen`.
|
||||
So, when importing any module from the reference, you can just use the 'local' variant.
|
||||
I.e. instead of importing `reference/src-gen/signatures/chocopy-sig`, please import `signatures/chocopy-sig`.
|
||||
|
||||
#### Signature
|
||||
|
||||
Signatures declare sorts and constructors for terms.
|
||||
In Spoofax, terms are used to represent abstract syntax trees.
|
||||
The corresponding signature is generated from the constructors in a syntax definition.
|
||||
You can find the signature for ChocoPy in the files `reference/src-gen/signatures/*-sig.str`.
|
||||
The signature was generated from a syntax definition, which itself is not included in the initial project.
|
||||
If you write your own syntax definition, the generated signatures can be found in `src-gen/signatures/`.
|
||||
|
||||
### Outline View
|
||||
|
||||
#### Rewrite Rules
|
||||
|
||||
An outline view can be specified by rewrite rules `to-outline-label` in `trans/outline.str`.
|
||||
These rules should rewrite AST nodes to their label in an outline view.
|
||||
For example, the following rule rewrites a variable declaration to its name, which will be used as a label.
|
||||
|
||||
```
|
||||
rules
|
||||
|
||||
to-outline-label: Var(t, v) -> v
|
||||
```
|
||||
|
||||
On the left-hand side, the rule matches a variable declaration.
|
||||
During the match, variables `t` and `v` are bound to actual terms.
|
||||
On the right-hand side, the rule instantiates a label.
|
||||
During the instantiation, variable `v` is replaced with the term it is bound to.
|
||||
You can extend `to-outline-label` to provide labels for
|
||||
|
||||
* class declarations,
|
||||
* field declarations and
|
||||
* method declarations.
|
||||
|
||||
When you build the project and open a ChocoPy file, you will get an outline of this program in the outline view.
|
||||
In case you do not see any outline view, you can select it in *Show View* from Eclipse's *Window* menu.
|
||||
|
||||
#### Naming Conventions
|
||||
|
||||
In Stratego, we use the following naming conventions:
|
||||
|
||||
* constructor and sort names: camel case, starting with an upper case (e.g. `Add`, `BinExp`)
|
||||
* rule names, strategy names, variable names: lower case, multiple words separated by `-` (e.g. `e1`, `project-path`)
|
||||
|
||||
#### String Interpolation
|
||||
|
||||
In many cases, you want to provide more information than just the name.
|
||||
For example, you might want to show not only a variable's name, but also it's type.
|
||||
The following rule achieves this:
|
||||
|
||||
```
|
||||
to-outline-label:
|
||||
Var(t, v) -> label
|
||||
where
|
||||
t' := <pp-partial-ChocoPy-string> t
|
||||
; label := <concat-strings> [v, ": ", t']
|
||||
```
|
||||
|
||||
On its right-hand side, it produces a `label`, which is bound in the `where` clause.
|
||||
First, the term bound to `t` is turned into a string bound to `t'` by applying a strategy `pp-partial-ChocoPy-string`.
|
||||
This strategy is defined in `ChocoPy/trans/pp.str` and uses the pretty-printing rules generated from our SDF3 grammar for ChocoPy.
|
||||
|
||||
Next, the label is bound to the concatenation of
|
||||
the string bound to `v`,
|
||||
a constant string `": "`,
|
||||
and the string bound to `t'`.
|
||||
|
||||
String concatenation is not very intuitive.
|
||||
Instead, you can also use string interpolation:
|
||||
|
||||
```
|
||||
to-outline-label:
|
||||
Var(t, v) -> $[[v]: [t']]
|
||||
where
|
||||
t' := <pp-partial-ChocoPy-string> t
|
||||
```
|
||||
|
||||
String interpolation allows you to combine text with variables.
|
||||
Text is enclosed in `$[` and `]`, while variables inside the text are enclosed in `[` and `]`.
|
||||
These variables need to be bound to strings.
|
||||
|
||||
You should provide the following information in your outline labels:
|
||||
|
||||
* class name and super class name
|
||||
* field name and type
|
||||
* method name, parameter types (not parameter names), return type
|
||||
* variable name and type
|
||||
|
||||
For parameter types, you need to turn a list of parameters into a string.
|
||||
You can do this with a recursive strategy:
|
||||
|
||||
```
|
||||
pp-params: // empty parameter list
|
||||
[] -> ...
|
||||
|
||||
pp-params: // single parameter
|
||||
[TypedVar(p, t)] -> ...
|
||||
|
||||
pp-params: // at least two parameters
|
||||
[TypedVar(p, t), param | params] -> ...
|
||||
where
|
||||
// do something on first parameter
|
||||
...
|
||||
// recursive call on remaining parameters
|
||||
... := <pp-params> [param | params]
|
||||
```
|
||||
|
||||
For grading, it is required to define a strategy `pp-params` that always rewrites a list of `TypedVar`s to a *string*.
|
||||
{: .notice .notice-warning}
|
||||
|
||||
Your current outline view is missing a root node.
|
||||
You can add a root node by providing a label for programs.
|
||||
|
||||
#### Annotations
|
||||
|
||||
In Stratego, terms can be annotated with additional information.
|
||||
The Spoofax outline view uses annotations to determine the icon of a node.
|
||||
You can specify the icon to use in an annotation:
|
||||
|
||||
```
|
||||
to-outline-label:
|
||||
Var(t, v) -> label{icon}
|
||||
where
|
||||
t' := <pp-partial-ChocoPy-string> t
|
||||
; label := $[[v]: [t']]
|
||||
; icon := "icons/var.gif"
|
||||
```
|
||||
|
||||
We do not require you to use icons and you will not earn any points with them.
|
||||
If you want to use them anyway, you should put the icons into the folder `icons`
|
||||
and place a proper attribution or license file next to them.
|
||||
|
||||
#### Challenge
|
||||
|
||||
Challenges are meant to distinguish excellent solutions from good solutions.
|
||||
Typically, they are less guided and require more investigation or higher programming skills.
|
||||
{: .notice .notice-success}
|
||||
|
||||
|
||||
1. Provide the file name as the root node label. In `outline.str`: import, command/control click and study `libspoofax/term/origin` for a suitable strategy.
|
||||
|
||||
2. Outline the main method as a subnode of the main class.
|
||||
You need to change this strategy in the `outline.str` file:
|
||||
|
||||
```
|
||||
outline := <custom-label-outline(to-outline-label, to-outline-node)> ast
|
||||
```
|
||||
|
||||
Also import, command/control click, and investigate `libspoofax/editor/outline` for inspiration.
|
||||
|
||||
3. Use one of the library strategies for folding to implement `pp-params`. You can find various folding strategies in the [API docs](http://releases.strategoxt.org/docs/api/libstratego-lib/stable/docs/).
|
||||
|
||||
### Desugaring
|
||||
|
||||
A uniform representation of unary and binary expressions eases static analysis and code generation.
|
||||
To get such a uniform representation, you need to desugar abstract syntax trees during the analysis phase.
|
||||
|
||||
#### Signature
|
||||
|
||||
Before you can implement a desugaring,
|
||||
you need to define a signature for the uniform representation of expressions in `trans/desugar.str`:
|
||||
|
||||
1. Identify unary and binary expressions in ChocoPy.
|
||||
A unary expression has one subexpression and an operator.
|
||||
A binary expression has two subexpressions and an operator.
|
||||
There are more than one kind of unary expressions and more than three kinds of binary expressions in ChocoPy.
|
||||
2. Specify new constants for unary and binary operators in a signature.
|
||||
Use `UnOp` and `BinOp` as types of these operators.
|
||||
Again, you should use names based on the semantics of an operator, not on its syntax.
|
||||
Reading an expression aloud might help you to find suitable constructor names.
|
||||
3. Define constructors `UnExp` and `BinExp`,
|
||||
which combine an operator and an expression (respectively two expressions) to an expression.
|
||||
|
||||
#### Rewrite Rules
|
||||
|
||||
The following rewrite rule defines a rule to desugar an addition:
|
||||
|
||||
```
|
||||
rules
|
||||
|
||||
desugar: Add(e1, e2) -> BinExp(Plus(), e1, e2)
|
||||
```
|
||||
|
||||
This rewrite rule is named `desugar`.
|
||||
On the left-hand side, the rule matches an addition.
|
||||
During the match, variables `e1` and `e2` are bound to actual terms.
|
||||
On the right-hand side, the rule instantiates a binary expression (in a uniform representation).
|
||||
During the instantiation, variables `e1` and `e2` are replaced with the terms they are bound to.
|
||||
You can extend `desugar` to replace the different unary and binary expressions in the abstract syntax tree
|
||||
with a uniform representation of these expressions.
|
||||
Define a rewrite rule `desugar` in `trans/desugar.str` for every unary or binary operator,
|
||||
which transforms the original expression into a uniform representation.
|
||||
|
||||
#### Editor Integration
|
||||
|
||||
To test your transformation, you need to define a builder.
|
||||
This is done similar to the builder for pretty-printing.
|
||||
First, import the `desugar` module (defined in `trans/desugar.str`) into the main `chocopy` module.
|
||||
Then, add the following rewrite rule to `trans/chocopy.str`:
|
||||
|
||||
```
|
||||
editor-desugar:
|
||||
(_, _, ast, path, project-path) -> (filename, text)
|
||||
where
|
||||
filename := <guarantee-extension(|"desugared.aterm")> path ;
|
||||
text := <desugar-all> ast
|
||||
```
|
||||
|
||||
This rule follows Spoofax' convention for strategies which implement editor services.
|
||||
On the left-hand site, it matches a tuple of
|
||||
|
||||
* the first two parts are ignored
|
||||
* the `path` of the current file and
|
||||
* the `project path`.
|
||||
|
||||
On the right-hand site, it instantiates a pair, consisting of a `filename` and the designated `text` of the file.
|
||||
Both variables are bound in the `where` clause.
|
||||
The file name is derived from the path of the current file,
|
||||
while the content of the file is a desugared version of the selected AST node.
|
||||
You also need to hook your strategy into the editor, making desugaring available in the *Syntax* menu.
|
||||
You can do this in `editor/Syntax.esv` under the *Show Parsed AST* action:
|
||||
|
||||
```
|
||||
action : "Show desugared syntax" = editor-desugar (source)
|
||||
```
|
||||
|
||||
This rule defines
|
||||
|
||||
* a builder,
|
||||
* its label in the *Syntax* menu, and
|
||||
* its implementation strategy `editor-desugar`.
|
||||
|
||||
Annotations can be used for different variants of builders:
|
||||
|
||||
* `(openeditor)` from the Syntax menu ensures that a new editor window is opened for the result.
|
||||
* Finally, `(source)` tells Spoofax to run the builder on an unanalysed (and also not desugared) AST.
|
||||
|
||||
Note that the `editor-desugar` rule uses `desugar-all`. You should implement this strategy as explained in the next section. Until you do, the menu option will not work.
|
||||
{: .notice .notice-warning}
|
||||
|
||||
|
||||
#### Strategies
|
||||
|
||||
Rewrite rules typically define local transformations inside an AST.
|
||||
Rewrite rules with the same name define a strategy of this name.
|
||||
Furthermore, strategies can be defined to orchestrate rewrite rules to complex transformations of complete ASTs.
|
||||
A strategy consists of a name and a definition, which is typically a combination of strategy applications.
|
||||
For example, the following strategy orchestrates local desugarings to a desugaring of complete ASTs:
|
||||
|
||||
```
|
||||
strategies
|
||||
|
||||
desugar-all = innermost(desugar)
|
||||
```
|
||||
|
||||
This strategy is named `desugar-all`.
|
||||
It applies local `desugar` rules.
|
||||
The application is guided by a generic traversal strategy `innermost`,
|
||||
which tries to apply its parameter inside a tree, starting at the leaves (bottom-up, left-to-right).
|
||||
Whenever an application is successful, the result is traversed again.
|
||||
|
||||
Same results can be achieved with different generic traversals.
|
||||
You should try different traversals in `trans/desugar.str`:
|
||||
|
||||
* `desugar-all = innermost(desugar)`
|
||||
* `desugar-all = topdown(desugar)`
|
||||
* `desugar-all = topdown(try(desugar))`
|
||||
* `desugar-all = bottomup(desugar)`
|
||||
* `desugar-all = bottomup(try(desugar))`
|
||||
* `desugar-all = alltd(desugar)`
|
||||
|
||||
Try to understand what is going on and decide for a suitable one.
|
||||
You can use the library strategy `debug` to print the currently visited node.
|
||||
For example, `innermost(debug; desugar)` will `debug` all nodes before it tries to `desugar` them.
|
||||
|
||||
As preparation for the exam, provide an explanation of
|
||||
1. the choice you made,
|
||||
2. why this choice is suitable for this project, and
|
||||
3. why other choices would be less suitable.
|
||||
|
||||
Try changing the `editor-desugar` rule to use `desugar-all` instead of `desugar`. If you chose a suitable strategy, the builder should succeed even when no node is selected in the ChocoPy program.
|
||||
|
||||
-->
|
||||
|
||||
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-12 col-lg-3 col-xl-3 " >
|
||||
<div class="sticky-top top70 d-none d-lg-block d-xl-block">
|
||||
<div class="pb4 mb3 border-bottom border-grey ">
|
||||
<nav>
|
||||
<ul class="pagination justify-content-left">
|
||||
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="../milestone/2.html">
|
||||
«
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="../project/index.html">
|
||||
^
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="9.html">»</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="border-top border-bottom border-grey mt-3 pt-3">
|
||||
<nav>
|
||||
<ul class="pagination justify-content-center">
|
||||
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="../milestone/2.html">
|
||||
Previous
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="9.html">Next</a>
|
||||
</li>
|
||||
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="../project/index.html">
|
||||
Index
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- Optional JavaScript -->
|
||||
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
|
||||
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
|
||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Add table
Add a link
Reference in a new issue