commit 13c1ed7b2a594f750069813e76e00bba52fb11c7 Author: Yehowshua Immanuel Date: Wed Feb 12 15:54:12 2025 -0500 first commit diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..ab8fbef --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,17 @@ +name: Build and deploy GH Pages +on: + push: + branches: + - master + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@v3.0.0 + - name: build_and_deploy + uses: shalzz/zola-deploy-action@v0.17.2 + env: + PAGES_BRANCH: gh-pages + TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4139cd5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea/ +public diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..93ab851 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Vincent Prouillet + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..70ba0ab --- /dev/null +++ b/README.md @@ -0,0 +1,54 @@ +# book + +A theme based on [Gitbook](https://www.gitbook.com), to write documentation +or books. + +![book screenshot](https://github.com/Keats/book/blob/master/screenshot.png?raw=true) + + +## Contents + +- [Installation](#installation) +- [Options](#options) + - [Numbered chapters](#numbered-chapters) + +## Installation +First download this theme to your `themes` directory: + +```bash +$ cd themes +$ git clone https://github.com/getzola/book.git +``` +and then enable it in your `config.toml`: + +```toml +theme = "book" +# Optional, if you want search +build_search_index = true +``` + +## Usage +Book will generate a book from the files you place in the `content` directory. Your book +can have two levels of hierarchy: chapters and subchapters. + +Each chapter should be a `section` within the Gutenberg site and should have an `_index.md` +file that sets its `weight` front-matter variable to its chapter number. For example, +chapter 2 should have `weight = 2`. Additionally, each chapter should also set the +`sort_by = "weight"` in its front matter. + +Each subchapter should be a `page` and should have its `weight` variable set to the subchapter +number. For example, subchapter 3.4 should have `weight = 4`. + +Finally, you should create an `_index.md` file and set the `redirect_to` front-matter variable +to redirect to the first section of your content. For example, if your first section has the +slug `introduction`, then you would set `redirect_to = "introduction"`. + +## Options + +### Numbered chapters +By default, the `book` theme will number the chapters and pages in the left menu. +You can disable that by setting the `book_number_chapters` in `extra`: + +```toml +book_number_chapters = false +``` diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..bd9f87f --- /dev/null +++ b/config.toml @@ -0,0 +1,12 @@ +base_url = "https://getzola.github.io/book/" +compile_sass = true +title = "book theme" +description = "A book theme" +build_search_index = true + +[markdown] +highlight_code = true +highlight_theme = "css" + +[extra] +book_number_chapters = true diff --git a/content/_index.md b/content/_index.md new file mode 100644 index 0000000..6abf22b --- /dev/null +++ b/content/_index.md @@ -0,0 +1,3 @@ ++++ +redirect_to = "chapter1" ++++ diff --git a/content/chapter1/_index.md b/content/chapter1/_index.md new file mode 100644 index 0000000..4a16897 --- /dev/null +++ b/content/chapter1/_index.md @@ -0,0 +1,152 @@ ++++ +title = "Introduction" +weight = 1 +sort_by = "weight" +insert_anchor_links = "right" ++++ + +BH (Bluespec Haskell/Classic) is a language for hardware design. The +language borrows its notation, type and package system from an existing +general-purpose functional programming language called Haskell +[@haskell12] where those constructs have been well tested for over a +decade. Unlike Haskell, BH is meant solely for hardware design--- a BH +program represents a circuit. The abstract model for these circuits is a +Term Rewriting System (TRS); details about using TRSs for describing +circuits, and compiling these descriptions to real hardware, may be +found in James Hoe's thesis [@jhoe]. BH has several restrictions and +extensions relative to Haskell, arising out of this hardware focus. + + +This document is not meant as a tutorial on BH (separate documents exist +for that purpose). Nevertheless, this document has numerous small +examples to explicate BH notation. + + +## Meta notation + + +The grammar rules in the presentation below mostly follow the usual EBNF +(Extended BNF) structure. Grammar alternatives are separated by +"$\mid$". Items enclosed in \[ \] are optional. Items enclosed in { } +can be repeated zero or more times. The last piece of notation is used +sloppily; sometimes there must be at least one item, and also, the last +terminal inside the { } is sometimes a separator rather than terminator. + + +## Identifiers and the rôle of upper and lower case + + +An identifier in BH consists of a letter followed by zero or more +letters, digits, underscores and single quotes. Identifiers are case +sensitive: `glurph`, `gluRph` and `Glurph` are three distinct +identifiers. + + +The case of the first letter in an identifier is very important. If the +first letter is lower case, the identifier is a "variable identifier", +referred to in the grammar rules as a *varId*. If the first letter is +upper case, the identifier is a "constructor identifier", referred to in +the grammar rules as a *conId*. + + +In BH, package names (*packageId*), type names (*tycon*) and value +constructor names are all constructor identifiers. (Ordinary) variables, +field names and type variables are all variable identifiers. + + +A lone underscore, "` `", is treated as a special identifier--- it is +used as a "don't care" pattern or expression (more details in Sections +[5.10](fixme) and +[6.1](fixme)). + + +## The Standard Prelude + + +The Standard Prelude is a predefined package that is imported implicitly +into every BH package. It contains a number of useful predefined +entities (types, values/functions, classes, instances, etc.). It is +somewhat analogous to the combination of various ".h" files and standard +libraries in C, except that in BH no special action is needed to import +the prelude or to link it in. We will refer to the prelude periodically +in the following sections, and there are more details in appendix +[15](fixme). + + +## Lexical syntax/layout + + +In BH, there are various syntactic constructs that involve zero or more +items enclosed in braces and separated by semicolons: These braces and semicolons can be omitted +entirely if the components are laid out with proper indentation. + + +Suppose the parser discovers a missing open brace (e.g., after the +keywords `where`, `let`, `do` and `of`). Then, the indentation of the +next lexical element is remembered (and the missing open brace is +implicitly inserted before it). For each subsequent line, if it contains +only whitespace or is indented more, then it is treated as a +continuation of the current item. If it is indented the same amount, it +is treated as the beginning of the next item (*i.e.,* a semicolon is +inserted implicitly before the item). If it is indented less, then the +list of items is considered to be complete (*i.e.,* a closing brace is +implicitly inserted). An explicit brace is never matched against an +implicit one. Thus, while using the layout rule, if the parser +encounters an explicit open brace, then it does not resume using the +layout rule for this list of items until it has "emerged" past the +explicit corresponding closing brace (a construct nested inside this +list of items may still use the layout rule). + + +## Comments in BH programs + + +In a BH program, a *comment* is legal as whitespace, and may be +introduced in two ways. An *ordinary comment* is introduced by a lexical +token consisting of two or more consecutive dashes followed by a +non-symbol, and extends up to and including the end of the line. (See +Section +[\[sec-infix-applications\]](fixme) for the list of symbols.) Note: the +lexical token `—>` is a legal token in BH, and since it contains three +consecutive dashes followed by a symbol, it does not begin a comment. + + +A *nested comment* is introduced by the lexeme "`{-`" and extends until +the next matching "`-}`", possibly spanning multiple lines. A nested +comment can itself contain another nested comment; this nesting can be +repeated to any depth. + + +In an ordinary comment, the character sequences "`{-`" and "`-}`" have +no special significance, and, in a nested comment, a sequence of dashes +has no special significance. + + +## General organization of this document + + +A concept that is pervasive in BH is the notion of a *type*. Every value +expression in BH, even a basic value identifier, has a type, and the +compiler does extensive static type checking to rule out absurd use of +values (such as taking the square root of an IP address). Types are +discussed in section [2](fixme). + + +A BH program consists of one or more packages. These outermost +constructs are described in section +[3](fixme). As +explained later, a BH package is a linguistic namespace-management +mechanism and does not have any direct correlation with any hardware +module being described by the program. Hardware modules correspond to +*modules*, a particular type of value in BH. + + +Within each package is a collection of top-level definitions. These are +described in section [4](fixme). + + +Amongst the top-level definitions are *value definitions* (section +[4.7](fixme)), +which constitute the actual meat of the code. Value definitions are +built around *expressions*, which are described in section +[5](fixme). diff --git a/content/chapter2/_index.md b/content/chapter2/_index.md new file mode 100644 index 0000000..6dc15f7 --- /dev/null +++ b/content/chapter2/_index.md @@ -0,0 +1,103 @@ ++++ +title = "Types" +weight = 2 +sort_by = "weight" +insert_anchor_links = "right" ++++ + +Every value expression and, in particular, every value identifier in BH +has a *type*. In some cases the programmer must supply a *type +signature* specifying this and in many cases the compiler infers it +automatically. The BH programmer should be aware of types at all times. + +``` +type ::= btype [ "->" type ] +btype ::= [ btype ] atype +atype ::= tycon | tyvar | ( { type , } ) +tycon ::= conId +``` + + +Most type expressions have the form: *TypeConstructor* $t_1$ $\cdots$ +$t_n$ where $t_1$ $\cdots$ $t_n$ are themselves type expressions, and $n +{\geq} 0$. The $t_1$ $\cdots$ $t_n$ are referred to as the *type +arguments* to the type constructor. $n$ is also called the *arity* of +the type constructor. + + +Familiar basic types have zero-arity type constructors (no type +arguments, $n = 0$). Examples: + + - `Integer` + - `Bool` + - `String` + - `Action` + +Other type constructors have arity $n > 0$; these are also known as +*parameterized types*. + +Examples: + + - `List Bool` + - `List (List Bool)` + - `Array Integer String` + - `Maybe Integer` + +These represent the types of lists of +Booleans, lists of lists of Booleans, arrays indexed by integers and +containing strings, and an optional result possibly containing an +integer. + + +A type can be *polymorphic*, indicated using type variables. Examples: + + - `List a` + - `List (Llist b)` + - `Array i (List String)` + + +These represent lists of things of some unknown type "`a`", lists of +lists of things of some unknown type "`b`", and arrays indexed by some +unknown type "`i`" and containing lists of strings. + + +One type constructor is given special status in the syntax. The type of +functions from arguments of type $t_1$ to results of type $t_2$ could +have been written as: + +Function $t_1$ $t_2$ + +but in BH we write the constructor as an infix arrow: + +$t_1$ -\> $t_2$ + +These associate to the right, *i.e.,* + +$t_1$ -\> $\cdots$ -\> $t_{n-1}$ -\> $t_n$ $\equiv$ $t_1$ +-\> ($\cdots$ -\> ($t_{n-1}$ -\> $t_n$)) + + +There is one particular set of niladic type constructors that look like +numbers. These are used to represent certain "sizes". For example, the +type: + +`Bit 16` + +consists of the unary type constructor `Bit` applied to type represented +by the niladic type constructor "`16`". The type as a whole represents +bit vectors of length 16 bits. Similarly the type + +`UInt 32` + + +represents the type of unsigned integers that can be represented in 32 +bits. These numeric types are said to have kind `#`, rather than kind +`*` for value types. + + +Strings can also be used as type, having kind `$`. This is less common, +but string types are quite useful in the generics library, described in +the *Libraries Reference Guide*. Examples: + + - `MetaData#("Prelude","Maybe",PrimUnit,2)` + - `MetaConsNamed#("Valid",1,1)` diff --git a/content/chapter2/page1.md b/content/chapter2/page1.md new file mode 100644 index 0000000..20ab0cc --- /dev/null +++ b/content/chapter2/page1.md @@ -0,0 +1,108 @@ ++++ +title = "Type classes and overloading" +weight = 1 ++++ + +BH's `class` and `instance` mechanisms form a systematic way to do +*overloading* (the approach has been well tested in Haskell). + + +Overloading is a way to use a common name to refer to a set of +operations at different types. For example, we may want to use the "`<`" +operator name for the integer comparison operation, the floating-point +comparison operation, the vector comparison operation and the matrix +comparison operation. Note that this is not the same as polymorphism: a +polymorphic function is a *single* function that is meaningful at an +infinity of types (*i.e.,* at every possible instantiation of the type +variables in its type). An overloaded identifier, on the other hand, +usually uses a common name to refer to a (usually) small set of distinct +operations. + + +Further, it may make sense to have "`<=`", "`>`" and "`>=`" operations +wherever there is a "`<`" operation, on integers, floating points +numbers, vectors and matrices. Rather than handle these separately, we +say: + + + - there is class of types which we will call `Ord` (for "ordered types"), + - that the integer, floating point, vector and matrix types are members +(or "instances") of this class, and + - that all types that are members of this class have appropriate +definitions for the "`<`", "`<=`", "`>`" and "`>=`" operations. We also +say that these operations are *overloaded* across these instance types, +and we refer to these operations as the *methods* of this class. + + +Another example: we could use a class `Hashable` with an operation +called `hash` to represent those types $T$ for which we can and do +define a hashing function. Each such type $T$ has to specify how to +compute the `hash` function at that type. + + +Classes, and the membership of a type in a class, do not come into +existence by magic. Every class is created explicitly using a class +declaration, described in section +[4.5](fixme). +A type must explicitly be made an instance of a class and the +corresponding class methods have to be provided explicitly; this is +described in [4.6](fixme). + + +### Context-qualified types + + +Consider the following type declaration: + +```hs +sort :: (Ord a) => List a -> List a +``` + + +It expresses the idea that a sorting function takes an (unsorted) input +list of items and produces a (sorted) output list of items, but it is +only meaningful for those types of items ("`a`") for which the ordering +functions (such as "`<`") are defined. Thus, it is ok to apply `sort` to +lists of `Integer`'s or lists of `Bool`'s, because those types are +instances of `Ord`, but it is not ok to apply `sort` to a list of, say, +`Counter`'s (assuming `Counter` is not an instance of the `Ord` class). + + +In the type of `sort` above, the part before "`=>`" is called a +*context*. A context expresses constraints on one or more type +variables--- in the above example, the constraint is that any actual +type "`a`" must be an instance of the `Ord` class. + + +A context-qualified type has the following grammar: + +``` +ctxType ::= [ context => ] type +context ::= ( {classId { varId }, }) +classId ::= conId +``` + +In the above example, the class `Ord` had only one type parameter +(*i.e.,* it constrains a single type) but, in general, a type class can +have multiple type parameters. For example, in BH we frequently use the +class "`Bits a n`" which constrains the type represented by `a` to be +representable in bit strings of length represented by the type `n`. + + +> **NOTE:** +> +> When using an overloaded identifier `x` there is always a question of +> whether or not there is enough type information available to the +> compiler to determine which of the overloaded `x`'s you mean. For +> example, if `read` is an overloaded function that takes strings to +> integers or Booleans, and `show` is an overloaded function that takes +> integers or Booleans to strings, then the expression `show (read s)` is +> ambiguous--- is the thing to be read an integer or a Boolean? +> +> In such ambiguous situations, the compiler will so notify you, and you +> may need to give it a little help by inserting an explicit type +> signature, e.g., +> +> ```hs +> show ((read s) :: Bool) +> ``` diff --git a/content/chapter3/_index.md b/content/chapter3/_index.md new file mode 100644 index 0000000..425b7a9 --- /dev/null +++ b/content/chapter3/_index.md @@ -0,0 +1,82 @@ ++++ +title = "Packages" +weight = 3 +sort_by = "weight" +insert_anchor_links = "right" ++++ + +Packages are the outermost constructs in BH--- all BH code must be +inside packages. There should be one package per file. A BH package is a +linguistic device for namespace control, and is particularly useful for +programming-in-the-large. A package does not directly correspond to +hardware modules. (Hardware modules correspond to BH modules, described +in section [5.13](fixme)) + + +A BH package consists of the package header, import declarations, and +top level definitions. The package header indicates which names defined +in this package are exported, *i.e.,* available for import into other +packages. + +``` +packageDefn ::= package packageId ( exportDecl ) where { + { importDecl ; } + { fixityDecl ; } + { topDefn ; } + } +exportDecl ::= varId |typeId [ conList ] +conList ::= (..) +importDecl ::= import [ qualified ] packageId +fixityDecl ::= fixity integer varId +fixity ::= infix |infixl |infixr +packageId ::= conId +``` + +Example: + +```hs +package Foo (x, y) where +import Bar +import Glurph + +-- top level definition ... +-- top level definition ... +-- top level definition ... +``` + +Here, `Foo` is the name of this package, `x` and `y` +are names exported from this package (they will be defined amongst the +top level definitions in this package), and `Bar` and `Glurph` are the +names of package being imported (for use in this package). + + +The export list is a list of identifiers, each optionally followed by +`(..)`. Each identifier in the list will be visible outside the package. +If the exported identifier is the name of `data`, `struct`, or +`interface`, then the constructors or fields of the type will be visible +only if `(..)` is used. Otherwise, if you export only the name of a type +without the `(..)` suffix, the type is an abstract (opaque) data type +outside the package. The list of identifiers may include identifiers +defined in the package as well as identifiers imported from other +packages. + + +If the keyword `qualified` is present in the import declaration all the +imported entities from that package must be referred to by a qualified +name. + + +The fixity declaration can be used to give a precedence level to a +user-defined infix operator. The `infixl` specifies a left associative +operator, `infixr` a right associative operator, and `infix` a +non-associative operator. + + +## Name clashes and qualified names + + +When used in any scope, a name must have an unambiguous meaning. If +there is name clash for a name $x$ because it is defined in the current +package and/or it is available from one or more imported packages, then +the ambiguity can be resolved by using a qualified name of the form +$M.x$ to refer to the version of $x$ contained in package $M$. diff --git a/content/chapter4/_index.md b/content/chapter4/_index.md new file mode 100644 index 0000000..3e1cd7f --- /dev/null +++ b/content/chapter4/_index.md @@ -0,0 +1,12 @@ ++++ +title = "Top Level Definitions" +weight = 4 +sort_by = "weight" +insert_anchor_links = "right" ++++ + +# Top level definitions + + +Top level definitions can be used only on the top level within a +package. diff --git a/content/chapter4/page1.md b/content/chapter4/page1.md new file mode 100644 index 0000000..d572b31 --- /dev/null +++ b/content/chapter4/page1.md @@ -0,0 +1,146 @@ ++++ +title = "`data`" +weight = 1 ++++ + +A `data` definition defines a brand new type, which is different from +every primitive type and every other type defined using a `data` +definition, even if they look structurally similar. The new type defined +by a `data` definition is a "sum of products", or a "union of products". + +``` +topDefn ::= data typeId {tyVarId } = {summand | }[ derive ] +summand ::= conId {type } +summand ::= conId { { fieldDef ; }} +derive ::= deriving ( { classId , }) +fieldDef ::= fieldId :: type +``` + +The *typeId* is the name of this new type. If the *tyVarId*'s exist, +they are type parameters, thereby making this new type polymorphic. In +each *summand*, the *conId* is called a "constructor". You can think of +them as unique *tag*'s that identify each summand. Each *conId* is +followed by a specification for the fields involved in that summand +(*i.e.,* the fields are the "product" within the summand). In the first +way of specifying a summand, the fields are just identified by position, +hence we only specify the types of the fields. In the second way of +specifying a summand, the fields are named, hence we specify the field +names (*fieldId*'s) and their types. + + +The same constructor name may occur in more than one type. The same +field name can occur in more than one type. The same field name can +occur in more than one summand within the same type, but the type of the +field must be the same in each summand. + + +The optional *derive* clause is used as a shorthand to make this new +type an instance of the *classId*'s, instead of using a separate, +full-blown `instance` declaration. This can only be done for certain +predefined *classId*'s: `Bits`, `Eq`, and `Bounded`. The compiler +automatically derives the operations corresponding to those classes +(such as `pack` and `unpack` for the `Bits` class). Type classes, +instances, and `deriving` are described in more detail in sections +[2.1](fixme), [4.5](fixme) and [ +4.6](fixme). + +To construct a value corresponding to some `data` definition $T$, one +simply applies the constructor to the appropriate number of arguments +(see section [5.3](fixme){reference-type="ref" +reference="sec-exprs-constrs"}); the values of those arguments become +the components/fields of the data structure. + + +To extract a component/field from such a value, one uses pattern +matching (see section [6](fixme){reference-type="ref" +reference="sec-patterns"}). + + +Example: + +```hs +data Bool = False | True +``` + + +This is a "trivial" case of a `data` definition. The type is not +polymorphic (no type parameters); there are two summands with +constructors `False` and `True`, and neither constructor has any fields. +It is a 2-way sum of empty products. A value of type `Bool` is either +the value `False` or the value `True` Definitions like these correspond +to an "enum" definition in C. + + +Example: + +```hs +data Operand = Register (Bit 5) + | Literal (Bit 22) + | Indexed (Bit 5) (Bit 5) +``` + + +Here, the first two summands have one field each; the third has two +fields. The fields are positional (no field names). The field of a +`Register` value must have type Bit 5. A value of type `Operand` is +either a `Register` containing a 5-bit value, or a `Literal` containing +a 22-bit value, or an `Indexed` containing two 5-bit values. + + +Example: + +```hs +data Maybe a = Nothing | Just a + deriving (Eq, Bits) +``` + +This is a very useful and commonly used type. Consider a function that, +given a key, looks up a table and returns some value associated with +that key. Such a function can return either `Nothing`, if the table does +not contain an entry for the given key, of `Just `$v$, if the table +contains $v$ associated with the key. The type is polymorphic (type +parameter "`a`") because it may be used with lookup functions for +integer tables, string tables, IP address tables, etc., *i.e.,* we do +not want here to over-specify the type of the value $v$ at which it may +be used. + + +Example: + +```hs +data Instruction = Immediate { op::Op; rs::Reg; rt::CPUReg; imm::UInt16; } + | Jump { op::Op; target::UInt26; } +``` + + +An `Instruction` is either an `Immediate` or a `Jump`. In the former +case, it contains a field called `op` containing a value of type `Op`, a +field called `rs` containing a value of type `Reg`, a field called `rt` +containing a value of type `CPUReg`, and a field called `imm` containing +a value of type `UInt16`. In the latter case, it contains a field called +`op` containing a value of type `Op`, and a field called `target` +containing a value of type `UInt26`. + +> **NOTE:** +> +> Error messages involving data type definitions sometimes show traces of +> how they are handled internally. Data type definitions are translated +> into a data type where each constructor has exactly one argument. The +> types above translate to: +> +> ```hs +> data Bool = False PrimUnit | True PrimUnit +> +> data Operand = Register (Bit 5) +> | Literal (Bit 22) +> | Indexed Operand_$Indexed +> struct Operand_$Indexed = { _1 :: Reg 5; _2 :: Reg 5 } +> +> data Maybe a = Nothing PrimUnit | Just a +> +> data Instruction = Immediate Instruction_$Immediate +> | Register Instruction_$Register +> +> struct Instruction_$Immediate = { op::Op; rs::Reg; rt::CPUReg; imm::UInt16; } +> struct Instruction_$Register = { op::Op; target::UInt26; } +> ``` diff --git a/content/chapter4/page2.md b/content/chapter4/page2.md new file mode 100644 index 0000000..f06c76f --- /dev/null +++ b/content/chapter4/page2.md @@ -0,0 +1,68 @@ ++++ +title = "`struct`" +weight = 1 ++++ + +Defines a record type (a "pure product"). This is a specialized form of +a `data` definition. The same field name may occur in more than one +type. + +``` +topDefn ::= struct typeId {tyVarId }= { { fieldDef ; }} [ derive ] +fieldDef ::= fieldId :: type +``` + +Example: + +``` +struct Proc = { pc :: Addr; rf :: RegFile; mem :: Memory } +struct Coord = { x :: Int; y :: Int } +``` + + +Section [5.6](fixme) describes how to construct values of a +`struct` type. A field of a `struct` type can be extracted either +directly using "dot" notation (section +[5.7](fixme)) or using pattern matching (section +[6.3](fixme)). + + +### Tuples {#sec-tuple-type} + + +One way to group multiple values together is to use a `data` definition +in which a constructor has multiple fields. + + +However, there is a built-in notation for a common form of grouping, +called "tuples". To group two (or more) values together the Prelude +contains a type, `PrimPair`, for which there is syntactic sugar for type +expressions, value expressions, and patterns. + +The type has the following definition + +```hs +struct PrimPair a b = { fst :: a; snd :: b } deriving (Eq, Bits, Bounded) +``` + +For type expressions the following shorthand can be used: + +(a, b) $\equiv$ PrimPair a b + +Or, more generally, + +($t_1$, $t_2$, $\cdots$, $t_n$) $\equiv$ PrimPair $t_1$ (PrimPair $t_2$ ($\cdots$ $t_n$)) + + +There is a corresponding shorthand for value expressions and patterns: + +(a, b) $\equiv$ PrimPair { fst = a; snd = b } + +There is also special syntax for the empty tuple. It is written "`()`" +for types, expressions, and patterns. The real type has the following +definition + +```hs +struct PrimUnit = { } deriving (Eq, Bits, Bounded) +``` + diff --git a/content/chapter4/page3.md b/content/chapter4/page3.md new file mode 100644 index 0000000..e4fc2f5 --- /dev/null +++ b/content/chapter4/page3.md @@ -0,0 +1,38 @@ ++++ +title = "`type`" +weight = 1 ++++ + +Defines a type synonym. These are used purely for readability, *i.e.,* a +type synonym can always be "expanded out" to its definition at any time. + +``` +topDefn ::= type typeId {tyVarId }= type +``` + +Examples: + +```hs +type Byte = Bit 8 +type Word = Bit 16 +type LongWord = Bit 32 +``` + +These provide commonly used names for certain bit lengths. In a +specification of a processor: + +```hs +data RegName = R0 | R1 | ... | R31 +type Rdest = RegName +type Rsrc = RegName +data ArithInstr = Add Rdest Rsrc Rsrc + | Sub Rdest Rsrc Rsrc +``` + +the last two lines suggest the roles of the registers in the +instructions, and is more readable than: + +```hs +data ArithInstr = Add RegName RegName RegName + | Sub RegName RegName RegName +``` diff --git a/content/chapter4/page4.md b/content/chapter4/page4.md new file mode 100644 index 0000000..c3aadbf --- /dev/null +++ b/content/chapter4/page4.md @@ -0,0 +1,43 @@ ++++ +title = "`interface`" +weight = 1 ++++ + +Defines an interface for a hardware module (see section +[5.13](fixme)). An +interface is essentially a `struct`, but its components are restricted +to those things that have a physical interpretation as wires in and out +of a circuit. The types of fields in an interface are more likely to +involve `Action`'s (see section +[5.11](fixme)), +which are typically interpreted as "enable signals" into a circuit. The +fields of an interface are also known as *methods* (not to be confused +with methods of a class, described in Sections +[2.1](fixme) and +[4.5](fixme)). + +``` +topDefn ::= interface typeId {tyVarId }= { { fieldDef ; }} +``` + +Example: + +```hs +interface Stack a = + push :: a -> Action + pop :: Action + top :: Maybe a +``` + +This describes a circuit that implements a stack (a LIFO) of items. This +polymorphic definition does not specify the type of the contents of the +stack, just that they have some type "`a`". Corresponding to the `push` +method, the circuit will have input wires to carry a value of type +"`a`", and a "push-enable" input wire that specifies when the value +present on the input wires should be pushed on the stack. Corresponding +to the `pop` component, the circuit will have a "pop-enable" input wire +that specifies when a value should be popped off the stack. +Corresponding to the `top` component, the circuit will have a set of +output wires: if the stack is empty, the wires will represent the value +`Nothing`, and if the stack is non-empty and $v$ is the value at the top +of the stack, the wires will represent `Maybe` $v$. diff --git a/content/chapter4/page5.md b/content/chapter4/page5.md new file mode 100644 index 0000000..c723c52 --- /dev/null +++ b/content/chapter4/page5.md @@ -0,0 +1,86 @@ ++++ +title = "`class` declarations" +weight = 1 ++++ + +The general concepts behind classes, instances, overloading etc. were +introduced in section [2.1](fixme). A new class is declared using the +following: + +``` +topDefn ::= class [ context => ] classId {tyVarId }[ | funDep ] where { + {varId :: ctxType ; } + } +``` + + +*classId* is the newly declared class. It can be polymorphic, if +*tyVarId*'s exist; these are called the *parameters* of the type class. +The *tyVarId*'s may themselves be constrained by *context*, in which +case the classes named in *context* are called the "super-classes" of +this class. The "*varId*`::`*ctxType*" list declares the class method +names and their types. + +Example (from the Prelude): + +```hs +class Literal a where + fromInteger :: Integer -> a +``` + +This defines the class `Literal`. It says that any type `a` in this +class must have a method (a function) called `fromInteger` that converts +an `Integer` value into the type `a`. In fact, this is the mechanism the +BH uses to interpret literal constants, e.g., to resolve whether a +literal like `6`847 is to be interpreted as a signed integer, an +unsigned integer, a floating point number, a bit value of 10 bits, a bit +value of 8 bits, etc. (This is described in more detail in Section +[5.3](fixme).) + + +Example (from the Prelude): + +```hs +class (Literal a) => Arith a where + (+) :: a -> a -> a + (-) :: a -> a -> a + negate :: a -> a + (*) :: a -> a -> a +``` + + +This defines the class `Arith` with super-class `Literal`. It says that +for any type `a` that is a member of the class `Arith`, it must also be +a member of the class `Literal`, and it must have four methods with the +given names and types. Said another way, an `Arith` type must have a way +to convert integer literals into that type, and it must have addition, +subtraction, negation and multiplication defined on it. + + +The optional *funDep* section specifies *functional dependencies* +between the parameters of the type class: + +``` +funDep ::= { {tyVarId }-> {tyVarId }, } +``` + +These declarations specify that a type parameter may be determined +uniquely by certain other type parameters. For example: + +```hs +class Add x y z | x y -> z, y z -> x, z x -> y +``` + +Here, the class declaration says that for any triple of types `x`, `y` +and `z` that are in the class `Add`, any two of the types uniquely +determines the remaining type, *i.e.,* + + - x and y uniquely determine z, + - y and z uniquely determine x, and + - z and z uniquely determine y. + +See section [8.1](fixme) for more detailed insights into +the use of functional dependencies. + +> **NOTE:** +> Functional dependencies are not currently checked by the compiler. diff --git a/content/chapter4/page6.md b/content/chapter4/page6.md new file mode 100644 index 0000000..c5a4574 --- /dev/null +++ b/content/chapter4/page6.md @@ -0,0 +1,113 @@ ++++ +title = "`instance` declarations" +weight = 1 ++++ + +A type can be declared as an instance of a class in two ways. The +general mechanism is the `instance` declaration; a convenient shortcut +that can sometimes be used is the `deriving` mechanism. + +The general `instance` declaration grammar is the following: + +``` +topDefn ::= instance context => classId {type }where + { {localDefn ; }} +``` + +This can be read as saying that the type *type* is an instance of class +*classId*, provided the constraints of *context* hold, and where the +*localDefn*'s specify the implementation of the methods of the class. + +Sometimes, when a new type is defined using a `data` declaration, it can +simultaneously be made a member of certain useful, predefined classes, +allowing the compiler to choose the "obvious" implementation of the +class methods. This is done using the `deriving` qualification to a +`data` declaration (described in section +[4.1](fixme)) or to a +`struct` declaration (described in section +[4.2](fixme)). The only classes for which `deriving` can +be used for general types are `Bits`, `Eq` and `Bounded`. Furthermore, +`deriving` can be used for any class if the type is a data type that is +isomorphic to a type that has an instance for the derived class. + +### Deriving `Bits` {#sec-deriving-Bits} + +The instances derived for the `Bits` class can be described as follows: + + - For a `struct` type it is simply the the concatenation of the bits for +all the fields. The first field is in the leftmost (most significant) +bits, and so on. + - For a `data` type, all values of the type occupy the same number of +bits, regardless of which disjunct (constructor) it belongs to. This +size is determined by the largest disjunct. The leftmost (most +significant) bits are a code (a tag) for the constructor. As few bits as +possible are used for this. The first constructor in the definition is +coded 0, the next constructor is coded 1, and so on. The size of the +rest of the bits is determined by the largest numbers of bits needed to +encode the fields for the constructors. For each constructor, the fields +are laid out left to right, and the concatenated bits are stored right +justified (*i.e.,* at the least significant bits). For disjuncts that +are smaller than the largest one, the bits between the constructor code +and the field bits, if any, are "don't care" bits. + +Examples: The type + +```hs +data Bool = False | True +``` + +uses one bit. `False` is represented by 0 and `True` by 1. + +```hs +struct Två = { första :: Bit 8; andra:: Bit 16 } +``` + +uses 24 bits with `första` in the upper 8 bits and `andra` +in the lower 16. + +```hs +data Maybe a = Nothing | Just +``` + +a will use $1+n$ bits, +where $n$ bits are needed to represent values of type `a`. The extra bit +will be the most significant bit and it will be 0 (followed by $n$ +unspecified bits) for `Nothing` and 1 (followed by the $n$ bits for `a`) +for `Just`. + + +### Deriving `Eq` + + +The instances derived for the `Eq` class is the natural equality for the +type. For a struct all fields have to be equal, for a data type the +constructors have to be equal and then all their parts. + + +### Deriving `Bounded` + + +An instance for `Bounded` can be derived for an enumeration type, +*i.e.,* a data type where all constructors are niladic. The `minBound` +will be the first constructor and the `maxBound` will be the last. + +`Bounded` can also be derived for a `struct` type if all the field types +of the struct are `Bounded`. The `minBound` will be the struct with all +fields having their respective `minBound`, and correspondingly for +`maxBound`. + +### Deriving for isomorphic types + +A data type with one constructor and one argument is isomorphic to its +type argument. For such a type any one-parameter class can be used, in a +`deriving`, for which there is an instance for the underlying type. + +Example: + +```hs +data Apples = Apple (UInt 32) deriving (Literal, Arith) +five :: Apples +five = 5 +eatApple :: Apples -\> Apples +eatApple n = n - 1 +``` diff --git a/content/chapter4/page7.md b/content/chapter4/page7.md new file mode 100644 index 0000000..bef4cc9 --- /dev/null +++ b/content/chapter4/page7.md @@ -0,0 +1,94 @@ ++++ +title = "Value definitions" +weight = 1 ++++ + +A value definition defines the value of an identifier (which could be a +function). Value definitions are the meat of a BH program. + +Value definitions consist of a type signature followed immediately by +one or more defining clauses: + +``` +topDefn ::= valueDefn +valueDefn ::= varId :: ctxType ; + {clause ; } +clause ::= varId + {apat }[ when guard ]= exp +``` + +The first line of a value definition is the type signature--- it simply +specifies that the identifier *varId* has the type *ctxType*. Subsequent +lines define the value, one clause at a time. The *varId*'s on the +left-hand side of the type signature and on the left-hand side of each +clause must all be the same, *i.e.,* they collectively define a single +*varId*. + +Each clause defines part of the value, using pattern matching and +guards. If there are patterns (*apat*'s) present, then the *varId* being +defined is a function, and the patterns represent arguments to the +function. The *guard* is a list of arbitrary predicates that may use +identifiers bound in the patterns (see Section [7](fixme)). + +The clause should be read as follows: if the function *varId* is applied to +arguments that match the corresponding *apat*'s (in which case, +identifiers in the *apat*'s are bound to the corresponding components of +the arguments), and if the predicates in the *guard* are true, then the +function returns the value of the expression *exp*. + +Example: + +```hs +wordSize :: Integer +wordSize = 16 +``` + + +This simply defines the identifier `wordSize` to have type `Integer` and +value 16. + + +Example: + +```hs +not :: Bool -> Bool +not True = False +not False = True +``` + +This defines the classical Boolean negation function. The type signature +specifies that `not` is a function with argument type `Bool` and result +type `Bool`. After that, the first clause specifies that if the argument +matches the value `True` (*i.e.,* it *is* the value `True`), then it +returns `False`. The final clause specifies that if the argument is +`False` it returns `True`. + + +Example: + +```hs +f :: Maybe Int -> Int -> Int +f (Just x) y when x > 10, Just y’ <- g y = x + y' +f _ _ = 0 +``` + +(If necessary, please first remember the definition of the `Maybe` type, +introduced in section [4.1](fixme)). The first line specifies that +`f` is a function +of two arguments, of type `Maybe Int` and `Int`, respectively, and that +its result has type `Int`. The second line specifies that if the first +argument has the form `Just x` (in which case let us call its component +`x`), if the second argument is anything (let us call it `y`), if `x`'s +value is greater than 10, if the result of applying `g` to `y` has the +form `Just y’` (in which case let us call the component `y’`), then the +result is the value of `x + y’`. In all other cases, the result is the +value 0. The bare underscores in the second line are *wild-card* +patterns that match anything (described in section +[6.1](fixme)). + + +Clauses are attempted in order, from top to bottom, proceeding to the +next clause only if the pattern matching and guard evaluation fail. +Within each clause, pattern matching and guard evaluation are attempted +from left to right. If no clause succeeds, then the system will raise a +"pattern matching error". diff --git a/content/chapter4/page8.md b/content/chapter4/page8.md new file mode 100644 index 0000000..9508a36 --- /dev/null +++ b/content/chapter4/page8.md @@ -0,0 +1,46 @@ ++++ +title = "Calling foreign functions" +weight = 1 ++++ + +A function can be declared to be foreign which means that its +implementation is not in BH. + + +``` +topDefn ::= foreign varId :: type [= string ] [ , ( {string }) ] +``` + +The optional string gives the name of the external "function" to use. If +no string is given the same name as the BH name is used. The optional +strings in parentheses are the port names of the Verilog module that +implements the function. Without port names positional arguments will be +used. + +Example: + +```hs +foreign countOnes :: Bit n -> Bit 32 = "pop_count" +``` + + +A call to `countOnes` will instantiate the Verilog `pop`` ``count` +module. It should have the same number of arguments (with the same type) +as the BH function, *and* an additional trailing argument which is the +result. If the function is (size) polymorphic the instantiated types +will be used as Verilog parameters. + + +Example: using the declaration above an action, with the type of `x` +being `Bit 5`, + +```hs +y := countOnes x +``` + + +will translate to something like + +```hs +pop_count #(5) ires1(R_x, I_y); +``` diff --git a/content/preface/_index.md b/content/preface/_index.md new file mode 100644 index 0000000..404d7b6 --- /dev/null +++ b/content/preface/_index.md @@ -0,0 +1,21 @@ ++++ +title = "UNDER CONSTRUCTION ⚠️" +weight = 5 +sort_by = "weight" +insert_anchor_links = "right" ++++ + +> Note that these docs are under construction and may not be finished +> until summer 2025! +> There are several chapters remaining to complete! + +This is a largely manual attempt(with much assistance from +[Pandoc][pandoc]) to convert the +[Bluespec Haskell/Classic LaTeX docs][bhdocs] to an online +readable markdown format. + +If you find any errors, please reach out to me at +[yehowshua@joyofhardware.com](mailto:yehowshua@joyofhardware.com) + +[pandoc]: https://pandoc.org +[bhdocs]: https://github.com/B-Lang-org/bsc/tree/main/doc/BH_ref_guide diff --git a/sass/_content.scss b/sass/_content.scss new file mode 100644 index 0000000..af1efe3 --- /dev/null +++ b/sass/_content.scss @@ -0,0 +1,231 @@ +@use "variables"; +.page { + position: absolute; + width: calc(100% - #{variables.$sidebar-width}); + height: 100%; + overflow-y: auto; + color: #000; + background: #fff; + padding-bottom: 20px; + transition: 0.5s; + + .zola-anchor { + color: #357aba; + padding-left: 10px; + text-decoration: none; + font-weight: initial; + + &:hover { + text-decoration: underline; + } + } + + img { + max-width: 100%; + } + + &__content { + a { + color: #357aba; + text-decoration: none; + + &:hover { + text-decoration: underline; + } + } + + hr { + height: 4px; + padding: 0; + margin: 1.7em 0; + overflow: hidden; + background-color: #e7e7e7; + border: none; + } + + pre { + padding: 1rem; + + span { + white-space: pre-wrap; + } + } + + blockquote { + margin: 0; + margin-bottom: .85em; + padding: 0 15px; + color: #858585; + border-left: 4px solid #e5e5e5; + } + + pre code { + background: none; + } + + code { + display: inline-block; + vertical-align: middle; + padding: 0.1em 0.3em; + border-radius: 3px; + color: #6e6b5e; + background: #f1f1f1; + font-size: 0.875em; + font-family: "Source Code Pro", Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace, monospace; + } + + iframe { + border: 0; + } + + table { + margin: 0 auto; + border-collapse: collapse; + border-color: #cccccc; + + td { + padding: 3px 20px; + border: 1px solid #ccc; + } + + thead { + th { + padding: 6px 13px; + font-weight: bold; + border: 1px solid #ccc; + } + } + } + + font-size: 1.6rem; + word-wrap: break-word; + line-height: 1.7; + position: relative; + left: 0; + max-width: 800px; + margin: 0 auto; + padding: 0 15px 40px; + + p { + margin-top: 0; + margin-bottom: 0.85em; + } + } + + .previous, .next { + position: fixed; + display: flex; + top: 50px; + bottom: 0; + font-size: 2.5em; + color: #ccc; + text-decoration: none; + text-align: center; + margin: 0; + max-width: 150px; + min-width: 90px; + justify-content: center; + align-content: center; + flex-direction: column; + + &:hover { + color: #333; + } + } + + .previous { + left: variables.$sidebar-width; + float: left; + transition: left 0.5s; + } + + .next { + // not 0 as it goes over the sidebar otherwise + right: 15px; + float: right; + } + + @include variables.max-screen(1250px) { + .previous, .next { + position: static; + top: auto; + display: inline-block; + max-width: 49%; + width: 49%; + + &:hover { + text-decoration: none; + } + } + } +} + +@include variables.min-screen(600px) { + .page { + left: variables.$sidebar-width; + } +} + +.page-without-menu { + width: 100%; + left: 0; + + .previous { + left: 15px; + } +} + +@include variables.max-screen(600px) { + .page { + width: 100%; + left: 0; + } + + .page-without-menu { + left: calc(100% - 100px); + } +} + +.search-container { + display: none; + + &--is-visible { + display: block; + } + + #search { + width: 100%; + padding: 1rem; + border: 1px solid #aaa; + border-radius: 3px; + background-color: #fafafa; + color: #000; + } + + .search-results { + &__header { + font-weight: bold; + padding: 1rem 0rem; + } + + &__items { + margin: 0; + padding: 0; + list-style: none; + } + + &__item { + margin-bottom: 1rem; + } + + &__teaser { + font-size: 90%; + } + } +} + +.search-mode { + .prev-link, .next-link { + display: none; + } +} diff --git a/sass/_document.scss b/sass/_document.scss new file mode 100644 index 0000000..a54f67f --- /dev/null +++ b/sass/_document.scss @@ -0,0 +1,18 @@ +* { + box-sizing: border-box; +} + +html { + font-size: 62.5%; +} + +body, html { + height: 100%; +} + +body { + text-rendering: optimizeLegibility; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + letter-spacing: 0.2px; +} diff --git a/sass/_header.scss b/sass/_header.scss new file mode 100644 index 0000000..a4034c4 --- /dev/null +++ b/sass/_header.scss @@ -0,0 +1,160 @@ +@mixin menu-icon() { + @keyframes clickfirst { + 0% { + transform: translateY(6px) rotate(0deg); + + } + + 100% { + transform: translateY(0) rotate(45deg); + } + } + + @keyframes clickmid { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + } + } + + @keyframes clicklast { + 0% { + transform: translateY(-6px) rotate(0deg); + } + + 100% { + transform: translateY(0) rotate(-45deg); + } + } + + @keyframes outfirst { + 0% { + transform: translateY(0) rotate(-45deg); + } + + 100% { + transform: translateY(-6px) rotate(0deg); + } + } + + @keyframes outmid { + 0% { + opacity: 0; + } + + 100% { + opacity: 1; + } + } + + @keyframes outlast { + 0% { + transform: translateY(0) rotate(45deg); + } + + 100% { + transform: translateY(6px) rotate(0deg); + } + } + + span { + position: absolute; + /* fallback for browsers which still doesn't support for `calc()` */ + left: 15px; + top: 25px; + left: calc((100% - 20px) / 2); + top: calc((100% - 1px) / 2); + width: 20px; + height: 2px; + background-color: rgba(0, 0, 0, 0.5); + + &:nth-child(1) { + transform: translateY(6px) rotate(0deg); + } + + &:nth-child(3) { + transform: translateY(-6px) rotate(0deg); + } + } + + &.icon-click { + span:nth-child(1) { + animation-duration: 0.5s; + animation-fill-mode: both; + animation-name: clickfirst; + } + + span:nth-child(2) { + animation-duration: 0.2s; + animation-fill-mode: both; + animation-name: clickmid; + } + + span:nth-child(3) { + animation-duration: 0.5s; + animation-fill-mode: both; + animation-name: clicklast; + } + } + + &.icon-out { + span:nth-child(1) { + animation-duration: 0.5s; + animation-fill-mode: both; + animation-name: outfirst; + } + + span:nth-child(2) { + animation-duration: 0.2s; + animation-fill-mode: both; + animation-name: outmid; + } + + span:nth-child(3) { + animation-duration: 0.5s; + animation-fill-mode: both; + animation-name: outlast; + } + } +} + +.page__header { + height: 50px; + + .menu-icon { + height: 50px; + width: 50px; + font-size: 24px; + text-align: center; + float: left; + position: relative; + transition: background .5s; + cursor: pointer; + + @include menu-icon(); + + &:hover { + span { + background-color: black; + } + } + } + + .search-icon { + height: 50px; + width: 50px; + display: inline-block; + text-align: center; + line-height: 50px; + color: rgba(0, 0, 0, 0.5); + cursor: pointer; + font-size: 2rem; + + &:hover { + color: black + } + } +} diff --git a/sass/_navigation.scss b/sass/_navigation.scss new file mode 100644 index 0000000..c028fa7 --- /dev/null +++ b/sass/_navigation.scss @@ -0,0 +1,57 @@ +@use "variables"; +.menu { + height: 100%; + position: absolute; + left: 0; + overflow-y: auto; + width: 300px; + color: #364149; + background: #fafafa; + border-right: 1px solid rgba(0, 0, 0, 0.07); + transition: 0.5s; + + ul { + list-style: none; + margin: 0; + padding: 0; + + a { + display: block; + color: #364149; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + text-decoration: none; + padding: 10px 15px; + + &:hover { + text-decoration: underline; + } + } + + li.active > a { + color: #0053bc; + text-decoration: none; + } + + ul { + padding-left: 20px; + } + } +} + +.menu-hidden { + width: 0; +} + +@include variables.max-screen(600px) { + .menu { + width: 0; + z-index: 1; + } + + .menu-hidden { + width: calc(100% - 100px); + z-index: 0; + } +} diff --git a/sass/_normalize.scss b/sass/_normalize.scss new file mode 100644 index 0000000..fa4e73d --- /dev/null +++ b/sass/_normalize.scss @@ -0,0 +1,447 @@ +/*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */ + +/* Document + ========================================================================== */ + +/** + * 1. Correct the line height in all browsers. + * 2. Prevent adjustments of font size after orientation changes in + * IE on Windows Phone and in iOS. + */ + +html { + line-height: 1.15; /* 1 */ + -ms-text-size-adjust: 100%; /* 2 */ + -webkit-text-size-adjust: 100%; /* 2 */ +} + +/* Sections + ========================================================================== */ + +/** + * Remove the margin in all browsers (opinionated). + */ + +body { + margin: 0; +} + +/** + * Add the correct display in IE 9-. + */ + +article, +aside, +footer, +header, +nav, +section { + display: block; +} + +/** + * Correct the font size and margin on `h1` elements within `section` and + * `article` contexts in Chrome, Firefox, and Safari. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/* Grouping content + ========================================================================== */ + +/** + * Add the correct display in IE 9-. + * 1. Add the correct display in IE. + */ + +figcaption, +figure, +main { /* 1 */ + display: block; +} + +/** + * Add the correct margin in IE 8. + */ + +figure { + margin: 1em 40px; +} + +/** + * 1. Add the correct box sizing in Firefox. + * 2. Show the overflow in Edge and IE. + */ + +hr { + box-sizing: content-box; /* 1 */ + height: 0; /* 1 */ + overflow: visible; /* 2 */ +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +pre { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/* Text-level semantics + ========================================================================== */ + +/** + * 1. Remove the gray background on active links in IE 10. + * 2. Remove gaps in links underline in iOS 8+ and Safari 8+. + */ + +a { + background-color: transparent; /* 1 */ + -webkit-text-decoration-skip: objects; /* 2 */ +} + +/** + * 1. Remove the bottom border in Chrome 57- and Firefox 39-. + * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. + */ + +abbr[title] { + border-bottom: none; /* 1 */ + text-decoration: underline; /* 2 */ + text-decoration: underline dotted; /* 2 */ +} + +/** + * Prevent the duplicate application of `bolder` by the next rule in Safari 6. + */ + +b, +strong { + font-weight: inherit; +} + +/** + * Add the correct font weight in Chrome, Edge, and Safari. + */ + +b, +strong { + font-weight: bolder; +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +code, +kbd, +samp { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/** + * Add the correct font style in Android 4.3-. + */ + +dfn { + font-style: italic; +} + +/** + * Add the correct background and color in IE 9-. + */ + +mark { + background-color: #ff0; + color: #000; +} + +/** + * Add the correct font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` elements from affecting the line height in + * all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* Embedded content + ========================================================================== */ + +/** + * Add the correct display in IE 9-. + */ + +audio, +video { + display: inline-block; +} + +/** + * Add the correct display in iOS 4-7. + */ + +audio:not([controls]) { + display: none; + height: 0; +} + +/** + * Remove the border on images inside links in IE 10-. + */ + +img { + border-style: none; +} + +/** + * Hide the overflow in IE. + */ + +svg:not(:root) { + overflow: hidden; +} + +/* Forms + ========================================================================== */ + +/** + * 1. Change the font styles in all browsers (opinionated). + * 2. Remove the margin in Firefox and Safari. + */ + +button, +input, +optgroup, +select, +textarea { + font-family: sans-serif; /* 1 */ + font-size: 100%; /* 1 */ + line-height: 1.15; /* 1 */ + margin: 0; /* 2 */ +} + +/** + * Show the overflow in IE. + * 1. Show the overflow in Edge. + */ + +button, +input { /* 1 */ + overflow: visible; +} + +/** + * Remove the inheritance of text transform in Edge, Firefox, and IE. + * 1. Remove the inheritance of text transform in Firefox. + */ + +button, +select { /* 1 */ + text-transform: none; +} + +/** + * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video` + * controls in Android 4. + * 2. Correct the inability to style clickable types in iOS and Safari. + */ + +button, +html [type="button"], /* 1 */ +[type="reset"], +[type="submit"] { + -webkit-appearance: button; /* 2 */ +} + +/** + * Remove the inner border and padding in Firefox. + */ + +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; +} + +/** + * Restore the focus styles unset by the previous rule. + */ + +button:-moz-focusring, +[type="button"]:-moz-focusring, +[type="reset"]:-moz-focusring, +[type="submit"]:-moz-focusring { + outline: 1px dotted ButtonText; +} + +/** + * Correct the padding in Firefox. + */ + +fieldset { + padding: 0.35em 0.75em 0.625em; +} + +/** + * 1. Correct the text wrapping in Edge and IE. + * 2. Correct the color inheritance from `fieldset` elements in IE. + * 3. Remove the padding so developers are not caught out when they zero out + * `fieldset` elements in all browsers. + */ + +legend { + box-sizing: border-box; /* 1 */ + color: inherit; /* 2 */ + display: table; /* 1 */ + max-width: 100%; /* 1 */ + padding: 0; /* 3 */ + white-space: normal; /* 1 */ +} + +/** + * 1. Add the correct display in IE 9-. + * 2. Add the correct vertical alignment in Chrome, Firefox, and Opera. + */ + +progress { + display: inline-block; /* 1 */ + vertical-align: baseline; /* 2 */ +} + +/** + * Remove the default vertical scrollbar in IE. + */ + +textarea { + overflow: auto; +} + +/** + * 1. Add the correct box sizing in IE 10-. + * 2. Remove the padding in IE 10-. + */ + +[type="checkbox"], +[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Correct the cursor style of increment and decrement buttons in Chrome. + */ + +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +/** + * 1. Correct the odd appearance in Chrome and Safari. + * 2. Correct the outline style in Safari. + */ + +[type="search"] { + -webkit-appearance: textfield; /* 1 */ + outline-offset: -2px; /* 2 */ +} + +/** + * Remove the inner padding and cancel buttons in Chrome and Safari on macOS. + */ + +[type="search"]::-webkit-search-cancel-button, +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * 1. Correct the inability to style clickable types in iOS and Safari. + * 2. Change font properties to `inherit` in Safari. + */ + +::-webkit-file-upload-button { + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ +} + +/* Interactive + ========================================================================== */ + +/* + * Add the correct display in IE 9-. + * 1. Add the correct display in Edge, IE, and Firefox. + */ + +details, /* 1 */ +menu { + display: block; +} + +/* + * Add the correct display in all browsers. + */ + +summary { + display: list-item; +} + +/* Scripting + ========================================================================== */ + +/** + * Add the correct display in IE 9-. + */ + +canvas { + display: inline-block; +} + +/** + * Add the correct display in IE. + */ + +template { + display: none; +} + +/* Hidden + ========================================================================== */ + +/** + * Add the correct display in IE 10-. + */ + +[hidden] { + display: none; +} diff --git a/sass/_syntax-charcoal.scss b/sass/_syntax-charcoal.scss new file mode 100644 index 0000000..2073236 --- /dev/null +++ b/sass/_syntax-charcoal.scss @@ -0,0 +1,162 @@ +@mixin dark { + .z-code { + color: #cccece; + background-color: #191919; + } + .z-comment, .z-punctuation.z-definition.z-comment { + color: #7e8384; + } + .z-variable { + color: #cccece; + } + .z-keyword, .z-storage.z-type, .z-storage.z-modifier { + color: #c594c5; + } + .z-keyword.z-operator, .z-constant.z-other.z-color, .z-punctuation, .z-meta.z-tag, .z-punctuation.z-definition.z-tag, .z-punctuation.z-separator.z-inheritance.z-php, .z-punctuation.z-definition.z-tag.z-html, .z-punctuation.z-definition.z-tag.z-begin.z-html, .z-punctuation.z-definition.z-tag.z-end.z-html, .z-punctuation.z-section.z-embedded, .z-keyword.z-other.z-template, .z-keyword.z-other.z-substitution { + color: #5fb3b3; + } + .z-entity.z-name.z-tag, .z-meta.z-tag.z-sgml, .z-markup.z-deleted.z-git_gutter { + color: #ff7b84; + } + .z-entity.z-name.z-function, .z-meta.z-function-call, .z-variable.z-function, .z-support.z-function, .z-keyword.z-other.z-special-method, .z-meta.z-block-level { + color: #78aade; + } + .z-support.z-other.z-variable, .z-string.z-other.z-link { + color: #fa7e81; + } + .z-constant.z-numeric, .z-constant.z-language, .z-support.z-constant, .z-constant.z-character, .z-variable.z-parameter, .z-keyword.z-other.z-unit { + color: #f99157; + } + .z-string, .z-constant.z-other.z-symbol, .z-constant.z-other.z-key, .z-entity.z-other.z-inherited-class, .z-markup.z-heading, .z-markup.z-inserted.z-git_gutter, .z-meta.z-group.z-braces.z-curly .z-constant.z-other.z-object.z-key.z-js .z-string.z-unquoted.z-label.z-js { + color: #99c794; + } + .z-entity.z-name.z-class, .z-entity.z-name.z-type.z-class, .z-support.z-type, .z-support.z-class, .z-support.z-orther.z-namespace.z-use.z-php, .z-meta.z-use.z-php, .z-support.z-other.z-namespace.z-php, .z-markup.z-changed.z-git_gutter { + color: #fac863; + } + .z-entity.z-name.z-module.z-js, .z-variable.z-import.z-parameter.z-js, .z-variable.z-other.z-class.z-js { + color: #fe7d83; + } + .z-variable.z-language { + color: #fe7d83; + } + .z-entity.z-name.z-method.z-js { + color: #d8dee9; + } + .z-meta.z-class-method.z-js .z-entity.z-name.z-function.z-js, .z-variable.z-function.z-constructor { + color: #d8dee9; + } + .z-entity.z-other.z-attribute-name { + color: #cd91c4; + } + .z-markup.z-inserted { + color: #99c794; + } + .z-markup.z-deleted { + color: #fe7d83; + } + .z-markup.z-changed { + color: #cd91c4; + } + .z-string.z-regexp { + color: #5fb3b3; + } + .z-constant.z-character.z-escape { + color: #5fb3b3; + } + .z-*url*, .z-*link*, .z-*uri* { + text-decoration: underline; + } + .z-constant.z-numeric.z-line-number.z-find-in-files { + color: #cf9a87; + } + .z-entity.z-name.z-filename.z-find-in-files { + color: #99c794; + } + .z-tag.z-decorator.z-js .z-entity.z-name.z-tag.z-js, .z-tag.z-decorator.z-js .z-punctuation.z-definition.z-tag.z-js { + color: #78aade; + } + .z-source.z-js .z-constant.z-other.z-object.z-key.z-js .z-string.z-unquoted.z-label.z-js { + color: #fe7d83; + } +} +@mixin light { + .z-code { + color: #727373; + background-color: #ffffff; + } + .z-comment, .z-punctuation.z-definition.z-comment { + color: #5f6364; + } + .z-variable { + color: #727373; + } + .z-keyword, .z-storage.z-type, .z-storage.z-modifier { + color: #916392; + } + .z-keyword.z-operator, .z-constant.z-other.z-color, .z-punctuation, .z-meta.z-tag, .z-punctuation.z-definition.z-tag, .z-punctuation.z-separator.z-inheritance.z-php, .z-punctuation.z-definition.z-tag.z-html, .z-punctuation.z-definition.z-tag.z-begin.z-html, .z-punctuation.z-definition.z-tag.z-end.z-html, .z-punctuation.z-section.z-embedded, .z-keyword.z-other.z-template, .z-keyword.z-other.z-substitution { + color: #237e7f; + } + .z-entity.z-name.z-tag, .z-meta.z-tag.z-sgml, .z-markup.z-deleted.z-git_gutter { + color: #ca4251; + } + .z-entity.z-name.z-function, .z-meta.z-function-call, .z-variable.z-function, .z-support.z-function, .z-keyword.z-other.z-special-method, .z-meta.z-block-level { + color: #4076a7; + } + .z-support.z-other.z-variable, .z-string.z-other.z-link { + color: #c14c52; + } + .z-constant.z-numeric, .z-constant.z-language, .z-support.z-constant, .z-constant.z-character, .z-variable.z-parameter, .z-keyword.z-other.z-unit { + color: #b75922; + } + .z-string, .z-constant.z-other.z-symbol, .z-constant.z-other.z-key, .z-entity.z-other.z-inherited-class, .z-markup.z-heading, .z-markup.z-inserted.z-git_gutter, .z-meta.z-group.z-braces.z-curly .z-constant.z-other.z-object.z-key.z-js .z-string.z-unquoted.z-label.z-js { + color: #517c4e; + } + .z-entity.z-name.z-class, .z-entity.z-name.z-type.z-class, .z-support.z-type, .z-support.z-class, .z-support.z-orther.z-namespace.z-use.z-php, .z-meta.z-use.z-php, .z-support.z-other.z-namespace.z-php, .z-markup.z-changed.z-git_gutter { + color: #926c00; + } + .z-entity.z-name.z-module.z-js, .z-variable.z-import.z-parameter.z-js, .z-variable.z-other.z-class.z-js { + color: #cb414d; + } + .z-variable.z-language { + color: #cb414d; + } + .z-entity.z-name.z-method.z-js { + color: #6c727c; + } + .z-meta.z-class-method.z-js .z-entity.z-name.z-function.z-js, .z-variable.z-function.z-constructor { + color: #6c727c; + } + .z-entity.z-other.z-attribute-name { + color: #996091; + } + .z-markup.z-inserted { + color: #517c4e; + } + .z-markup.z-deleted { + color: #cb414d; + } + .z-markup.z-changed { + color: #996091; + } + .z-string.z-regexp { + color: #237e7f; + } + .z-constant.z-character.z-escape { + color: #237e7f; + } + .z-*url*, .z-*link*, .z-*uri* { + text-decoration: underline; + } + .z-constant.z-numeric.z-line-number.z-find-in-files { + color: #976756; + } + .z-entity.z-name.z-filename.z-find-in-files { + color: #517c4e; + } + .z-tag.z-decorator.z-js .z-entity.z-name.z-tag.z-js, .z-tag.z-decorator.z-js .z-punctuation.z-definition.z-tag.z-js { + color: #4076a7; + } + .z-source.z-js .z-constant.z-other.z-object.z-key.z-js .z-string.z-unquoted.z-label.z-js { + color: #cb414d; + } +} diff --git a/sass/_syntax.scss b/sass/_syntax.scss new file mode 100644 index 0000000..a3007f4 --- /dev/null +++ b/sass/_syntax.scss @@ -0,0 +1,551 @@ +@mixin dark { + .z-code { + color: #bfbab0; + background-color: #191919; + } + .z-comment, .z-punctuation.z-definition.z-comment { + color: #87929f; + font-style: italic; + } + .z-variable { + color: #bfbab0; + } + .z-string, .z-constant.z-other.z-symbol { + color: #c2d94c; + } + .z-constant.z-numeric { + color: #f29718; + } + .z-string.z-regexp, .z-constant.z-character.z-escape { + color: #95e6cb; + } + .z-constant.z-language { + color: #f29718; + } + .z-constant.z-character, .z-constant.z-other { + color: #f29718; + } + .z-variable.z-member { + color: #fe7d83; + } + .z-keyword, .z-keyword.z-operator.z-word { + color: #00bbff; + } + .z-keyword.z-operator { + color: #f29668; + } + .z-punctuation.z-separator, .z-punctuation.z-terminator { + color: #bfbab0; + } + .z-punctuation.z-section { + color: #bfbab0; + } + .z-punctuation.z-accessor { + color: #f29668; + } + .z-punctuation.z-definition.z-annotation { + color: #bfbab0; + } + .z-variable.z-other.z-dollar.z-only.z-js, .z-variable.z-other.z-object.z-dollar.z-only.z-js, .z-variable.z-type.z-dollar.z-only.z-js, .z-support.z-class.z-dollar.z-only.z-js { + color: #e6b673; + } + .z-storage { + color: #00bbff; + } + .z-storage.z-type { + color: #00bbff; + } + .z-entity.z-name.z-function { + color: #ffb454; + } + .z-entity.z-name, .z-entity.z-name.z-tag, .z-entity.z-name.z-label { + color: #59c2ff; + } + .z-entity.z-other.z-inherited-class { + color: #59c2ff; + text-decoration: underline; + } + .z-variable.z-parameter { + color: #f29718; + } + .z-variable.z-language { + color: #39bae6; + font-style: italic; + } + .z-entity.z-name.z-tag, .z-meta.z-tag.z-sgml { + color: #39bae6; + } + .z-punctuation.z-definition.z-tag.z-end, .z-punctuation.z-definition.z-tag.z-begin, .z-punctuation.z-definition.z-tag { + color: #39bae6; + } + .z-entity.z-other.z-attribute-name { + color: #ffb454; + } + .z-variable.z-function, .z-variable.z-annotation { + color: #ffb454; + } + .z-support.z-function, .z-support.z-macro { + color: #fe7d83; + } + .z-support.z-constant { + color: #f29668; + font-style: italic; + } + .z-support.z-type, .z-support.z-class { + color: #39bae6; + font-style: italic; + } + .z-invalid { + color: #ff7c6d; + } + .z-invalid.z-deprecated { + color: #ffffff; + background-color: #00bbff; + } + .z-meta.z-diff, .z-meta.z-diff.z-header { + color: #c594c5; + } + .z-source.z-ruby .z-variable.z-other.z-readwrite { + color: #ffb454; + } + .z-source.z-css .z-entity.z-name.z-tag, .z-source.z-sass .z-entity.z-name.z-tag, .z-source.z-scss .z-entity.z-name.z-tag, .z-source.z-less .z-entity.z-name.z-tag, .z-source.z-stylus .z-entity.z-name.z-tag { + color: #59c2ff; + } + .z-source.z-css .z-support.z-type, .z-source.z-sass .z-support.z-type, .z-source.z-scss .z-support.z-type, .z-source.z-less .z-support.z-type, .z-source.z-stylus .z-support.z-type { + color: #798491; + } + .z-support.z-type.z-property-name { + color: #39bae6; + } + .z-constant.z-numeric.z-line-number.z-find-in-files { + color: #798491; + } + .z-constant.z-numeric.z-line-number.z-match { + color: #00bbff; + } + .z-entity.z-name.z-filename.z-find-in-files { + color: #c2d94c; + } + .z-message.z-error { + color: #ff7c6d; + } + .z-source.z-json .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json, .z-source.z-json .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-punctuation.z-definition.z-string { + color: #39bae6; + } + .z-source.z-json .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json, .z-source.z-json .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-punctuation.z-definition.z-string { + color: #39bae6; + } + .z-source.z-json .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json, .z-source.z-json .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-punctuation.z-definition.z-string { + color: #39bae6; + } + .z-source.z-json .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json, .z-source.z-json .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-punctuation.z-definition.z-string { + color: #39bae6; + } + .z-source.z-json .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json, .z-source.z-json .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-punctuation.z-definition.z-string { + color: #39bae6; + } + .z-source.z-json .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json, .z-source.z-json .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-punctuation.z-definition.z-string { + color: #39bae6; + } + .z-source.z-json .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json, .z-source.z-json .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-punctuation.z-definition.z-string { + color: #39bae6; + } + .z-source.z-json .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json, .z-source.z-json .z-meta .z-meta.z-structure.z-dictionary.z-json .z-punctuation.z-definition.z-string { + color: #39bae6; + } + .z-source.z-json .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json, .z-source.z-json .z-meta.z-structure.z-dictionary.z-json .z-punctuation.z-definition.z-string { + color: #39bae6; + } + .z-markup.z-heading { + color: #00bbff; + font-weight: bold; + } + .z-string.z-other.z-link, .z-markup.z-underline.z-link { + color: #95e6cb; + text-decoration: underline; + font-style: italic; + } + .z-punctuation.z-definition.z-image { + color: #ffb454; + } + .z-markup.z-italic { + color: #fe7d83; + font-style: italic; + } + .z-markup.z-bold { + color: #fe7d83; + font-weight: bold; + } + .z-markup.z-italic .z-markup.z-bold, .z-markup.z-bold .z-markup.z-italic { + font-weight: bold; + font-style: italic; + } + .z-markup.z-raw { + background-color: #bfbab0; + } + .z-markup.z-raw.z-inline { + background-color: #bfbab0; + } + .z-meta.z-separator { + color: #798491; + background-color: #bfbab0; + font-weight: bold; + } + .z-markup.z-quote { + color: #f29718; + font-style: italic; + } + .z-markup.z-list.z-numbered.z-bullet, .z-markup.z-list .z-punctuation.z-definition.z-list_item { + color: #95e6cb; + } + .z-markup.z-inserted { + color: #c2d94c; + } + .z-markup.z-changed { + color: #39bae6; + } + .z-markup.z-deleted { + color: #fe7d83; + } + .z-markup.z-strike { + color: #e6b673; + } + .z-markup.z-table { + color: #39bae6; + background-color: #bfbab0; + } + .z-text.z-html.z-markdown .z-markup.z-raw.z-inline { + color: #f29668; + } + .z-text.z-html.z-markdown .z-meta.z-dummy.z-line-break { + color: #798491; + } + .z-markup.z-raw.z-block.z-fenced.z-markdown { + color: #bfbab0; + background-color: #bfbab0; + } + .z-punctuation.z-definition.z-fenced.z-markdown, .z-variable.z-language.z-fenced.z-markdown { + color: #798491; + background-color: #bfbab0; + } + .z-variable.z-language.z-fenced.z-markdown { + color: #798491; + } + .z-markup.z-inserted.z-git_gutter { + color: #c2d94c; + } + .z-markup.z-changed.z-git_gutter { + color: #39bae6; + } + .z-markup.z-deleted.z-git_gutter { + color: #ff7c6d; + } + .z-markup.z-ignored.z-git_gutter { + color: #20272c; + } + .z-markup.z-untracked.z-git_gutter { + color: #20272c; + } + .z-gutter_color { + color: #ffffff; + } + .z-acejump.z-label.z-blue { + color: #ffffff; + background-color: #39bae6; + } + .z-acejump.z-label.z-green { + color: #ffffff; + background-color: #c2d94c; + } + .z-acejump.z-label.z-orange { + color: #ffffff; + background-color: #00bbff; + } + .z-acejump.z-label.z-purple { + color: #ffffff; + background-color: #fe7d83; + } + .z-sublimelinter.z-mark.z-warning { + color: #39bae6; + } + .z-sublimelinter.z-gutter-mark { + color: #ffffff; + } + .z-sublimelinter.z-mark.z-error { + color: #ff7c6d; + } +} +@mixin light { + .z-code { + color: #50565a; + background-color: #fafafa; + } + .z-comment, .z-punctuation.z-definition.z-comment { + color: #676c72; + font-style: italic; + } + .z-variable { + color: #50565a; + } + .z-string, .z-constant.z-other.z-symbol { + color: #497700; + } + .z-constant.z-numeric { + color: #b04e00; + } + .z-string.z-regexp, .z-constant.z-character.z-escape { + color: #007b59; + } + .z-constant.z-language { + color: #b04e00; + } + .z-constant.z-character, .z-constant.z-other { + color: #b04e00; + } + .z-variable.z-member { + color: #c33947; + } + .z-keyword, .z-keyword.z-operator.z-word { + color: #0062e8; + } + .z-keyword.z-operator { + color: #a4552c; + } + .z-punctuation.z-separator, .z-punctuation.z-terminator { + color: #50565a; + } + .z-punctuation.z-section { + color: #50565a; + } + .z-punctuation.z-accessor { + color: #a4552c; + } + .z-punctuation.z-definition.z-annotation { + color: #50565a; + } + .z-variable.z-other.z-dollar.z-only.z-js, .z-variable.z-other.z-object.z-dollar.z-only.z-js, .z-variable.z-type.z-dollar.z-only.z-js, .z-support.z-class.z-dollar.z-only.z-js { + color: #8b6426; + } + .z-storage { + color: #0062e8; + } + .z-storage.z-type { + color: #0062e8; + } + .z-entity.z-name.z-function { + color: #a45600; + } + .z-entity.z-name, .z-entity.z-name.z-tag, .z-entity.z-name.z-label { + color: #0070b3; + } + .z-entity.z-other.z-inherited-class { + color: #0070b3; + text-decoration: underline; + } + .z-variable.z-parameter { + color: #b04e00; + } + .z-variable.z-language { + color: #007492; + font-style: italic; + } + .z-entity.z-name.z-tag, .z-meta.z-tag.z-sgml { + color: #007492; + } + .z-punctuation.z-definition.z-tag.z-end, .z-punctuation.z-definition.z-tag.z-begin, .z-punctuation.z-definition.z-tag { + color: #007492; + } + .z-entity.z-other.z-attribute-name { + color: #a45600; + } + .z-variable.z-function, .z-variable.z-annotation { + color: #a45600; + } + .z-support.z-function, .z-support.z-macro { + color: #b94046; + } + .z-support.z-constant { + color: #a4552c; + font-style: italic; + } + .z-support.z-type, .z-support.z-class { + color: #007492; + font-style: italic; + } + .z-invalid { + color: #da0001; + } + .z-invalid.z-deprecated { + color: #ffffff; + background-color: #0062e8; + } + .z-source.z-ruby .z-variable.z-other.z-readwrite { + color: #a45600; + } + .z-source.z-css .z-entity.z-name.z-tag, .z-source.z-sass .z-entity.z-name.z-tag, .z-source.z-scss .z-entity.z-name.z-tag, .z-source.z-less .z-entity.z-name.z-tag, .z-source.z-stylus .z-entity.z-name.z-tag { + color: #0070b3; + } + .z-source.z-css .z-support.z-type, .z-source.z-sass .z-support.z-type, .z-source.z-scss .z-support.z-type, .z-source.z-less .z-support.z-type, .z-source.z-stylus .z-support.z-type { + color: #676c72; + } + .z-support.z-type.z-property-name { + color: #007492; + } + .z-constant.z-numeric.z-line-number.z-find-in-files { + color: #676c72; + } + .z-constant.z-numeric.z-line-number.z-match { + color: #0062e8; + } + .z-entity.z-name.z-filename.z-find-in-files { + color: #497700; + } + .z-message.z-error { + color: #da0001; + } + .z-source.z-json .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json, .z-source.z-json .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-punctuation.z-definition.z-string { + color: #007492; + } + .z-source.z-json .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json, .z-source.z-json .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-punctuation.z-definition.z-string { + color: #007492; + } + .z-source.z-json .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json, .z-source.z-json .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-punctuation.z-definition.z-string { + color: #007492; + } + .z-source.z-json .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json, .z-source.z-json .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-punctuation.z-definition.z-string { + color: #007492; + } + .z-source.z-json .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json, .z-source.z-json .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-punctuation.z-definition.z-string { + color: #007492; + } + .z-source.z-json .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json, .z-source.z-json .z-meta .z-meta .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-punctuation.z-definition.z-string { + color: #007492; + } + .z-source.z-json .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json, .z-source.z-json .z-meta .z-meta .z-meta .z-meta.z-structure.z-dictionary.z-json .z-punctuation.z-definition.z-string { + color: #007492; + } + .z-source.z-json .z-meta .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json, .z-source.z-json .z-meta .z-meta.z-structure.z-dictionary.z-json .z-punctuation.z-definition.z-string { + color: #007492; + } + .z-source.z-json .z-meta.z-structure.z-dictionary.z-json .z-string.z-quoted.z-double.z-json, .z-source.z-json .z-meta.z-structure.z-dictionary.z-json .z-punctuation.z-definition.z-string { + color: #007492; + } + .z-markup.z-heading { + color: #0062e8; + font-weight: bold; + } + .z-string.z-other.z-link, .z-markup.z-underline.z-link { + color: #007b59; + text-decoration: underline; + font-style: italic; + } + .z-punctuation.z-definition.z-image { + color: #a45600; + } + .z-markup.z-italic { + color: #b94046; + font-style: italic; + } + .z-markup.z-bold { + color: #b94046; + font-weight: bold; + } + .z-markup.z-italic .z-markup.z-bold, .z-markup.z-bold .z-markup.z-italic { + font-weight: bold; + font-style: italic; + } + .z-markup.z-raw { + background-color: #50565a; + } + .z-markup.z-raw.z-inline { + background-color: #50565a; + } + .z-meta.z-separator { + color: #676c72; + background-color: #50565a; + font-weight: bold; + } + .z-markup.z-quote { + color: #b04e00; + font-style: italic; + } + .z-markup.z-list.z-numbered.z-bullet, .z-markup.z-list .z-punctuation.z-definition.z-list_item { + color: #007b59; + } + .z-markup.z-inserted { + color: #497700; + } + .z-markup.z-changed { + color: #007492; + } + .z-markup.z-deleted { + color: #b94046; + } + .z-markup.z-strike { + color: #8b6426; + } + .z-markup.z-table { + color: #007492; + background-color: #50565a; + } + .z-text.z-html.z-markdown .z-markup.z-raw.z-inline { + color: #a4552c; + } + .z-text.z-html.z-markdown .z-meta.z-dummy.z-line-break { + color: #676c72; + } + .z-markup.z-raw.z-block.z-fenced.z-markdown { + color: #50565a; + background-color: #50565a; + } + .z-punctuation.z-definition.z-fenced.z-markdown, .z-variable.z-language.z-fenced.z-markdown { + color: #676c72; + background-color: #50565a; + } + .z-variable.z-language.z-fenced.z-markdown { + color: #676c72; + } + .z-markup.z-inserted.z-git_gutter { + color: #497700; + } + .z-markup.z-changed.z-git_gutter { + color: #007492; + } + .z-markup.z-deleted.z-git_gutter { + color: #da0001; + } + .z-markup.z-ignored.z-git_gutter { + color: #696b6c; + } + .z-markup.z-untracked.z-git_gutter { + color: #696b6c; + } + .z-gutter_color { + color: #ffffff; + } + .z-acejump.z-label.z-blue { + color: #ffffff; + background-color: #007492; + } + .z-acejump.z-label.z-green { + color: #ffffff; + background-color: #497700; + } + .z-acejump.z-label.z-orange { + color: #ffffff; + background-color: #0062e8; + } + .z-acejump.z-label.z-purple { + color: #ffffff; + background-color: #b94046; + } + .z-sublimelinter.z-mark.z-warning { + color: #007492; + } + .z-sublimelinter.z-gutter-mark { + color: #ffffff; + } + .z-sublimelinter.z-mark.z-error { + color: #da0001; + } +} diff --git a/sass/_variables.scss b/sass/_variables.scss new file mode 100644 index 0000000..4114b0a --- /dev/null +++ b/sass/_variables.scss @@ -0,0 +1,13 @@ +@mixin min-screen($min-width: $body-width) { + @media screen and (min-width: $min-width) { + @content; + } +} + +@mixin max-screen($max-width: $body-width) { + @media screen and (max-width: $max-width) { + @content; + } +} + +$sidebar-width: 300px; diff --git a/sass/book.scss b/sass/book.scss new file mode 100644 index 0000000..df1c5f7 --- /dev/null +++ b/sass/book.scss @@ -0,0 +1,12 @@ +@charset "utf-8"; + +@use "normalize"; + +@use "variables"; +@use "document"; +@use "navigation"; +@use "content"; +@use "header"; + +@use "syntax" as syntax; +@include syntax.light; diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000..d4dfd07 Binary files /dev/null and b/screenshot.png differ diff --git a/static/book.js b/static/book.js new file mode 100644 index 0000000..8e4fc10 --- /dev/null +++ b/static/book.js @@ -0,0 +1,223 @@ +function initToggleMenu() { + var $menu = document.querySelector(".menu"); + var $menuIcon = document.querySelector(".menu-icon"); + var $page = document.querySelector(".page"); + $menuIcon.addEventListener("click", function() { + $menu.classList.toggle("menu-hidden"); + $page.classList.toggle("page-without-menu"); + }); +} + +function debounce(func, wait) { + var timeout; + + return function () { + var context = this; + var args = arguments; + clearTimeout(timeout); + + timeout = setTimeout(function () { + timeout = null; + func.apply(context, args); + }, wait); + }; +} + +// Taken from mdbook +// The strategy is as follows: +// First, assign a value to each word in the document: +// Words that correspond to search terms (stemmer aware): 40 +// Normal words: 2 +// First word in a sentence: 8 +// Then use a sliding window with a constant number of words and count the +// sum of the values of the words within the window. Then use the window that got the +// maximum sum. If there are multiple maximas, then get the last one. +// Enclose the terms in . +function makeTeaser(body, terms) { + var TERM_WEIGHT = 40; + var NORMAL_WORD_WEIGHT = 2; + var FIRST_WORD_WEIGHT = 8; + var TEASER_MAX_WORDS = 30; + + var stemmedTerms = terms.map(function (w) { + return elasticlunr.stemmer(w.toLowerCase()); + }); + var termFound = false; + var index = 0; + var weighted = []; // contains elements of ["word", weight, index_in_document] + + // split in sentences, then words + var sentences = body.toLowerCase().split(". "); + + for (var i in sentences) { + var words = sentences[i].split(" "); + var value = FIRST_WORD_WEIGHT; + + for (var j in words) { + var word = words[j]; + + if (word.length > 0) { + for (var k in stemmedTerms) { + if (elasticlunr.stemmer(word).startsWith(stemmedTerms[k])) { + value = TERM_WEIGHT; + termFound = true; + } + } + weighted.push([word, value, index]); + value = NORMAL_WORD_WEIGHT; + } + + index += word.length; + index += 1; // ' ' or '.' if last word in sentence + } + + index += 1; // because we split at a two-char boundary '. ' + } + + if (weighted.length === 0) { + return body; + } + + var windowWeights = []; + var windowSize = Math.min(weighted.length, TEASER_MAX_WORDS); + // We add a window with all the weights first + var curSum = 0; + for (var i = 0; i < windowSize; i++) { + curSum += weighted[i][1]; + } + windowWeights.push(curSum); + + for (var i = 0; i < weighted.length - windowSize; i++) { + curSum -= weighted[i][1]; + curSum += weighted[i + windowSize][1]; + windowWeights.push(curSum); + } + + // If we didn't find the term, just pick the first window + var maxSumIndex = 0; + if (termFound) { + var maxFound = 0; + // backwards + for (var i = windowWeights.length - 1; i >= 0; i--) { + if (windowWeights[i] > maxFound) { + maxFound = windowWeights[i]; + maxSumIndex = i; + } + } + } + + var teaser = []; + var startIndex = weighted[maxSumIndex][2]; + for (var i = maxSumIndex; i < maxSumIndex + windowSize; i++) { + var word = weighted[i]; + if (startIndex < word[2]) { + // missing text from index to start of `word` + teaser.push(body.substring(startIndex, word[2])); + startIndex = word[2]; + } + + // add around search terms + if (word[1] === TERM_WEIGHT) { + teaser.push(""); + } + startIndex = word[2] + word[0].length; + teaser.push(body.substring(word[2], startIndex)); + + if (word[1] === TERM_WEIGHT) { + teaser.push(""); + } + } + teaser.push("…"); + return teaser.join(""); +} + +function formatSearchResultItem(item, terms) { + var li = document.createElement("li"); + li.classList.add("search-results__item"); + li.innerHTML = `${item.doc.title}`; + li.innerHTML += `
${makeTeaser(item.doc.body, terms)}
`; + return li; +} + +// Go from the book view to the search view +function toggleSearchMode() { + var $bookContent = document.querySelector(".book-content"); + var $searchContainer = document.querySelector(".search-container"); + if ($searchContainer.classList.contains("search-container--is-visible")) { + $searchContainer.classList.remove("search-container--is-visible"); + document.body.classList.remove("search-mode"); + $bookContent.style.display = "block"; + } else { + $searchContainer.classList.add("search-container--is-visible"); + document.body.classList.add("search-mode"); + $bookContent.style.display = "none"; + document.getElementById("search").focus(); + } +} + +function initSearch() { + var $searchInput = document.getElementById("search"); + if (!$searchInput) { + return; + } + var $searchIcon = document.querySelector(".search-icon"); + $searchIcon.addEventListener("click", toggleSearchMode); + + var $searchResults = document.querySelector(".search-results"); + var $searchResultsHeader = document.querySelector(".search-results__header"); + var $searchResultsItems = document.querySelector(".search-results__items"); + var MAX_ITEMS = 10; + + var options = { + bool: "AND", + fields: { + title: {boost: 2}, + body: {boost: 1}, + } + }; + var currentTerm = ""; + var index = elasticlunr.Index.load(window.searchIndex); + + $searchInput.addEventListener("keyup", debounce(function() { + var term = $searchInput.value.trim(); + if (term === currentTerm || !index) { + return; + } + $searchResults.style.display = term === "" ? "none" : "block"; + $searchResultsItems.innerHTML = ""; + if (term === "") { + return; + } + + var results = index.search(term, options).filter(function (r) { + return r.doc.body !== ""; + }); + if (results.length === 0) { + $searchResultsHeader.innerText = `No search results for '${term}'.`; + return; + } + + currentTerm = term; + $searchResultsHeader.innerText = `${results.length} search results for '${term}':`; + for (var i = 0; i < Math.min(results.length, MAX_ITEMS); i++) { + if (!results[i].doc.body) { + continue; + } + // var item = document.createElement("li"); + // item.innerHTML = formatSearchResultItem(results[i], term.split(" ")); + console.log(results[i]); + $searchResultsItems.appendChild(formatSearchResultItem(results[i], term.split(" "))); + } + }, 150)); +} + +if (document.readyState === "complete" || + (document.readyState !== "loading" && !document.documentElement.doScroll) +) { + initToggleMenu(); +} else { + document.addEventListener("DOMContentLoaded", function () { + initToggleMenu(); + initSearch(); + }); +} diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..900fdb1 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,130 @@ + + + + + + + + + {% if page %} + {% if page.description %} + + {% elif config.description %} + + {% endif %} + {% elif config.description %} + + {% endif %} + + {% block title %}{{ config.title }}{% endblock title %} + + {% block js %} + {% endblock js %} + + + {% block css %} + + {% endblock css %} + + {% block extra_head %} + {% endblock extra_head %} + + + + + + + + +
+ + +
+ {% if config.build_search_index %} +
+ +
+
+
    +
    +
    + {% endif %} +
    + {% block content %} + {% endblock content %} +
    +
    + + + + +
    + + {% block js_body %} + {% if config.build_search_index %} + + + {% endif %} + + {% endblock js_body %} + + + diff --git a/templates/page.html b/templates/page.html new file mode 100644 index 0000000..d22c2dd --- /dev/null +++ b/templates/page.html @@ -0,0 +1,40 @@ +{% extends "index.html" %} + + +{% block content %} +

    {{ page.title }}

    + {{ page.content | safe }} +{% endblock content %} + +{% block prev_link %} + {% if page.smaller %} + + {% else %} + {# No page before, find the link for the section it's in if there is one #} + {% set parent = get_section(path=page.ancestors | reverse | first) %} + + {% endif %} +{% endblock prev_link %} + +{% block next_link %} + {% if page.higher %} + + {% else %} + {# No page after, find the link for the following section #} + {% set index = get_section(path="_index.md") %} + {% set found_current = false %} + {% for s in index.subsections %} + {% set subsection = get_section(path=s) %} + {% if found_current %} + + {# no break #} + {% set_global found_current = false %} + {% endif %} + {% for p in subsection.pages %} + {% if p.permalink == page.permalink %} + {% set_global found_current = true %} + {% endif %} + {% endfor %} + {% endfor %} + {% endif %} +{% endblock next_link %} diff --git a/templates/section.html b/templates/section.html new file mode 100644 index 0000000..1673ab2 --- /dev/null +++ b/templates/section.html @@ -0,0 +1,52 @@ +{% extends "index.html" %} + +{% block content %} +

    {{ section.title }}

    + {{ section.content | safe }} +{% endblock content %} + +{% block prev_link %} + {# need to find the last page of the previous section or the previous section directly + if there isn't any pages in it #} + {% set index = get_section(path="_index.md") %} + {% set found_current = false %} + {% for s in index.subsections | reverse %} + {% set subsection = get_section(path=s) %} + {% if subsection.permalink == section.permalink %} + {% set_global found_current = true %} + {% else %} + {% if found_current %} + {% if subsection.pages %} + {% set last_page = subsection.pages | last %} + + {% else %} + + {% endif %} + {# no break #} + {% set_global found_current = false %} + {% endif %} + {% endif %} + {% endfor %} +{% endblock prev_link %} + +{% block next_link %} + {% if section.pages %} + {% set next_page = section.pages | first %} + + {% else %} + {# No page in the section, find the link for the following section #} + {% set index = get_section(path="_index.md") %} + {% set found_current = false %} + {% for s in index.subsections %} + {% set subsection = get_section(path=s) %} + {% if found_current %} + + {# no break #} + {% set_global found_current = false %} + {% endif %} + {% if subsection.permalink == section.permalink %} + {% set_global found_current = true %} + {% endif %} + {% endfor %} + {% endif %} +{% endblock next_link %} diff --git a/theme.toml b/theme.toml new file mode 100644 index 0000000..b343822 --- /dev/null +++ b/theme.toml @@ -0,0 +1,13 @@ +name = "book" +description = "A book theme inspired from GitBook/mdBook" +license = "MIT" +homepage = "https://github.com/getzola/book" +min_version = "0.17.0" +demo = "https://getzola.github.io/book/" + +[extra] +book_number_chapters = true + +[author] +name = "Vincent Prouillet" +homepage = "https://www.vincentprouillet.com"