docs_ci: check all md files except thirdparty (#6855)
parent
d8f64f516b
commit
df4165c7ee
|
@ -3,14 +3,10 @@ name: CI
|
|||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- "doc/**"
|
||||
- "CHANGELOG.md"
|
||||
- "CONTRIBUTING.md"
|
||||
- "**.md"
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- "doc/**"
|
||||
- "CHANGELOG.md"
|
||||
- "CONTRIBUTING.md"
|
||||
- "**.md"
|
||||
|
||||
jobs:
|
||||
code-formatting:
|
||||
|
|
|
@ -3,23 +3,21 @@ name: Docs CI
|
|||
on:
|
||||
push:
|
||||
paths:
|
||||
- ".github/workflows/docs_ci.yml"
|
||||
- "cmd/tools/check-md.v"
|
||||
- "doc/**"
|
||||
- "CHANGELOG.md"
|
||||
- "CONTRIBUTING.md"
|
||||
- "**.md"
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/docs_ci.yml"
|
||||
- "cmd/tools/check-md.v"
|
||||
- "doc/**"
|
||||
- "CHANGELOG.md"
|
||||
- "CONTRIBUTING.md"
|
||||
- "**.md"
|
||||
|
||||
jobs:
|
||||
docs-line-len-check:
|
||||
check-markdown:
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Build V
|
||||
run: make
|
||||
- name: Check docs line length & code examples
|
||||
run: ./v run cmd/tools/check-md.v doc/docs.md doc/upcoming.md CHANGELOG.md CONTRIBUTING.md
|
||||
- name: Check markdown line length & code examples
|
||||
run: ./v run cmd/tools/check-md.v -all
|
||||
|
|
10
README.md
10
README.md
|
@ -24,7 +24,8 @@
|
|||
## Key Features of V
|
||||
|
||||
- Simplicity: the language can be learned in less than an hour
|
||||
- Fast compilation: ≈80k loc/s with a Clang backend, ≈1 million loc/s with x64 and tcc backends *(Intel i5-7500, SSD, no optimization)*
|
||||
- Fast compilation: ≈80k loc/s with a Clang backend,
|
||||
≈1 million loc/s with x64 and tcc backends *(Intel i5-7500, SSD, no optimization)*
|
||||
- Easy to develop: V compiles itself in less than a second
|
||||
- Performance: as fast as C (V's main backend compiles to human readable C)
|
||||
- Safety: no null, no globals, no undefined behavior, immutability by default
|
||||
|
@ -80,7 +81,8 @@ v up
|
|||
|
||||
### C compiler
|
||||
|
||||
It's recommended to use Clang or GCC or Visual Studio. If you are doing development, you most likely already have one of those installed.
|
||||
It's recommended to use Clang or GCC or Visual Studio.
|
||||
If you are doing development, you most likely already have one of those installed.
|
||||
|
||||
Otherwise, follow these instructions:
|
||||
|
||||
|
@ -88,7 +90,9 @@ Otherwise, follow these instructions:
|
|||
|
||||
- [Installing a C compiler on Windows](https://github.com/vlang/v/wiki/Installing-a-C-compiler-on-Windows)
|
||||
|
||||
However, if none is found when running `make` on Linux or Windows, TCC would be downloaded and set as an alternative C backend. It's very lightweight (several MB) so this shouldn't take too long.
|
||||
However, if none is found when running `make` on Linux or Windows,
|
||||
TCC would be downloaded and set as an alternative C backend.
|
||||
It's very lightweight (several MB) so this shouldn't take too long.
|
||||
|
||||
### Symlinking
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ const (
|
|||
)
|
||||
|
||||
fn main() {
|
||||
files_paths := os.args[1..]
|
||||
files_paths := if '-all' in os.args { md_file_paths() } else { os.args[1..] }
|
||||
mut warnings := 0
|
||||
mut errors := 0
|
||||
mut oks := 0
|
||||
|
@ -28,7 +28,13 @@ fn main() {
|
|||
}
|
||||
for i, line in lines {
|
||||
if line.len > too_long_line_length {
|
||||
if line.starts_with('|') {
|
||||
if mdfile.state == .vexample {
|
||||
println(wline(file_path, i, line.len, 'long V example line'))
|
||||
warnings++
|
||||
} else if mdfile.state == .codeblock {
|
||||
println(wline(file_path, i, line.len, 'long code block line'))
|
||||
warnings++
|
||||
} else if line.starts_with('|') {
|
||||
println(wline(file_path, i, line.len, 'long table'))
|
||||
warnings++
|
||||
} else if line.contains('https') {
|
||||
|
@ -57,6 +63,18 @@ fn main() {
|
|||
}
|
||||
}
|
||||
|
||||
fn md_file_paths() []string {
|
||||
mut files_to_check := []string{}
|
||||
md_files := os.walk_ext('.', '.md')
|
||||
for file in md_files {
|
||||
if file.starts_with('./thirdparty') {
|
||||
continue
|
||||
}
|
||||
files_to_check << file
|
||||
}
|
||||
return files_to_check
|
||||
}
|
||||
|
||||
fn ftext(s string, cb fn (string) string) string {
|
||||
if term_colors {
|
||||
return cb(s)
|
||||
|
@ -100,6 +118,7 @@ mut:
|
|||
enum MDFileParserState {
|
||||
markdown
|
||||
vexample
|
||||
codeblock
|
||||
}
|
||||
|
||||
struct MDFile {
|
||||
|
@ -125,12 +144,24 @@ fn (mut f MDFile) parse_line(lnumber int, line string) {
|
|||
}
|
||||
return
|
||||
}
|
||||
if line.starts_with('```') && f.state == .vexample {
|
||||
f.state = .markdown
|
||||
f.current.eline = lnumber
|
||||
f.examples << f.current
|
||||
f.current = VCodeExample{}
|
||||
return
|
||||
if line.starts_with('```') {
|
||||
match f.state {
|
||||
.vexample {
|
||||
f.state = .markdown
|
||||
f.current.eline = lnumber
|
||||
f.examples << f.current
|
||||
f.current = VCodeExample{}
|
||||
return
|
||||
}
|
||||
.codeblock {
|
||||
f.state = .markdown
|
||||
return
|
||||
}
|
||||
.markdown {
|
||||
f.state = .codeblock
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
if f.state == .vexample {
|
||||
f.current.text << line
|
||||
|
|
|
@ -3,10 +3,8 @@
|
|||
![](https://github.com/fuyutarow/Conways-Game-of-Life-with-Vlang/raw/master/v-gun.gif)
|
||||
|
||||
|
||||
```v
|
||||
```
|
||||
v run life.v
|
||||
```
|
||||
|
||||
Created by fuyutarow: https://github.com/fuyutarow/Conways-Game-of-Life-with-Vlang
|
||||
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ As you can see, there are no routing rules. The `index()` action handles the `/`
|
|||
Vweb often uses convention over configuration and adding a new action requires
|
||||
no routing rules either:
|
||||
|
||||
```v
|
||||
```v oksyntax
|
||||
fn (mut app App) time() vweb.Result {
|
||||
app.vweb.text(time.now().format())
|
||||
return vweb.Result{}
|
||||
|
@ -132,7 +132,7 @@ Let's return an HTML view instead. Create `index.html` in the same directory:
|
|||
|
||||
and update our `index()` action so that it returns the HTML view we just created:
|
||||
|
||||
```v
|
||||
```v ignore
|
||||
pub fn (mut app App) index() vweb.Result {
|
||||
message := 'Hello, world from Vweb!'
|
||||
return $vweb.html()
|
||||
|
@ -158,7 +158,8 @@ but V is a language with pure functions by default, and you won't be able
|
|||
to modify any data from a view. `<b>@foo.bar()</b>` will only work if the `bar()` method
|
||||
doesn't modify `foo`.
|
||||
|
||||
The HTML template is compiled to V during the compilation of the website, that's done by the `$vweb.html()` line.
|
||||
The HTML template is compiled to V during the compilation of the website,
|
||||
that's done by the `$vweb.html()` line.
|
||||
(`$` always means compile time actions in V.) offering the following benefits:
|
||||
|
||||
- Great performance, since the templates don't need to be compiled
|
||||
|
@ -203,7 +204,7 @@ Run the file with `sqlite3 blog.db < blog.sqlite`.
|
|||
|
||||
Add a SQLite handle to `App`:
|
||||
|
||||
```v
|
||||
```v oksyntax
|
||||
import sqlite
|
||||
|
||||
struct App {
|
||||
|
@ -217,7 +218,7 @@ pub mut:
|
|||
|
||||
Modify the `init_once()` method we created earlier to connect to a database:
|
||||
|
||||
```v
|
||||
```v oksyntax
|
||||
pub fn (mut app App) init_once() {
|
||||
db := sqlite.connect(':memory:') or { panic(err) }
|
||||
db.exec('create table `Article` (id integer primary key, title text default "", text text default "")')
|
||||
|
@ -233,7 +234,7 @@ to have one DB connection for all requests.
|
|||
Create a new file `article.v`:
|
||||
|
||||
|
||||
```v
|
||||
```v oksyntax
|
||||
// article.v
|
||||
module main
|
||||
|
||||
|
@ -252,7 +253,7 @@ pub fn (app &App) find_all_articles() []Article {
|
|||
|
||||
Let's fetch the articles in the `index()` action:
|
||||
|
||||
```v
|
||||
```v ignore
|
||||
pub fn (app &App) index() vweb.Result {
|
||||
articles := app.find_all_articles()
|
||||
return $vweb.html()
|
||||
|
@ -284,7 +285,7 @@ That was very simple, wasn't it?
|
|||
The built-in V ORM uses a syntax very similar to SQL. The queries are built with V.
|
||||
For example, if we only wanted to find articles with ids between 100 and 200, we'd do:
|
||||
|
||||
```
|
||||
```v oksyntax
|
||||
return sql app.db {
|
||||
select from Article where id >= 100 && id <= 200
|
||||
}
|
||||
|
@ -292,8 +293,7 @@ return sql app.db {
|
|||
|
||||
Retrieving a single article is very simple:
|
||||
|
||||
```v
|
||||
|
||||
```v oksyntax
|
||||
pub fn (app &App) retrieve_article() ?Article {
|
||||
return sql app.db {
|
||||
select from Article limit 1
|
||||
|
@ -304,7 +304,7 @@ pub fn (app &App) retrieve_article() ?Article {
|
|||
V ORM uses V's optionals for single values, which is very useful, since
|
||||
bad queries will always be handled by the developer:
|
||||
|
||||
```v
|
||||
```v oksyntax
|
||||
article := app.retrieve_article(10) or {
|
||||
app.vweb.text('Article not found')
|
||||
return
|
||||
|
@ -331,7 +331,7 @@ Create `new.html`:
|
|||
</html>
|
||||
```
|
||||
|
||||
```v
|
||||
```v oksyntax
|
||||
pub fn (mut app App) new_article() vweb.Result {
|
||||
title := app.vweb.form['title']
|
||||
text := app.vweb.form['text']
|
||||
|
@ -369,7 +369,7 @@ This tutorial used the traditional server-side rendering. If you prefer
|
|||
to render everything on the client or need an API, creating JSON endpoints
|
||||
in V is very simple:
|
||||
|
||||
```v
|
||||
```v oksyntax
|
||||
pub fn (mut app App) articles() vweb.Result {
|
||||
articles := app.find_all_articles()
|
||||
app.vweb.json(json.encode(articles))
|
||||
|
|
|
@ -10,7 +10,8 @@ A module to provide eventing capabilities using pub/sub.
|
|||
|
||||
**EventBus:**
|
||||
|
||||
1. `publish(name string, sender voidptr, args voidptr)` - publish an event with provided Params & name
|
||||
1. `publish(name string, sender voidptr, args voidptr)` - publish an event with provided
|
||||
Params & name
|
||||
2. `clear_all()` - clear all subscribers
|
||||
3. `has_subscriber(name string)` - check if a subscriber to an event exists
|
||||
|
||||
|
@ -18,7 +19,9 @@ A module to provide eventing capabilities using pub/sub.
|
|||
|
||||
1. `subscribe(name string, handler EventHandlerFn)` - subscribe to an event
|
||||
2. `subscribe_once(name string, handler EventHandlerFn)` - subscribe only once to an event
|
||||
3. `subscribe_method(name string, handler EventHandlerFn, receiver voidptr)` - subscribe to an event and also set the `receiver` as a parameter. Since it's not yet possible to send methods as parameters, this is a workaround.
|
||||
3. `subscribe_method(name string, handler EventHandlerFn, receiver voidptr)` - subscribe to
|
||||
an event and also set the `receiver` as a parameter.
|
||||
Since it's not yet possible to send methods as parameters, this is a workaround.
|
||||
4. `is_subscribed(name string)` - check if we are subscribed to an event
|
||||
5. `unsubscribe(name string)` - unsubscribe from an event
|
||||
|
||||
|
@ -26,9 +29,8 @@ A module to provide eventing capabilities using pub/sub.
|
|||
|
||||
The function given to `subscribe`, `subscribe_method` and `subscribe_once` must match this:
|
||||
|
||||
```v
|
||||
fn(receiver voidptr, args voidptr, sender voidptr){
|
||||
|
||||
```v oksyntax
|
||||
fn(receiver voidptr, args voidptr, sender voidptr) {
|
||||
}
|
||||
|
||||
// Since V can map structs to voidptr, this also works
|
||||
|
@ -52,7 +54,7 @@ _Note: As a general rule, you will need to **subscribe before publishing**._
|
|||
|
||||
**main.v**
|
||||
|
||||
```v
|
||||
```v oksyntax
|
||||
module main
|
||||
import eventbus
|
||||
|
||||
|
@ -78,7 +80,7 @@ fn on_error(receiver voidptr, e &Error, work &Work) {
|
|||
|
||||
**work.v**
|
||||
|
||||
```v
|
||||
```v oksyntax
|
||||
module main
|
||||
|
||||
struct Work{
|
||||
|
@ -100,12 +102,15 @@ fn do_work(){
|
|||
|
||||
### Notes:
|
||||
|
||||
1. Each `EventBus` instance has it's own registry (i.e. there is no global event registry so you can't just subscribe to an event wherever you are.
|
||||
2. Each `EventBus` has a `Subscriber` instance which will need to be either exposed or you can make small public helper functions specific to your module like (`onPress`, `onError`) and etc.
|
||||
3. The `eventbus` module has some helpers to ease getting/setting of Params (since V doesn't support empty interfaces yet or reflection) so use them (see usage above).
|
||||
1. Each `EventBus` instance has it's own registry (i.e. there is no global event registry
|
||||
so you can't just subscribe to an event wherever you are.
|
||||
2. Each `EventBus` has a `Subscriber` instance which will need to be either exposed or you can make
|
||||
small public helper functions specific to your module like (`onPress`, `onError`) and etc.
|
||||
3. The `eventbus` module has some helpers to ease getting/setting of Params
|
||||
(since V doesn't support empty interfaces yet or reflection) so use them (see usage above).
|
||||
|
||||
**The rationale behind separating Subscriber & Publisher:**
|
||||
|
||||
This is mainly for security because if publisher & subscriber are both passed around,
|
||||
a client can easily publish events acting as the server.
|
||||
This is mainly for security because if publisher & subscriber are both passed around,
|
||||
a client can easily publish events acting as the server.
|
||||
So a client should only be able to use the Subscriber methods.
|
||||
|
|
|
@ -1,86 +1,95 @@
|
|||
# V HTML
|
||||
|
||||
A HTML parser made in V
|
||||
A HTML parser made in V.
|
||||
|
||||
## Usage
|
||||
|
||||
If description below isn't enought, see test files
|
||||
If the description below isn't enought, please look at the test files.
|
||||
|
||||
### Parser
|
||||
|
||||
Responsible for read HTML in full strings or splited string and returns all Tag objets of it HTML or return a DocumentObjectModel, that will try to find how the HTML Tree is.
|
||||
Responsible for read HTML in full strings or splited string and returns all Tag objets of
|
||||
it HTML or return a DocumentObjectModel, that will try to find how the HTML Tree is.
|
||||
|
||||
#### split_parse(data string)
|
||||
This functions is the main function called by parse method to fragment parse your HTML
|
||||
This functions is the main function called by parse method to fragment parse your HTML.
|
||||
|
||||
#### parse_html(data string, is_file bool)
|
||||
This function is called passing a filename or a complete html data string to it
|
||||
This function is called passing a filename or a complete html data string to it.
|
||||
|
||||
#### add_code_tag(name string)
|
||||
This function is used to add a tag for the parser ignore it's content. For example, if you have an html or XML with a custom tag, like `<script>`, using this function, like `add_code_tag('script')` will make all `script` tags content be jumped, so you still have its content, but will not confuse the parser with it's `>` or `<`
|
||||
This function is used to add a tag for the parser ignore it's content.
|
||||
For example, if you have an html or XML with a custom tag, like `<script>`, using this function,
|
||||
like `add_code_tag('script')` will make all `script` tags content be jumped,
|
||||
so you still have its content, but will not confuse the parser with it's `>` or `<`.
|
||||
|
||||
#### finalize()
|
||||
When using **split_parse** method, you must call this function to ends the parse completely
|
||||
When using **split_parse** method, you must call this function to ends the parse completely.
|
||||
|
||||
#### get_tags() []Tag_ptr
|
||||
This functions returns a array with all tags and it's content
|
||||
This functions returns a array with all tags and it's content.
|
||||
|
||||
#### get_dom() DocumentObjectModel
|
||||
Returns the DocumentObjectModel for current parsed tags
|
||||
Returns the DocumentObjectModel for current parsed tags.
|
||||
|
||||
### WARNING
|
||||
If you want to reuse parser object to parse another HTML, call `initialize_all()` function first
|
||||
If you want to reuse parser object to parse another HTML, call `initialize_all()` function first.
|
||||
|
||||
### DocumentObjectModel
|
||||
|
||||
A DOM object that will make easier to access some tags and search it
|
||||
A DOM object that will make easier to access some tags and search it.
|
||||
|
||||
#### get_by_attribute_value(name string, value string) []Tag_ptr
|
||||
This function retuns a Tag array with all tags in document that have a attribute with given name and given value
|
||||
This function retuns a Tag array with all tags in document
|
||||
that have a attribute with given name and given value.
|
||||
|
||||
#### get_by_tag(name string) []Tag_ptr
|
||||
This function retuns a Tag array with all tags in document that have a name with the given value
|
||||
This function retuns a Tag array with all tags in document that have a name with the given value.
|
||||
|
||||
#### get_by_attribute(name string) []Tag_ptr
|
||||
This function retuns a Tag array with all tags in document that have a attribute with given name
|
||||
This function retuns a Tag array with all tags in document that have a attribute with given name.
|
||||
|
||||
#### get_root() Tag_ptr
|
||||
This function returns the root Tag
|
||||
This function returns the root Tag.
|
||||
|
||||
#### get_all_tags() []Tag_ptr
|
||||
This function returns all important tags, removing close tags
|
||||
This function returns all important tags, removing close tags.
|
||||
|
||||
### Tag
|
||||
|
||||
An object that holds tags information, such as `name`, `attributes`, `children`
|
||||
An object that holds tags information, such as `name`, `attributes`, `children`.
|
||||
|
||||
#### get_children() []Tag_ptr
|
||||
Returns all children as an array
|
||||
Returns all children as an array.
|
||||
|
||||
#### get_parent() &Tag
|
||||
Returns the parent of current tag
|
||||
Returns the parent of current tag.
|
||||
|
||||
#### get_name() string
|
||||
Returns tag name
|
||||
Returns tag name.
|
||||
|
||||
#### get_content() string
|
||||
Returns tag content
|
||||
Returns tag content.
|
||||
|
||||
#### get_attributes() map[string]string
|
||||
Returns all attributes and it value
|
||||
Returns all attributes and it value.
|
||||
|
||||
#### text() string
|
||||
Returns the content of the tag and all tags inside it. Also, any `<br>` tag will be converted into `\n`
|
||||
Returns the content of the tag and all tags inside it.
|
||||
Also, any `<br>` tag will be converted into `\n`.
|
||||
|
||||
## Some questions that can appear
|
||||
|
||||
### Q: Why in parser have a `builder_str() string` method that returns only the lexeme string?
|
||||
|
||||
A: Because in early stages of the project, strings.Builder are used, but for some bug existing somewhere, it was necessary to use string directly. Later, it's planned to use strings.Builder again
|
||||
A: Because in early stages of the project, `strings.Builder` are used,
|
||||
but for some bug existing somewhere, it was necessary to use `string` directly.
|
||||
Later, it's planned to use `strings.Builder` again.
|
||||
|
||||
### Q: Why have a `compare_string(a string, b string) bool` method?
|
||||
|
||||
A: For some reason when using != and == in strings directly, it not working. So, this method is a workaround
|
||||
A: For some reason when using != and == in strings directly, it is not working.
|
||||
So this method is a workaround.
|
||||
|
||||
### Q: Will be something like `XPath`?
|
||||
|
||||
|
|
|
@ -14,30 +14,45 @@ The V `rand` module provides two main ways in which users can generate pseudoran
|
|||
|
||||
# General Background
|
||||
|
||||
A PRNG is a Pseudo Random Number Generator. Computers cannot generate truly random numbers without an external source of noise or entropy. We can use algorithms to generate sequences of seemingly random numbers, but their outputs will always be deterministic. This is often useful for simulations that need the same starting seed.
|
||||
A PRNG is a Pseudo Random Number Generator.
|
||||
Computers cannot generate truly random numbers without an external source of noise or entropy.
|
||||
We can use algorithms to generate sequences of seemingly random numbers,
|
||||
but their outputs will always be deterministic.
|
||||
This is often useful for simulations that need the same starting seed.
|
||||
|
||||
If you need truly random numbers that are going to be used for cryptography, use the `crypto.rand` module.
|
||||
If you need truly random numbers that are going to be used for cryptography,
|
||||
use the `crypto.rand` module.
|
||||
|
||||
# Guaranteed functions
|
||||
|
||||
The following 21 functions are guaranteed to be supported by `rand` as well as the individual PRNGs.
|
||||
The following 21 functions are guaranteed to be supported by `rand`
|
||||
as well as the individual PRNGs.
|
||||
|
||||
- `seed(seed_data)` where `seed_data` is an array of `u32` values. Different generators require different number of bits as the initial seed. The smallest is 32-bits, required by `sys.SysRNG`. Most others require 64-bits or 2 `u32` values.
|
||||
- `seed(seed_data)` where `seed_data` is an array of `u32` values.
|
||||
Different generators require different number of bits as the initial seed.
|
||||
The smallest is 32-bits, required by `sys.SysRNG`.
|
||||
Most others require 64-bits or 2 `u32` values.
|
||||
- `u32()`, `u64()`, `int()`, `i64()`, `f32()`, `f64()`
|
||||
- `u32n(max)`, `u64n(max)`, `intn(max)`, `i64n(max)`, `f32n(max)`, `f64n(max)`
|
||||
- `u32_in_range(min, max)`, `u64_in_range(min, max)`, `int_in_range(min, max)`, `i64_in_range(min, max)`, `f32_in_range(min, max)`, `f64_in_range(min, max)`
|
||||
- `u32_in_range(min, max)`, `u64_in_range(min, max)`, `int_in_range(min, max)`,
|
||||
`i64_in_range(min, max)`, `f32_in_range(min, max)`, `f64_in_range(min, max)`
|
||||
- `int31()`, `int63()`
|
||||
|
||||
# Utility Functions
|
||||
|
||||
All the generators are time-seeded. The helper functions publicly available in `rand.util` module are:
|
||||
All the generators are time-seeded.
|
||||
The helper functions publicly available in `rand.util` module are:
|
||||
|
||||
1. `time_seed_array()` - returns a `[]u32` that can be directly plugged into the `seed()` functions.
|
||||
2. `time_seed_32()` and `time_seed_64()` - 32-bit and 64-bit values respectively that are generated from the current time.
|
||||
2. `time_seed_32()` and `time_seed_64()` - 32-bit and 64-bit values respectively
|
||||
that are generated from the current time.
|
||||
|
||||
# Caveats
|
||||
|
||||
Note that the `sys.SysRNG` struct (in the C backend) uses `C.srand()` which sets the seed globally. Consequently, all instances of the RNG will be affected. This problem does not arise for the other RNGs. A workaround (if you _must_ use the libc RNG) is to:
|
||||
Note that the `sys.SysRNG` struct (in the C backend) uses `C.srand()` which sets the seed globally.
|
||||
Consequently, all instances of the RNG will be affected.
|
||||
This problem does not arise for the other RNGs.
|
||||
A workaround (if you _must_ use the libc RNG) is to:
|
||||
|
||||
1. Seed the first instance.
|
||||
2. Generate all values required.
|
||||
|
|
|
@ -8,10 +8,12 @@ Write here the introduction... not today!! -_-
|
|||
|
||||
## Basic assumption
|
||||
|
||||
In this release, during the writing of the code some assumptions are made and are valid for all the features.
|
||||
In this release, during the writing of the code some assumptions are made
|
||||
and are valid for all the features.
|
||||
|
||||
1. The matching stops at the end of the string not at the newline chars.
|
||||
2. The basic elements of this regex engine are the tokens, in a query string a simple char is a token. The token is the atomic unit of this regex engine.
|
||||
2. The basic elements of this regex engine are the tokens,
|
||||
in a query string a simple char is a token. The token is the atomic unit of this regex engine.
|
||||
|
||||
## Match positional limiter
|
||||
|
||||
|
@ -37,19 +39,26 @@ The cc matches all the chars specified inside, it is delimited by square bracket
|
|||
|
||||
the sequence of chars in the class is evaluated with an OR operation.
|
||||
|
||||
For example, the following cc `[abc]` matches any char that is `a` or `b` or `c` but doesn't match `C` or `z`.
|
||||
For example, the following cc `[abc]` matches any char that is `a` or `b` or `c`
|
||||
but doesn't match `C` or `z`.
|
||||
|
||||
Inside a cc is possible to specify a "range" of chars, for example `[ad-f]` is equivalent to write `[adef]`.
|
||||
Inside a cc is possible to specify a "range" of chars,
|
||||
for example `[ad-f]` is equivalent to write `[adef]`.
|
||||
|
||||
A cc can have different ranges at the same time like `[a-zA-z0-9]` that matches all the lowercase,uppercase and numeric chars.
|
||||
A cc can have different ranges at the same time like `[a-zA-z0-9]` that matches all the lowercase,
|
||||
uppercase and numeric chars.
|
||||
|
||||
It is possible negate the cc using the caret char at the start of the cc like: `[^abc]` that matches every char that is not `a` or `b` or `c`.
|
||||
It is possible negate the cc using the caret char at the start of the cc like: `[^abc]`
|
||||
that matches every char that is not `a` or `b` or `c`.
|
||||
|
||||
A cc can contain meta-chars like: `[a-z\d]` that matches all the lowercase latin chars `a-z` and all the digits `\d`.
|
||||
A cc can contain meta-chars like: `[a-z\d]` that matches all the lowercase latin chars `a-z`
|
||||
and all the digits `\d`.
|
||||
|
||||
It is possible to mix all the properties of the char class together.
|
||||
|
||||
**Note:** In order to match the `-` (minus) char, it must be located at the first position in the cc, for example `[-_\d\a]` will match `-` minus, `_`underscore, `\d` numeric chars, `\a` lower case chars.
|
||||
**Note:** In order to match the `-` (minus) char, it must be located at the first position
|
||||
in the cc, for example `[-_\d\a]` will match `-` minus, `_`underscore, `\d` numeric chars,
|
||||
`\a` lower case chars.
|
||||
|
||||
### Meta-chars
|
||||
|
||||
|
@ -63,7 +72,7 @@ A meta-char can match different type of chars.
|
|||
* `\D` matches a non digit
|
||||
* `\s`matches a space char, one of `[' ','\t','\n','\r','\v','\f']`
|
||||
* `\S` matches a non space char
|
||||
* `\a` matches only a lowercase char `[a-z]`
|
||||
* `\a` matches only a lowercase char `[a-z]`
|
||||
* `\A` matches only an uppercase char `[A-Z]`
|
||||
|
||||
### Quantifier
|
||||
|
@ -80,16 +89,21 @@ Each token can have a quantifier that specify how many times the char can or mus
|
|||
|
||||
- `{x}` matches exactly x time, `a{2}` matches `aa` but doesn't match `aaa` or `a`
|
||||
- `{min,}` matches at minimum min time, `a{2,}` matches `aaa` or `aa` but doesn't match `a`
|
||||
- `{,max}` matches at least 0 time and maximum max time, `a{,2}` matches `a` and `aa` but doesn't match `aaa`
|
||||
- `{min,max}` matches from min times to max times, `a{2,3}` matches `aa` and `aaa` but doesn't match `a` or `aaaa`
|
||||
- `{,max}` matches at least 0 time and maximum max time,
|
||||
`a{,2}` matches `a` and `aa` but doesn't match `aaa`
|
||||
- `{min,max}` matches from min times to max times,
|
||||
`a{2,3}` matches `aa` and `aaa` but doesn't match `a` or `aaaa`
|
||||
|
||||
a long quantifier may have a `greedy off` flag that is the `?` char after the brackets, `{2,4}?` means to match the minimum number possible tokens in this case 2.
|
||||
a long quantifier may have a `greedy off` flag that is the `?` char after the brackets,
|
||||
`{2,4}?` means to match the minimum number possible tokens in this case 2.
|
||||
|
||||
### dot char
|
||||
|
||||
the dot is a particular meta char that matches "any char", is more simple explain it with an example:
|
||||
the dot is a particular meta char that matches "any char",
|
||||
is more simple explain it with an example:
|
||||
|
||||
suppose to have `abccc ddeef` as source string to parse with regex, the following table show the query strings and the result of parsing source string.
|
||||
suppose to have `abccc ddeef` as source string to parse with regex,
|
||||
the following table show the query strings and the result of parsing source string.
|
||||
|
||||
| query string | result |
|
||||
| ------------ | ------ |
|
||||
|
@ -102,39 +116,50 @@ the dot char matches any char until the next token match is satisfied.
|
|||
|
||||
### OR token
|
||||
|
||||
the token `|` is a logic OR operation between two consecutive tokens, `a|b` matches a char that is `a` or `b`.
|
||||
the token `|` is a logic OR operation between two consecutive tokens,
|
||||
`a|b` matches a char that is `a` or `b`.
|
||||
|
||||
The OR token can work in a "chained way": `a|(b)|cd ` test first `a` if the char is not `a` then test the group `(b)` and if the group doesn't match test the token `c`.
|
||||
The OR token can work in a "chained way": `a|(b)|cd ` test first `a` if the char is not `a`
|
||||
then test the group `(b)` and if the group doesn't match test the token `c`.
|
||||
|
||||
**note: The OR work at token level! It doesn't work at concatenation level!**
|
||||
|
||||
A query string like `abc|bde` is not equal to `(abc)|(bde)`!! The OR work only on `c|b` not at char concatenation level.
|
||||
A query string like `abc|bde` is not equal to `(abc)|(bde)`!!
|
||||
The OR work only on `c|b` not at char concatenation level.
|
||||
|
||||
### Groups
|
||||
|
||||
Groups are a method to create complex patterns with repetition of blocks of tokens.
|
||||
|
||||
The groups are delimited by round brackets `( )`, groups can be nested and can have a quantifier as all the tokens.
|
||||
The groups are delimited by round brackets `( )`,
|
||||
groups can be nested and can have a quantifier as all the tokens.
|
||||
|
||||
`c(pa)+z` match `cpapaz` or `cpaz` or `cpapapaz` .
|
||||
|
||||
`(c(pa)+z ?)+` matches `cpaz cpapaz cpapapaz` or `cpapaz`
|
||||
`(c(pa)+z ?)+` matches `cpaz cpapaz cpapapaz` or `cpapaz`
|
||||
|
||||
let analyze this last case, first we have the group `#0` that are the most outer round brackets `(...)+`, this group has a quantifier that say to match its content at least one time `+`.
|
||||
let analyze this last case, first we have the group `#0`
|
||||
that are the most outer round brackets `(...)+`,
|
||||
this group has a quantifier that say to match its content at least one time `+`.
|
||||
|
||||
After we have a simple char token `c` and a second group that is the number `#1` :`(pa)+`, this group try to match the sequence `pa` at least one time as specified by the `+` quantifier.
|
||||
After we have a simple char token `c` and a second group that is the number `#1` :`(pa)+`,
|
||||
this group try to match the sequence `pa` at least one time as specified by the `+` quantifier.
|
||||
|
||||
After, we have another simple token `z` and another simple token ` ?` that is the space char (ascii code 32) followed by the `?` quantifier that say to capture the space char 0 or 1 time.
|
||||
After, we have another simple token `z` and another simple token ` ?`
|
||||
that is the space char (ascii code 32) followed by the `?` quantifier
|
||||
that say to capture the space char 0 or 1 time.
|
||||
|
||||
This explain because the `(c(pa)+z ?)+` query string can match `cpaz cpapaz cpapapaz` .
|
||||
|
||||
In this implementation the groups are "capture groups", it means that the last temporal result for each group can be retrieved from the `RE` struct.
|
||||
In this implementation the groups are "capture groups",
|
||||
it means that the last temporal result for each group can be retrieved from the `RE` struct.
|
||||
|
||||
The "capture groups" are store as couple of index in the field `groups` that is an `[]int` inside the `RE` struct.
|
||||
The "capture groups" are store as couple of index in the field `groups`
|
||||
that is an `[]int` inside the `RE` struct.
|
||||
|
||||
**example:**
|
||||
|
||||
```v
|
||||
```v oksyntax
|
||||
text := "cpaz cpapaz cpapapaz"
|
||||
query:= r"(c(pa)+z ?)+"
|
||||
mut re := regex.regex_opt(query) or { panic(err) }
|
||||
|
@ -157,16 +182,19 @@ for gi < re.groups.len {
|
|||
// 1 :[pa]
|
||||
```
|
||||
|
||||
**note:** *to show the `group id number` in the result of the `get_query()` the flag `debug` of the RE object must be `1` or `2`*
|
||||
**note:** *to show the `group id number` in the result of the `get_query()`*
|
||||
*the flag `debug` of the RE object must be `1` or `2`*
|
||||
|
||||
### Groups Continuous saving
|
||||
|
||||
In particular situations it is useful have a continuous save of the groups, this is possible initializing the saving array field in `RE` struct: `group_csave`.
|
||||
In particular situations it is useful have a continuous save of the groups,
|
||||
this is possible initializing the saving array field in `RE` struct: `group_csave`.
|
||||
|
||||
This feature allow to collect data in a continuous way.
|
||||
|
||||
In the example we pass a text followed by a integer list that we want collect.
|
||||
To achieve this task we can use the continuous saving of the group that save each captured group in a array that we set with: `re.group_csave = [-1].repeat(3*20+1)`.
|
||||
In the example we pass a text followed by a integer list that we want collect.
|
||||
To achieve this task we can use the continuous saving of the group
|
||||
that save each captured group in a array that we set with: `re.group_csave = [-1].repeat(3*20+1)`.
|
||||
|
||||
The array will be filled with the following logic:
|
||||
|
||||
|
@ -176,9 +204,10 @@ The array will be filled with the following logic:
|
|||
`re.group_csave[1+n*3]` start index in the source string of the saved group
|
||||
`re.group_csave[1+n*3]` end index in the source string of the saved group
|
||||
|
||||
The regex save until finish or found that the array have no space. If the space ends no error is raised, further records will not be saved.
|
||||
The regex save until finish or found that the array have no space.
|
||||
If the space ends no error is raised, further records will not be saved.
|
||||
|
||||
```v
|
||||
```v oksyntax
|
||||
fn example2() {
|
||||
test_regex()
|
||||
|
||||
|
@ -234,7 +263,7 @@ cg id: 0 [4, 8] => [ 01,]
|
|||
cg id: 0 [8, 11] => [23,]
|
||||
cg id: 0 [11, 15] => [45 ,]
|
||||
cg id: 0 [15, 19] => [56, ]
|
||||
cg id: 0 [19, 21] => [78]
|
||||
cg id: 0 [19, 21] => [78]
|
||||
```
|
||||
|
||||
### Named capturing groups
|
||||
|
@ -245,13 +274,14 @@ This regex module support partially the question mark `?` PCRE syntax for groups
|
|||
|
||||
`(?P<mygroup>abcdef)` **named group:** the group content is saved and labeled as `mygroup`
|
||||
|
||||
The label of the groups is saved in the `group_map` of the `RE` struct, this is a map from `string` to `int` where the value is the index in `group_csave` list of index.
|
||||
The label of the groups is saved in the `group_map` of the `RE` struct,
|
||||
this is a map from `string` to `int` where the value is the index in `group_csave` list of index.
|
||||
|
||||
Have a look at the example for the use of them.
|
||||
|
||||
example:
|
||||
|
||||
```v
|
||||
```v oksyntax
|
||||
import regex
|
||||
fn main() {
|
||||
test_regex()
|
||||
|
@ -270,8 +300,8 @@ fn main() {
|
|||
q_str := re.get_query()
|
||||
println("O.Query: $query")
|
||||
println("Query : $q_str")
|
||||
|
||||
re.debug = 0
|
||||
|
||||
re.debug = 0
|
||||
start, end := re.match_string(text)
|
||||
if start < 0 {
|
||||
err_str := re.get_parse_error_string(start)
|
||||
|
@ -331,7 +361,7 @@ cg id: 1 [22, 28] => [hello/]
|
|||
cg id: 1 [28, 37] => [pippo12_/]
|
||||
cg id: 1 [37, 42] => [pera.]
|
||||
cg id: 1 [42, 46] => [html]
|
||||
raw array: [8, 0, 0, 4, 1, 7, 11, 1, 11, 16, 1, 16, 22, 1, 22, 28, 1, 28, 37, 1, 37, 42, 1, 42, 46]
|
||||
raw array: [8, 0, 0, 4, 1, 7, 11, 1, 11, 16, 1, 16, 22, 1, 22, 28, 1, 28, 37, 1, 37, 42, 1, 42, 46]
|
||||
named capturing groups:
|
||||
'format':[0, 4] => 'http'
|
||||
'token':[42, 46] => 'html'
|
||||
|
@ -341,25 +371,27 @@ named capturing groups:
|
|||
|
||||
It is possible to set some flags in the regex parser that change the behavior of the parser itself.
|
||||
|
||||
```v
|
||||
```v oksyntax
|
||||
// example of flag settings
|
||||
mut re := regex.new()
|
||||
re.flag = regex.F_BIN
|
||||
|
||||
re.flag = regex.F_BIN
|
||||
```
|
||||
|
||||
- `F_BIN`: parse a string as bytes, utf-8 management disabled.
|
||||
|
||||
- `F_EFM`: exit on the first char matches in the query, used by the find function.
|
||||
- `F_MS`: matches only if the index of the start match is 0, same as `^` at the start of the query string.
|
||||
- `F_ME`: matches only if the end index of the match is the last char of the input string, same as `$` end of query string.
|
||||
- `F_MS`: matches only if the index of the start match is 0,
|
||||
same as `^` at the start of the query string.
|
||||
- `F_ME`: matches only if the end index of the match is the last char of the input string,
|
||||
same as `$` end of query string.
|
||||
- `F_NL`: stop the matching if found a new line char `\n` or `\r`
|
||||
|
||||
## Functions
|
||||
|
||||
### Initializer
|
||||
|
||||
These functions are helper that create the `RE` struct, a `RE` struct can be created manually if you needed.
|
||||
These functions are helper that create the `RE` struct,
|
||||
a `RE` struct can be created manually if you needed.
|
||||
|
||||
#### **Simplified initializer**
|
||||
|
||||
|
@ -378,7 +410,7 @@ pub fn new() RE
|
|||
pub fn new_by_size(mult int) RE
|
||||
```
|
||||
After a base initializer is used, the regex expression must be compiled with:
|
||||
```v
|
||||
```v oksyntax
|
||||
// compile compiles the REgex returning an error if the compilation fails
|
||||
pub fn (re mut RE) compile_opt(in_txt string) ?
|
||||
```
|
||||
|
@ -387,7 +419,7 @@ pub fn (re mut RE) compile_opt(in_txt string) ?
|
|||
|
||||
These are the operative functions
|
||||
|
||||
```v
|
||||
```v oksyntax
|
||||
// match_string try to match the input string, return start and end index if found else start is -1
|
||||
pub fn (re mut RE) match_string(in_txt string) (int,int)
|
||||
|
||||
|
@ -409,7 +441,7 @@ This module has few small utilities to help the writing of regex expressions.
|
|||
|
||||
the following example code show how to visualize the syntax errors in the compilation phase:
|
||||
|
||||
```v
|
||||
```v oksyntax
|
||||
query:= r"ciao da ab[ab-]" // there is an error, a range not closed!!
|
||||
mut re := new()
|
||||
|
||||
|
@ -425,7 +457,8 @@ re.compile_opt(query) or { println(err) }
|
|||
|
||||
### **Compiled code**
|
||||
|
||||
It is possible view the compiled code calling the function `get_query()` the result will be something like this:
|
||||
It is possible to view the compiled code calling the function `get_query()`.
|
||||
The result will be something like this:
|
||||
|
||||
```
|
||||
========================================
|
||||
|
@ -495,21 +528,24 @@ the columns have the following meaning:
|
|||
|
||||
`PC: 1` program counter of the step
|
||||
|
||||
`=>7fffffff ` hex code of the instruction
|
||||
`=>7fffffff ` hex code of the instruction
|
||||
|
||||
`i,ch,len:[ 0,'a',1]` `i` index in the source string, `ch` the char parsed, `len` the length in byte of the char parsed
|
||||
`i,ch,len:[ 0,'a',1]` `i` index in the source string, `ch` the char parsed,
|
||||
`len` the length in byte of the char parsed
|
||||
|
||||
`f.m:[ 0, 1]` `f` index of the first match in the source string, `m` index that is actual matching
|
||||
|
||||
`query_ch: [b]` token in use and its char
|
||||
|
||||
`{2,3}:1?` quantifier `{min,max}`, `:1` is the actual counter of repetition, `?` is the greedy off flag if present
|
||||
`{2,3}:1?` quantifier `{min,max}`, `:1` is the actual counter of repetition,
|
||||
`?` is the greedy off flag if present.
|
||||
|
||||
### **Custom Logger output**
|
||||
|
||||
The debug functions output uses the `stdout` as default, it is possible to provide an alternative output setting a custom output function:
|
||||
The debug functions output uses the `stdout` as default,
|
||||
it is possible to provide an alternative output setting a custom output function:
|
||||
|
||||
```v
|
||||
```v oksyntax
|
||||
// custom print function, the input will be the regex debug string
|
||||
fn custom_print(txt string) {
|
||||
println("my log: $txt")
|
||||
|
@ -524,7 +560,7 @@ re.log_func = custom_print // every debug output from now will call this functi
|
|||
|
||||
Here there is a simple code to perform some basically match of strings
|
||||
|
||||
```v
|
||||
```v oksyntax
|
||||
struct TestObj {
|
||||
source string // source string to parse
|
||||
query string // regex query string
|
||||
|
@ -545,18 +581,18 @@ fn example() {
|
|||
for c,tst in tests {
|
||||
mut re := regex.new()
|
||||
re.compile_opt(tst.query) or { println(err) continue }
|
||||
|
||||
|
||||
// print the query parsed with the groups ids
|
||||
re.debug = 1 // set debug on at minimum level
|
||||
println("#${c:2d} query parsed: ${re.get_query()}")
|
||||
re.debug = 0
|
||||
|
||||
|
||||
// do the match
|
||||
start, end := re.match_string(tst.source)
|
||||
if start >= 0 && end > start {
|
||||
println("#${c:2d} found in: [$start, $end] => [${tst.source[start..end]}]")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// print the groups
|
||||
mut gi := 0
|
||||
for gi < re.groups.len {
|
||||
|
@ -564,7 +600,7 @@ fn example() {
|
|||
println("group ${gi/2:2d} :[${tst.source[re.groups[gi]..re.groups[gi+1]]}]")
|
||||
}
|
||||
gi += 2
|
||||
}
|
||||
}
|
||||
println("")
|
||||
}
|
||||
}
|
||||
|
@ -575,4 +611,3 @@ fn main() {
|
|||
```
|
||||
|
||||
more example code is available in the test code for the `regex` module `vlib\regex\regex_test.v`.
|
||||
|
||||
|
|
|
@ -6,7 +6,10 @@ These are v implementations of the C language `printf` and `sprintf` functions.
|
|||
|
||||
### v_sprintf
|
||||
|
||||
`v_sprintf` has a variable number of parameters. The first is a format string to control the appearance of the final string. Each format specifier (%s, %d, etc.) in the format string is replaced by the textual version of the following parameters.
|
||||
`v_sprintf` has a variable number of parameters.
|
||||
The first is a format string to control the appearance of the final string.
|
||||
Each format specifier (%s, %d, etc.) in the format string
|
||||
is replaced by the textual version of the following parameters.
|
||||
|
||||
```v
|
||||
import strconv
|
||||
|
@ -23,7 +26,8 @@ Hello World!
|
|||
|
||||
### v_printf
|
||||
|
||||
`v_printf` creates the same modified string as `v_sprintf`, using the same format specifiers, but it will immediately print the modified string to stdout instead of returning a string.
|
||||
`v_printf` creates the same modified string as `v_sprintf`, using the same format specifiers,
|
||||
but it will immediately print the modified string to stdout instead of returning a string.
|
||||
|
||||
### Syntax
|
||||
|
||||
|
@ -45,9 +49,14 @@ The Flags field may be zero or more (in any order) of:
|
|||
|
||||
#### Width field
|
||||
|
||||
The Width field specifies a *maximum* number of characters to output, and is typically used to pad fixed-width fields in tabulated output, it causes truncation of oversized fields.
|
||||
The Width field specifies a *maximum* number of characters to output,
|
||||
and is typically used to pad fixed-width fields in tabulated output,
|
||||
it causes truncation of oversized fields.
|
||||
|
||||
The width field may be omitted, or it may be a numeric integer value, or may also be specified by a parameter when indicated by an asterisk `*`. For example, `v_printf("%*.s", 5, my_string)` will result in ` mystring` being printed, with a total width of 5 characters.
|
||||
The width field may be omitted, or it may be a numeric integer value,
|
||||
or may also be specified by a parameter when indicated by an asterisk `*`.
|
||||
For example, `v_printf("%*.s", 5, my_string)` will result in ` mystring` being printed,
|
||||
with a total width of 5 characters.
|
||||
|
||||
#### Length field
|
||||
|
||||
|
@ -83,7 +92,7 @@ The Type field can be any of:
|
|||
|
||||
various types
|
||||
|
||||
```v
|
||||
```v oksyntax
|
||||
a0 := u32(10)
|
||||
b0 := 200
|
||||
c0 := byte(12)
|
||||
|
@ -102,7 +111,7 @@ ciao: [10 ] 200 12 [ ciAo] [000000C8] [0.3123 ] [200000.000
|
|||
|
||||
integer
|
||||
|
||||
```v
|
||||
```v oksyntax
|
||||
a := byte(12)
|
||||
b := i16(13)
|
||||
c := 14
|
||||
|
@ -118,7 +127,7 @@ println(temp_s)
|
|||
|
||||
unsigned integer
|
||||
|
||||
```v
|
||||
```v oksyntax
|
||||
a1 := byte(0xff)
|
||||
b1 := u16(0xffff)
|
||||
c1 := u32(0xffff_ffff)
|
||||
|
@ -134,7 +143,7 @@ println(temp_s)
|
|||
|
||||
hexadecimal
|
||||
|
||||
```v
|
||||
```v oksyntax
|
||||
a1 := byte(0xff)
|
||||
b1 := i16(0xffff)
|
||||
c1 := u32(0xffff_ffff)
|
||||
|
@ -150,7 +159,7 @@ ff ffff ffffffff ffffffffffffffff
|
|||
|
||||
hexadecimal
|
||||
|
||||
```v
|
||||
```v oksyntax
|
||||
a2 := 125
|
||||
sc7 := "[%9x] [%9X] [%-9x] [%-9X] [%09x] [%09X]"
|
||||
temp_s = strconv.v_sprintf(sc7, a2, a2, a2, a2, a2, a2)
|
||||
|
@ -163,7 +172,7 @@ println(temp_s)
|
|||
|
||||
floating points
|
||||
|
||||
```v
|
||||
```v oksyntax
|
||||
f0 := 0.312345
|
||||
f1 := 200000.0
|
||||
f2 := -1234.300e6
|
||||
|
@ -179,7 +188,7 @@ println(temp_s)
|
|||
|
||||
float automatic notations
|
||||
|
||||
```v
|
||||
```v oksyntax
|
||||
mut ft := -1e-7
|
||||
mut x := 0
|
||||
sc8 := "[%20g][%20G]|"
|
||||
|
@ -211,7 +220,7 @@ for x < 12 {
|
|||
|
||||
The format module also has some utility functions:
|
||||
|
||||
```v
|
||||
```v oksyntax
|
||||
// calling struct
|
||||
struct BF_param {
|
||||
pad_ch byte = ` ` // padding char
|
||||
|
@ -234,6 +243,7 @@ fn remove_tail_zeros(s string) string
|
|||
|
||||
`format_fl` format a float number in normal notation using the parameters in the `BF_param` struct.
|
||||
|
||||
`format_es format a float number in scientific notation using the parameters in the `BF_param` struct.
|
||||
`format_es format a float number in scientific notation using the parameters in the BF_param`
|
||||
struct.
|
||||
|
||||
`remove_tail_zeros` removes the tailing zeros from a floating point number as string.
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
# Quickstart
|
||||
|
||||
The V `term` module is a module designed to provide the building blocks for building very simple TUI apps.
|
||||
For more complex apps, you should really look at the `term.input` module, as it includes terminal events, is easier to use,
|
||||
and is much more performant for large draws.
|
||||
The V `term` module is a module designed to provide the building blocks
|
||||
for building very simple TUI apps.
|
||||
For more complex apps, you should really look at the `term.input` module,
|
||||
as it includes terminal events, is easier to use and is much more performant for large draws.
|
||||
|
||||
# Use
|
||||
|
||||
You can use the `term` module to either color the output on a terminal or to decide on where to put the output in your terminal.
|
||||
You can use the `term` module to either color the output on a terminal
|
||||
or to decide on where to put the output in your terminal.
|
||||
|
||||
For example let's make a simple program which prints colored text in the middle of the terminal.
|
||||
|
||||
|
@ -16,8 +18,8 @@ import os
|
|||
|
||||
fn main() {
|
||||
term.clear() // clears the content in the terminal
|
||||
width, height := term.get_terminal_size() // get the size of the terminal
|
||||
term.set_cursor_position(x: width / 2, y: height / 2) // now we point the cursor to the middle of the terminal
|
||||
width, height := term.get_terminal_size() // get the size of the terminal
|
||||
term.set_cursor_position(x: width / 2, y: height / 2) // now we point the cursor to the middle of the terminal
|
||||
println(term.strikethrough(term.bright_green("hello world"))) // Print green text
|
||||
term.set_cursor_position(x: 0, y: height) // Sets the position of the cursor to the bottom of the terminal
|
||||
mut var := os.input('press q to quit: ')
|
||||
|
@ -38,17 +40,17 @@ This simple program covers many of the principal aspects of the `term ` module.
|
|||
|
||||
Here are some functions you should be aware of in the `term `module:
|
||||
|
||||
```v
|
||||
```v oksyntax
|
||||
// returns the height and the width of the terminal
|
||||
term.get_terminal_size() (width, height)
|
||||
|
||||
// returns the string as green text to be printed on stdout
|
||||
// returns the string as green text to be printed on stdout
|
||||
term.ok_message(string)
|
||||
|
||||
// returns the string as red text to be printed on stdout
|
||||
// returns the string as red text to be printed on stdout
|
||||
term.fail_message(string)
|
||||
|
||||
// returns the string as yellow text to be printed on stdout
|
||||
// returns the string as yellow text to be printed on stdout
|
||||
term.warning_message(string)
|
||||
|
||||
//clears the entire terminal and leaves a blank one
|
||||
|
@ -66,7 +68,8 @@ term.strikethrough(string)
|
|||
// underlines the given string
|
||||
term.underline(string)
|
||||
|
||||
// colors the background of the outup following the given color, the available colors are: black,blue,yellow,green,cyan,gray
|
||||
// colors the background of the output following the given color
|
||||
// the available colors are: black, blue, yellow, green, cyan, gray
|
||||
term.bg_<color>(string)
|
||||
|
||||
// sets the position of the cursor at a given place in the terminal
|
||||
|
|
|
@ -44,32 +44,58 @@ See the `/examples/term.ui/` folder for more usage examples.
|
|||
|
||||
#### Configuration
|
||||
|
||||
- `user_data voidptr` - a pointer to any `user_data`, it will be passed as the last argument to each callback. Used for accessing your app context from the different callbacks.
|
||||
- `init_fn fn(voidptr)` - a callback that will be called after initialization, and before the first event / frame. Useful for initializing any user data.
|
||||
- `frame_fn fn(voidptr)` - a callback that will be fired on each frame, at a rate of `frame_rate` frames per second.
|
||||
`event_fn fn(&Event, voidptr)` - a callback that will be fired for every event received
|
||||
- `cleanup_fn fn(voidptr)` - a callback that will be fired once, before the application exits.
|
||||
`fail_fn fn(string)` - a callback that will be fired if a fatal error occurs during app initialization.
|
||||
- `buffer_size int = 256` - the internal size of the read buffer. Increasing it may help in case you're missing events, but you probably shouldn't lower this value unless you make sure you're still receiving all events. In general, higher frame rates work better with lower buffer sizes, and vice versa.
|
||||
- `frame_rate int = 30` - the number of times per second that the `frame` callback will be fired. 30fps is a nice balance between smoothness and performance, but you can increase or lower it as you wish.
|
||||
- `hide_cursor bool` - whether to hide the mouse cursor. Useful if you want to use your own.
|
||||
- `capture_events bool` - sets the terminal into raw mode, which makes it intercept some escape codes such as `ctrl + c` and `ctrl + z`. Useful if you want to use those key combinations in your app.
|
||||
- `window_title string` - sets the title of the terminal window. This may be changed later, by calling the `set_window_title()` method.
|
||||
- `reset []int = [1, 2, 3, 4, 6, 7, 8, 9, 11, 13, 14, 15, 19]` - a list of reset signals, to setup handlers to cleanup the terminal state when they're received. You should not need to change this, unless you know what you're doing.
|
||||
- `user_data voidptr` - a pointer to any `user_data`, it will be passed as the last argument to
|
||||
each callback. Used for accessing your app context from the different callbacks.
|
||||
- `init_fn fn(voidptr)` - a callback that will be called after initialization
|
||||
and before the first event / frame. Useful for initializing any user data.
|
||||
- `frame_fn fn(voidptr)` - a callback that will be fired on each frame,
|
||||
at a rate of `frame_rate` frames per second.
|
||||
`event_fn fn(&Event, voidptr)` - a callback that will be fired for every event received.
|
||||
- `cleanup_fn fn(voidptr)` - a callback that will be fired once, before the application exits.
|
||||
- `fail_fn fn(string)` - a callback that will be fired
|
||||
if a fatal error occurs during app initialization.
|
||||
- `buffer_size int = 256` - the internal size of the read buffer.
|
||||
Increasing it may help in case you're missing events, but you probably shouldn't lower
|
||||
this value unless you make sure you're still receiving all events. In general,
|
||||
higher frame rates work better with lower buffer sizes, and vice versa.
|
||||
- `frame_rate int = 30` - the number of times per second that the `frame` callback will be fired.
|
||||
30fps is a nice balance between smoothness and performance,
|
||||
but you can increase or lower it as you wish.
|
||||
- `hide_cursor bool` - whether to hide the mouse cursor. Useful if you want to use your own.
|
||||
- `capture_events bool` - sets the terminal into raw mode, which makes it intercept some
|
||||
escape codes such as `ctrl + c` and `ctrl + z`.
|
||||
Useful if you want to use those key combinations in your app.
|
||||
- `window_title string` - sets the title of the terminal window.
|
||||
This may be changed later, by calling the `set_window_title()` method.
|
||||
- `reset []int = [1, 2, 3, 4, 6, 7, 8, 9, 11, 13, 14, 15, 19]` - a list of reset signals,
|
||||
to setup handlers to cleanup the terminal state when they're received.
|
||||
You should not need to change this, unless you know what you're doing.
|
||||
|
||||
All of these fields may be omitted, in which case, the default value will be used. In the case of the various callbacks, they will not be fired if a handler has not been specified.
|
||||
All of these fields may be omitted, in which case, the default value will be used.
|
||||
In the case of the various callbacks, they will not be fired if a handler has not been specified.
|
||||
|
||||
|
||||
#### FAQ
|
||||
|
||||
Q: Why does this module not work on Windows?
|
||||
A: As with many other things, Windows has a completely different and incompatible way of handling input parsing and drawing primitives, and support has not been implemented yet. Contributions are definitely welcome though.
|
||||
Q: Why does this module not work on Windows?
|
||||
A: As with many other things, Windows has a completely different and incompatible way of handling
|
||||
input parsing and drawing primitives, and support has not been implemented yet.
|
||||
Contributions are definitely welcome though.
|
||||
|
||||
Q: My terminal (doesn't receive events / doesn't print anything / prints gibberish characters), what's up with that?
|
||||
A: Please check if your terminal. The module has been tested with `xterm`-based terminals on Linux (like `gnome-terminal` and `konsole`), and `Terminal.app` and `iterm2` on macOS. If your terminal does not work, open an issue with the output of `echo $TERM`.
|
||||
Q: My terminal (doesn't receive events / doesn't print anything / prints gibberish characters),
|
||||
what's up with that?
|
||||
A: Please check if your terminal. The module has been tested with `xterm`-based terminals on Linux
|
||||
(like `gnome-terminal` and `konsole`), and `Terminal.app` and `iterm2` on macOS.
|
||||
If your terminal does not work, open an issue with the output of `echo $TERM`.
|
||||
|
||||
Q: There are screen tearing issues when doing large prints
|
||||
A: This is an issue with how terminals render frames, as they may decide to do so in the middle of receiving a frame, and cannot be fully fixed unless your console implements the [synchronized updates spec](https://gitlab.com/gnachman/iterm2/-/wikis/synchronized-updates-spec). It can be reduced *drastically*, though, by using the rendering methods built in to the module, and by only painting frames when your app's content has actually changed.
|
||||
Q: There are screen tearing issues when doing large prints
|
||||
A: This is an issue with how terminals render frames,
|
||||
as they may decide to do so in the middle of receiving a frame,
|
||||
and cannot be fully fixed unless your console implements the [synchronized updates spec](https://gitlab.com/gnachman/iterm2/-/wikis/synchronized-updates-spec).
|
||||
It can be reduced *drastically*, though, by using the rendering methods built in to the module,
|
||||
and by only painting frames when your app's content has actually changed.
|
||||
|
||||
Q: Why does the module only emit `keydown` events, and not `keyup` like `sokol`/`gg`?
|
||||
A: It's because of the way terminals emit events. Every key event is received as a keypress, and there isn't a way of telling terminals to send keyboard events differently, nor a reliable way of converting these into `keydown` / `keyup` events.
|
||||
Q: Why does the module only emit `keydown` events, and not `keyup` like `sokol`/`gg`?
|
||||
A: It's because of the way terminals emit events. Every key event is received as a keypress,
|
||||
and there isn't a way of telling terminals to send keyboard events differently,
|
||||
nor a reliable way of converting these into `keydown` / `keyup` events.
|
||||
|
|
|
@ -18,7 +18,7 @@ There's also the V forum: https://github.com/vlang/vorum
|
|||
|
||||
`vorum.v` contains all GET and POST actions.
|
||||
|
||||
```Go
|
||||
```v ignore
|
||||
pub fn (app mut App) index() {
|
||||
posts := app.find_all_posts()
|
||||
$vweb.html()
|
||||
|
@ -52,7 +52,8 @@ pub fn (app App) post() {
|
|||
@end
|
||||
```
|
||||
|
||||
`$vweb.html()` compiles an HTML template into V during compilation, and embeds the resulting code in current action.
|
||||
`$vweb.html()` compiles an HTML template into V during compilation,
|
||||
and embeds the resulting code in current action.
|
||||
|
||||
That means that the template automatically has access to that action's entire environment.
|
||||
|
||||
|
@ -60,4 +61,3 @@ That means that the template automatically has access to that action's entire en
|
|||
### Deploying vweb apps
|
||||
|
||||
Everything, including HTML templates, is in one binary file. That's all you need to deploy.
|
||||
|
||||
|
|
|
@ -2,9 +2,11 @@ This package is to generate data-driven HTML output.
|
|||
|
||||
# Directives
|
||||
Each directive begins with an `@` sign.
|
||||
Some directives begin contains a `{}` block, others only have `''` (string) parameters. More on the directives itself.
|
||||
Some directives begin contains a `{}` block, others only have `''` (string) parameters.
|
||||
More on the directives itself.
|
||||
|
||||
Newlines on the beginning and end are ignored in `{}` blocks, otherwise this (see [if](##if) for this syntax):
|
||||
Newlines on the beginning and end are ignored in `{}` blocks,
|
||||
otherwise this (see [if](##if) for this syntax):
|
||||
```html
|
||||
@if bool_val {
|
||||
<span>This is shown if bool_val is true</span>
|
||||
|
@ -19,7 +21,8 @@ would result in:
|
|||
which could result in unreadable output.
|
||||
|
||||
## if
|
||||
The if directive consists of three parts, the `@if` tag, the condition (same syntax like in V) and the `{}` block where you can write html which will be rendered if the condition is true:
|
||||
The if directive consists of three parts, the `@if` tag, the condition (same syntax like in V)
|
||||
and the `{}` block where you can write html which will be rendered if the condition is true:
|
||||
```
|
||||
@if <condition> {}
|
||||
```
|
||||
|
@ -45,7 +48,8 @@ while the one-liner results in:
|
|||
```
|
||||
|
||||
## for
|
||||
The for directive consists of three parts, the `@for` tag, the condition (same syntax like in V) and the `{}` block where you can write html which will be rendered for each loop:
|
||||
The for directive consists of three parts, the `@for` tag, the condition (same syntax like in V)
|
||||
and the `{}` block where you can write html which will be rendered for each loop:
|
||||
```
|
||||
@for <condition> {}
|
||||
```
|
||||
|
@ -84,7 +88,8 @@ You can also write (and all other for condition syntaxes that are allowed in V):
|
|||
```
|
||||
|
||||
## include
|
||||
The include directive is for including other html files (which will be processed as well) and consists of two parts, the `@include` tag and a following `'<path>'` string.
|
||||
The include directive is for including other html files (which will be processed as well)
|
||||
and consists of two parts, the `@include` tag and a following `'<path>'` string.
|
||||
The path parameter is relative to the `/templates` directory in the corresponding project.
|
||||
|
||||
### Example
|
||||
|
@ -101,7 +106,9 @@ Project root
|
|||
```html
|
||||
<div>@include 'header/base'</div>
|
||||
```
|
||||
> Note that there shouldn't be a file suffix, it is automatically appended and only allows `html` files.
|
||||
> Note that there shouldn't be a file suffix,
|
||||
it is automatically appended and only allows `html` files.
|
||||
|
||||
# Variables
|
||||
All variables which are declared before can be used through the `@{my_var}` syntax. It's also possible to use properties of structs here like `@{my_struct.prop}`.
|
||||
All variables which are declared before can be used through the `@{my_var}` syntax.
|
||||
It's also possible to use properties of structs here like `@{my_struct.prop}`.
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
> `json2` was named just to avoid any unwanted potential conflicts with the existing codegen tailored for the main `json` module which is powered by CJSON.
|
||||
> `json2` was named just to avoid any unwanted potential conflicts with the existing codegen
|
||||
> tailored for the main `json` module which is powered by CJSON.
|
||||
|
||||
An experimental version of the JSON parser written from scratch on V.
|
||||
An experimental version of the JSON parser written from scratch on V.
|
||||
|
||||
## Usage
|
||||
```v
|
||||
```v oksyntax
|
||||
import x.json2
|
||||
import http
|
||||
import net.http
|
||||
|
||||
fn main() {
|
||||
// Decoding
|
||||
|
@ -18,7 +19,7 @@ fn main() {
|
|||
person := raw_person.as_map()
|
||||
name := person['name'].str() // Bob
|
||||
age := person['age'].int() // 19
|
||||
pi := person['pi'].f64() // 3.14....
|
||||
pi := person['pi'].f64() // 3.14....
|
||||
|
||||
// Constructing an `Any` type
|
||||
mut me := map[string]json2.Any
|
||||
|
@ -34,7 +35,7 @@ fn main() {
|
|||
me['interests'] = arr
|
||||
|
||||
mut pets := map[string]json2.Any
|
||||
pets['Sam'] = 'Maltese Shitzu'
|
||||
pets['Sam'] = 'Maltese Shitzu'
|
||||
me['pets'] = pets
|
||||
|
||||
// Stringify to JSON
|
||||
|
@ -46,11 +47,16 @@ fn main() {
|
|||
}
|
||||
```
|
||||
## Using `decode<T>` and `encode<T>`
|
||||
> Codegen for this feature is still WIP. You need to manually define the methods before using the module to structs.
|
||||
> Codegen for this feature is still WIP.
|
||||
> You need to manually define the methods before using the module to structs.
|
||||
|
||||
In order to use the `decode<T>` and `encode<T>` function, you need to explicitly define two methods: `from_json` and `to_json`. `from_json` accepts a `json2.Any` argument and inside of it you need to map the fields you're going to put into the type. As for `to_json` method, you just need to map the values into `json2.Any` and turn it into a string.
|
||||
In order to use the `decode<T>` and `encode<T>` function, you need to explicitly define
|
||||
two methods: `from_json` and `to_json`. `from_json` accepts a `json2.Any` argument
|
||||
and inside of it you need to map the fields you're going to put into the type.
|
||||
As for `to_json` method, you just need to map the values into `json2.Any`
|
||||
and turn it into a string.
|
||||
|
||||
```v
|
||||
```v ignore
|
||||
struct Person {
|
||||
mut:
|
||||
name string
|
||||
|
@ -88,12 +94,14 @@ fn main() {
|
|||
```
|
||||
|
||||
## Using struct tags
|
||||
`x.json2` cannot use struct tags just like when you use the `json` module. However, it emits an `Any` type when decoding so it can be flexible on the way you use it.
|
||||
`x.json2` cannot use struct tags just like when you use the `json` module.
|
||||
However, it emits an `Any` type when decoding so it can be flexible on the way you use it.
|
||||
|
||||
### Null Values
|
||||
`x.json2` have a `null` value for differentiating an undefined value and a null value. Use `is` for verifying the field you're using is a null.
|
||||
`x.json2` have a `null` value for differentiating an undefined value and a null value.
|
||||
Use `is` for verifying the field you're using is a null.
|
||||
|
||||
```v
|
||||
```v ignore
|
||||
fn (mut p Person) from_json(f json2.Any) {
|
||||
obj := f.as_map()
|
||||
if obj['age'] is json2.Null {
|
||||
|
@ -104,16 +112,18 @@ fn (mut p Person) from_json(f json2.Any) {
|
|||
```
|
||||
|
||||
### Custom field names
|
||||
In `json`, you can specify the field name you're mapping into the struct field by specifying a `json:` tag. In `x.json2`, just simply cast the base field into a map (`as_map()`) and get the value of the field you wish to put into the struct/type.
|
||||
In `json`, you can specify the field name you're mapping into the struct field by specifying
|
||||
a `json:` tag. In `x.json2`, just simply cast the base field into a map (`as_map()`)
|
||||
and get the value of the field you wish to put into the struct/type.
|
||||
|
||||
```v
|
||||
```v ignore
|
||||
fn (mut p Person) from_json(f json2.Any) {
|
||||
obj := f.as_map()
|
||||
p.name = obj['nickname'].str()
|
||||
}
|
||||
```
|
||||
|
||||
```v
|
||||
```v oksyntax
|
||||
fn (mut p Person) to_json() string {
|
||||
obj := f.as_map()
|
||||
obj['nickname'] = p.name
|
||||
|
@ -122,12 +132,16 @@ fn (mut p Person) to_json() string {
|
|||
```
|
||||
|
||||
### Undefined Values
|
||||
Getting undefined values has the same behavior as regular V types. If you're casting a base field into `map[string]json2.Any` and fetch an undefined entry/value, it simply returns empty. As for the `[]json2.Any`, it returns an index error.
|
||||
Getting undefined values has the same behavior as regular V types.
|
||||
If you're casting a base field into `map[string]json2.Any` and fetch an undefined entry/value,
|
||||
it simply returns empty. As for the `[]json2.Any`, it returns an index error.
|
||||
|
||||
## Casting a value to an incompatible type
|
||||
`x.json2` provides methods for turning `Any` types into usable types. The following list shows the possible outputs when casting a value to an incompatible type.
|
||||
`x.json2` provides methods for turning `Any` types into usable types.
|
||||
The following list shows the possible outputs when casting a value to an incompatible type.
|
||||
|
||||
1. Casting non-array values as array (`arr()`) will return an array with the value as the content.
|
||||
2. Casting non-map values as map (`as_map()`) will return a map with the value as the content.
|
||||
3. Casting non-string values to string (`str()`) will return the stringified representation of the value.
|
||||
4. Casting non-numeric values to int/float (`int()`/`f64()`) will return zero.
|
||||
3. Casting non-string values to string (`str()`)
|
||||
will return the stringified representation of the value.
|
||||
4. Casting non-numeric values to int/float (`int()`/`f64()`) will return zero.
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# Autobahn tests
|
||||
|
||||
This is the autobahn automatic tests on build. The performance tests are skipped due to timeouts in Github actions.
|
||||
This is the autobahn automatic tests on build.
|
||||
The performance tests are skipped due to timeouts in Github actions.
|
||||
|
|
Loading…
Reference in New Issue