├── .gitignore ├── JSON-Schema-Test-Suite └── tests │ └── draft4 │ ├── run.jq │ └── run.sh ├── LICENSE ├── Makefile ├── README.md ├── bin ├── jgen ├── jgen.jq ├── jval ├── jval.jq ├── jxml └── jxml.jq ├── doc ├── JQ-Distilled.md └── JQ-language-grammar.md ├── examples ├── bogussort.jq ├── closure.jq ├── cross.jq ├── cut.jq ├── dice.jq ├── mkascii.jq ├── mklatin1.jq ├── newton.jq ├── nqbrute.jq ├── nqsbrute.jq ├── nqsmart.jq ├── octcode.jq ├── prolog.jq ├── read.jq ├── script.jq ├── seconds.jq ├── sendmoremoney.jq ├── series.jq ├── shuffle.jq └── triple.jq ├── fadado.github.io ├── README.md ├── array │ ├── array.jq │ ├── choice.jq │ ├── kleene.jq │ ├── set.jq │ └── tuple.jq ├── json │ ├── json.jq │ └── schema.jq ├── math │ ├── bitwise.jq │ ├── chance.jq │ ├── math.jq │ └── sequence.jq ├── music │ ├── interval-class-vector.jq │ ├── interval-pattern.jq │ ├── interval-table.jq │ ├── pitch-class-set.jq │ ├── pitch-class.jq │ └── pitch.jq ├── object │ ├── object.jq │ └── set.jq ├── prelude.jq ├── stream.jq ├── string │ ├── ascii.jq │ ├── ascii.json │ ├── csv.jq │ ├── latin1.jq │ ├── latin1.json │ ├── regexp.jq │ ├── roman.jq │ ├── snobol.jq │ ├── string.jq │ ├── table.jq │ └── url.jq ├── types.jq └── word │ ├── alphabet.jq │ ├── factor.jq │ ├── language.jq │ ├── scanner.jq │ └── word.jq ├── schemata ├── README.md ├── address.schema.json ├── calendar.schema.json ├── card.schema.json ├── geo.schema.json ├── hyper-schema.schema.json ├── hyper-schema_expanded.schema.json └── schema.schema.json └── tests ├── array.test ├── array_choice.test ├── array_kleene.test ├── array_set.test ├── array_tuple.test ├── json.test ├── json_schema.test ├── math.test ├── math_bitwise.test ├── math_chance.test ├── math_sequence.test ├── music_interval-class-vector.test ├── music_interval-pattern.test ├── music_interval-table.test ├── music_pitch-class-set.test ├── music_pitch-class.test ├── music_pitch.test ├── object.test ├── object_set.test ├── prelude.test ├── stream.test ├── string.test ├── string_ascii.test ├── string_latin1.test ├── string_regexp.test ├── string_roman.test ├── string_snobol.test ├── string_table.test ├── string_url.test ├── types.test ├── word.test ├── word_alphabet.test ├── word_factor.test ├── word_language.test └── word_scanner.test /.gitignore: -------------------------------------------------------------------------------- 1 | .logs/ 2 | *.swp 3 | /JSON-Schema-Test-Suite/ 4 | /go 5 | -------------------------------------------------------------------------------- /JSON-Schema-Test-Suite/tests/draft4/run.jq: -------------------------------------------------------------------------------- 1 | # Called from the `jval` Bash script 2 | 3 | import "fadado.github.io/json/schema" as schema; 4 | 5 | def report($schema): 6 | if (.data | schema::valid($schema)) == .valid 7 | then empty 8 | else $TEST+": "+.description 9 | end 10 | ; 11 | 12 | .[] 13 | | .schema as $schema 14 | | .tests[] | report($schema) 15 | 16 | # vim:ai:sw=4:ts=4:et:syntax=jq 17 | -------------------------------------------------------------------------------- /JSON-Schema-Test-Suite/tests/draft4/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | declare -r JBOL='/usr/local/share/jbol' 4 | 5 | for t in *.json 6 | do 7 | #echo $t 1>&2 8 | jq -L $JBOL \ 9 | --arg TEST $t \ 10 | --from-file run.jq \ 11 | --raw-output \ 12 | $t 13 | echo 14 | done | grep . 15 | 16 | # vim:syntax=sh:ai:sw=4:ts=4:et 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Joan Josep Ordinas Rosa 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /bin/jgen: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # jgen -- Generates JSON schemas for instance documents 4 | # 5 | # Joan Ordinas 6 | 7 | # Script version 8 | declare -r VERSION='0.1' 9 | 10 | # This script name 11 | declare -r SELF="${0##*/}" 12 | 13 | # JQ script 14 | declare -r JQ="${0}.jq" 15 | 16 | # Data and modules directory: edit if necessary 17 | declare -r JBOL='/usr/local/share/jbol' 18 | 19 | ######################################################################## 20 | 21 | # Bash options 22 | set -o errexit -o noglob -o nounset -o pipefail 23 | shopt -s expand_aliases 24 | 25 | # Print text on screen 26 | alias print='echo 1>&2 -e' 27 | alias message='cat 1>&2' 28 | 29 | # Abort with message 30 | function fatal { 31 | print "Error: $*" 32 | exit 1 33 | } 34 | 35 | # Show help 36 | function help { 37 | message < 6 | 7 | # Script version 8 | declare -r VERSION='0.1' 9 | 10 | # This script name 11 | declare -r SELF="${0##*/}" 12 | 13 | # JQ script 14 | declare -r JQ="${0}.jq" 15 | 16 | # Data and modules directory: edit if necessary 17 | declare -r JBOL='/usr/local/share/jbol' 18 | 19 | # Meta schemas 20 | declare -r METASCHEMA="${JBOL}/schemata/schema.schema.json" 21 | declare -r HYPERSCHEMA="${JBOL}/schemata/hyper-schema_expanded.schema.json" 22 | 23 | ######################################################################## 24 | 25 | # Bash options 26 | set -o errexit -o noglob -o nounset -o pipefail 27 | shopt -s expand_aliases 28 | 29 | # Print text on screen 30 | alias print='echo 1>&2 -e' 31 | alias message='cat 1>&2' 32 | 33 | # Abort with message 34 | function fatal { 35 | print "Error: $*" 36 | exit 1 37 | } 38 | 39 | # Show help 40 | function help { 41 | message <&1 86 | } 87 | 88 | ######################################################################## 89 | 90 | function main { 91 | local -i quiet=0 metaschema=0 hyperschema=0 92 | 93 | local opt 94 | while getopts :hqsvy-: opt; do 95 | case $opt in 96 | h) help ;; 97 | q) quiet=1 ;; 98 | s) metaschema=1 ;; 99 | y) hyperschema=1 ;; 100 | v) version ;; 101 | -) case $OPTARG in 102 | help) help ;; 103 | quiet) quiet=1 ;; 104 | schema) metaschema=1 ;; 105 | hyper) hyperschema=1 ;; 106 | version) version ;; 107 | *) usage "--${OPTARG}";; 108 | esac 109 | ;; 110 | ?) usage "-${OPTARG}";; 111 | esac 112 | done 113 | 114 | shift $((OPTIND-1)) 115 | (( $# > 0 )) || help 116 | (( metaschema+hyperschema < 2 )) || help 117 | 118 | [[ -e $JBOL ]] || fatal "modules directory does not exists" 119 | [[ -e $METASCHEMA ]] || fatal "missing Schema meta-schema" 120 | [[ -e $HYPERSCHEMA ]] || fatal "missing Hyper-Schema meta-schema" 121 | 122 | local schema diagnostic 123 | if ((metaschema)); then 124 | schema=$METASCHEMA 125 | elif ((hyperschema)); then 126 | schema=$HYPERSCHEMA 127 | else 128 | schema=$1; shift 129 | fi 130 | 131 | diagnostic=$(validate $schema "$@") 132 | if [[ -z "$diagnostic" ]]; then 133 | return 0 134 | else 135 | (( quiet )) || echo "$diagnostic" 136 | return 1 137 | fi 138 | } 139 | 140 | # Call main and exit 141 | main "$@" 142 | 143 | exit 144 | 145 | # vim:syntax=sh:ai:sw=4:ts=4:et 146 | -------------------------------------------------------------------------------- /bin/jval.jq: -------------------------------------------------------------------------------- 1 | # Called from the `jval` Bash script 2 | 3 | include "fadado.github.io/types"; 4 | import "fadado.github.io/json/schema" as schema; 5 | 6 | # SCHEMA parameter is provided by the calling script 7 | $SCHEMA[0] as $schema | 8 | 9 | # Require '$schema' property in root schema 10 | if ($schema | isobject) and ($schema | has("$schema")) 11 | then 12 | try (schema::validate($schema) | "") # no output if ok 13 | catch "Validation error (\(input_filename):\(input_line_number)); instance: \(.instance); schema: \(.schema)" 14 | else 15 | "Error: expected '$schema' property in schema root object: \(.)" 16 | end 17 | 18 | # vim:ai:sw=4:ts=4:et:syntax=jq 19 | -------------------------------------------------------------------------------- /bin/jxml: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # jxml -- Transforms JSON to XML 4 | # 5 | # Joan Ordinas 6 | 7 | # Script version 8 | declare -r VERSION='0.1' 9 | 10 | # This script name 11 | declare -r SELF="${0##*/}" 12 | 13 | # JQ script 14 | declare -r JQ="${0}.jq" 15 | 16 | # Data and modules directory: edit if necessary 17 | declare -r JBOL='/usr/local/share/jbol' 18 | 19 | ######################################################################## 20 | 21 | # Bash options 22 | set -o errexit -o noglob -o nounset -o pipefail 23 | shopt -s expand_aliases 24 | 25 | # Print text on screen 26 | alias print='echo 1>&2 -e' 27 | alias message='cat 1>&2' 28 | 29 | # Abort with message 30 | function fatal { 31 | print "Error: $*" 32 | exit 1 33 | } 34 | 35 | # Show help 36 | function help { 37 | message <&1 106 | 107 | } 108 | 109 | # Call main and exit 110 | main "$@" 111 | 112 | exit 113 | 114 | # vim:syntax=sh:ai:sw=4:ts=4:et 115 | -------------------------------------------------------------------------------- /bin/jxml.jq: -------------------------------------------------------------------------------- 1 | # Called from the `jxml` Bash script 2 | 3 | import "fadado.github.io/json" as json; 4 | 5 | json::xmldoc($opt_root; $opt_element; $opt_tab; null) 6 | 7 | # vim:ai:sw=4:ts=4:et:syntax=jq 8 | -------------------------------------------------------------------------------- /doc/JQ-language-grammar.md: -------------------------------------------------------------------------------- 1 | **jq** simplified grammar extracted from the files `parser.y` and `lexer.l` in the **jq** sources. 2 | 3 | Only for language lawyers ;-) 4 | 5 | ```yacc 6 | TopLevel: 7 | Module Imports Exp | 8 | Module Imports FuncDefs 9 | 10 | Module: 11 | %empty | 12 | "module" Exp ';' 13 | 14 | Imports: 15 | %empty | 16 | Import Imports 17 | 18 | Import: 19 | ImportWhat ';' | 20 | ImportWhat Exp ';' 21 | 22 | ImportWhat: 23 | "import" ImportFrom "as" '$' IDENT | 24 | "import" ImportFrom "as" IDENT | 25 | "include" ImportFrom 26 | 27 | ImportFrom: 28 | String 29 | 30 | FuncDefs: 31 | %empty | 32 | FuncDef FuncDefs 33 | 34 | FuncDef: 35 | "def" IDENT ':' Exp ';' | 36 | "def" IDENT '(' Params ')' ':' Exp ';' 37 | 38 | Params: 39 | Param | 40 | Params ';' Param 41 | 42 | Param: 43 | '$' IDENT | 44 | IDENT 45 | 46 | Exp: 47 | FuncDef Exp | 48 | Term "as" Pattern '|' Exp | 49 | "reduce" Term "as" Pattern '(' Exp ';' Exp ')' | 50 | "foreach" Term "as" Pattern '(' Exp ';' Exp ';' Exp ')' | 51 | "foreach" Term "as" Pattern '(' Exp ';' Exp ')' | 52 | "if" Exp "then" Exp ElseBody | 53 | "try" Exp "catch" Exp | 54 | "try" Exp | 55 | "label" '$' IDENT '|' Exp | 56 | Exp '?' | 57 | Exp '=' Exp | 58 | Exp "or" Exp | 59 | Exp "and" Exp | 60 | Exp "//" Exp | 61 | Exp "//=" Exp | 62 | Exp "|=" Exp | 63 | Exp '|' Exp | 64 | Exp ',' Exp | 65 | Exp '+' Exp | 66 | Exp "+=" Exp | 67 | '-' Exp | 68 | Exp '-' Exp | 69 | Exp "-=" Exp | 70 | Exp '*' Exp | 71 | Exp "*=" Exp | 72 | Exp '/' Exp | 73 | Exp '%' Exp | 74 | Exp "/=" Exp | 75 | Exp "%=" Exp | 76 | Exp "==" Exp | 77 | Exp "!=" Exp | 78 | Exp '<' Exp | 79 | Exp '>' Exp | 80 | Exp "<=" Exp | 81 | Exp ">=" Exp | 82 | Term 83 | 84 | Pattern: 85 | '$' IDENT | 86 | '[' ArrayPats ']' | 87 | '{' ObjPats '}' 88 | 89 | ArrayPats: 90 | Pattern | 91 | ArrayPats ',' Pattern 92 | 93 | ObjPats: 94 | ObjPat | 95 | ObjPats ',' ObjPat 96 | 97 | ObjPat: 98 | '$' IDENT | 99 | IDENT ':' Pattern | 100 | Keyword ':' Pattern | 101 | String ':' Pattern | 102 | '(' Exp ')' ':' Pattern 103 | 104 | ElseBody: 105 | "elif" Exp "then" Exp ElseBody | 106 | "else" Exp "end" 107 | 108 | Term: 109 | '.' | 110 | ".." | 111 | "break" '$' IDENT | 112 | Term FIELD '?' | 113 | FIELD '?' | 114 | Term '.' String '?' | 115 | '.' String '?' | 116 | Term FIELD | 117 | FIELD | 118 | Term '.' String | 119 | '.' String | 120 | Term '[' Exp ']' '?' | 121 | Term '[' Exp ']' | 122 | Term '[' ']' '?' | 123 | Term '[' ']' | 124 | Term '[' Exp ':' Exp ']' '?' | 125 | Term '[' Exp ':' ']' '?' | 126 | Term '[' ':' Exp ']' '?' | 127 | Term '[' Exp ':' Exp ']' | 128 | Term '[' Exp ':' ']' | 129 | Term '[' ':' Exp ']' | 130 | LITERAL | 131 | String | 132 | FORMAT | 133 | '(' Exp ')' | 134 | '[' Exp ']' | 135 | '[' ']' | 136 | '{' MkDict '}' | 137 | '$' "__loc__" | 138 | '$' IDENT | 139 | IDENT | 140 | IDENT '(' Args ')' 141 | 142 | Args: 143 | Arg | 144 | Args ';' Arg 145 | 146 | Arg: 147 | Exp 148 | 149 | MkDict: 150 | %empty | 151 | MkDictPair | 152 | MkDictPair ',' MkDict 153 | 154 | MkDictPair: 155 | IDENT ':' ExpD | 156 | Keyword ':' ExpD | 157 | String ':' ExpD | 158 | String | 159 | '$' IDENT | 160 | IDENT | 161 | '(' Exp ')' ':' ExpD 162 | 163 | ExpD: 164 | ExpD '|' ExpD | 165 | '-' ExpD | 166 | Term 167 | 168 | Keyword: 169 | "module" | 170 | "import" | 171 | "include" | 172 | "def" | 173 | "as" | 174 | "if" | 175 | "then" | 176 | "else" | 177 | "elif" | 178 | "end" | 179 | "and" | 180 | "or" | 181 | "reduce" | 182 | "foreach" | 183 | "try" | 184 | "catch" | 185 | "label" | 186 | "break" | 187 | "__loc__" 188 | 189 | String: 190 | "\"" QQString "\"" | 191 | FORMAT "\"" QQString "\"" 192 | 193 | /* 194 | * IDENT: ([a-zA-Z_][a-zA-Z_0-9]*::)*[a-zA-Z_][a-zA-Z_0-9]* 195 | * FIELD: \.[a-zA-Z_][a-zA-Z_0-9]* 196 | * LITERAL: a JSON number 197 | * FORMAT: "@"[a-zA-Z0-9_]+ 198 | * QQString: a JSON string content with interpolations \(...) 199 | */ 200 | 201 | ``` 202 | -------------------------------------------------------------------------------- /examples/bogussort.jq: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/jq -cnRrf 2 | 3 | include "fadado.github.io/prelude"; 4 | import "fadado.github.io/array/tuple" as tuple; 5 | import "fadado.github.io/array/choice" as choice; 6 | 7 | def issorted: 8 | def _issorted($xs; $len): 9 | def r: 10 | . >= $len 11 | or $xs[.] >= $xs[.-1] 12 | and (.+1|r) 13 | ; 14 | 1|r 15 | ; 16 | _issorted(.; length) 17 | ; 18 | 19 | def bogussort: 20 | label $fence 21 | | choice::shuffle 22 | | tuple::permutations 23 | | select(issorted) 24 | | . , break $fence 25 | 26 | # | first( 27 | # tuple::permutations 28 | # | select(issorted) 29 | # ) 30 | ; 31 | 32 | def main: 33 | [range(8)] | bogussort 34 | ; 35 | 36 | main 37 | 38 | # vim:ai:sw=4:ts=4:et:syntax=jq 39 | -------------------------------------------------------------------------------- /examples/closure.jq: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/jq -cnRrf 2 | 3 | # 1 FIF- 4 | def recurse(f): def r: . , (f | r); r; 5 | 6 | # 2 FIFI 7 | def iterate(f): def r: .[] , (map(f) | select(length>0) | r); [.]|r; 8 | 9 | # 3 -I-I 10 | def reiterate(f): def r: . , (r | f); r; 11 | 12 | #1: Louis | William | Charles | Philip | Elizabeth II | George VI | Elizabeth | Diana | Chaterine 13 | #2: Louis | William | Chaterine | Charles | Diana | Philip | Elizabeth II | George VI | Elizabeth 14 | #3: Louis | William | Chaterine | Charles | Diana | Philip | Elizabeth II | George VI | Elizabeth... 15 | 16 | def family_tree: 17 | { 18 | "Queen Elizabeth II": ["King George VI", "Queen Elizabeth"], 19 | "Princess Margaret": ["King George VI", "Queen Elizabeth"], 20 | "Charles, Prince of Wales": ["Prince Philip", "Queen Elizabeth II"], 21 | "Anne, Princess Royal": ["Prince Philip", "Queen Elizabeth II"], 22 | "Prince Andrew": ["Prince Philip", "Queen Elizabeth II"], 23 | "Prince Edward": ["Prince Philip", "Queen Elizabeth II"], 24 | "Prince William": ["Charles, Prince of Wales", "Diana"], 25 | "Prince Harry": ["Charles, Prince of Wales", "Diana"], 26 | "Prince George": ["Prince William", "Chaterine"], 27 | "Princess Charlotte": ["Prince William", "Chaterine"], 28 | "Prince Louis": ["Prince William", "Chaterine"] 29 | }; 30 | 31 | def parents: 32 | . as $member 33 | | family_tree as $F 34 | | $F[$member][]? 35 | ; 36 | 37 | def ancestors1($member): $member | recurse(parents); 38 | def ancestors2($member): $member | iterate(parents); 39 | def ancestors3($member): $member | reiterate(parents); 40 | 41 | # 42 | ancestors1("Prince Louis"), 43 | "="*72, 44 | ancestors2("Prince Louis"), 45 | "="*72, 46 | ancestors3("Prince Louis") 47 | 48 | # vim:ai:sw=4:ts=4:et:syntax=jq 49 | -------------------------------------------------------------------------------- /examples/cross.jq: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/jq -nRrf 2 | 3 | # Output all intersections between two words 4 | 5 | import "fadado.github.io/word/scanner" as scanner; 6 | 7 | def chars: 8 | (./"")[] 9 | ; 10 | 11 | def lpad($n): 12 | " "*$n + . 13 | ; 14 | 15 | # Produces a stream of intersections between two words 16 | def cross($word1; $word2): 17 | (0|scanner::upto($word1;$word2)) as $i | 18 | (0|scanner::upto($word2;$word1[$i:$i+1])) as $j | 19 | [ 20 | ($word2[0:$j] | chars | lpad($i)), 21 | $word1, 22 | ($word2[$j+1:] | chars | lpad($i)) 23 | ] 24 | ; 25 | 26 | # Entry point 27 | def main: 28 | cross("computer"; "center") | (.[], "") 29 | ; 30 | 31 | main 32 | 33 | # vim:ai:sw=4:ts=4:et:syntax=jq 34 | -------------------------------------------------------------------------------- /examples/cut.jq: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/jq -cnRrf 2 | 3 | # Cut experiments 4 | 5 | def found_coin(x): 6 | x==["LA",1,2] or 7 | x==["NY",1,1] or 8 | x==["BOS",2,2] 9 | ; 10 | 11 | def find_boxes: 12 | ["LA", "NY", "BOS"][] as $city 13 | | label $cut 14 | | (1,2) as $store 15 | | (1,2) as $box 16 | | [$city, $store, $box] 17 | | if found_coin(.) 18 | then ., "--8<-------", break $cut 19 | else . 20 | end 21 | ; 22 | 23 | def cut: error("8<"); 24 | 25 | def ifcut(v): 26 | if .=="8<" then v else error end 27 | ; 28 | 29 | def find_boxes_X: 30 | ["LA", "NY", "BOS"][] as $city 31 | |try 32 | (1,2) as $store 33 | | (1,2) as $box 34 | | [$city, $store, $box] as $triple 35 | | if found_coin($triple) 36 | then $triple, cut 37 | else $triple 38 | end 39 | catch ifcut("--8<-------") 40 | ; 41 | 42 | find_boxes, 43 | "", 44 | find_boxes_X 45 | 46 | # vim:ai:sw=4:ts=4:et:syntax=jq 47 | -------------------------------------------------------------------------------- /examples/dice.jq: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/jq -cnRrf 2 | 3 | import "fadado.github.io/math" as math; 4 | import "fadado.github.io/math/chance" as chance; 5 | 6 | def dice: 7 | 1+chance::random(6; chance::randomize) 8 | ; 9 | 10 | # test 11 | def main($N): 12 | def average(g): 13 | math::sum(g) / math::count(g) 14 | ; 15 | range(4) | 16 | average(limit($N; dice)) 17 | , 18 | [ limit($N; dice) ] 19 | ; 20 | 21 | main(149) 22 | 23 | # vim:ai:sw=4:ts=4:et:syntax=jq 24 | -------------------------------------------------------------------------------- /examples/mkascii.jq: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/jq -nf 2 | 3 | include "fadado.github.io/prelude"; 4 | include "fadado.github.io/string"; 5 | import "fadado.github.io/string/table" as table; 6 | import "fadado.github.io/object/set" as set; 7 | 8 | ([range(32),127]|implode) as $cntrl | 9 | " \t\r\n\f\u000b" as $space | 10 | " \t" as $blank | 11 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" as $upper | 12 | "abcdefghijklmnopqrstuvwxyz" as $lower | 13 | ($lower+$upper) as $alpha | 14 | ($upper+$lower) as $ALPHA | 15 | "0123456789" as $digit | 16 | ($alpha+$digit) as $alnum | 17 | ($digit+"abcdefABCDEF") as $xdigit | 18 | "(<)=[*>\\!+?]{\",@^|#-_}$.`~%/&:';" as $punct | 19 | ($upper+$lower+$digit+$punct) as $graph | 20 | ($blank+$graph) as $print | 21 | 22 | { 23 | # strings 24 | "cntrl": $cntrl, 25 | "space": $space, 26 | "blank": $blank, 27 | "upper": $upper, 28 | "lower": $lower, 29 | "alpha": $alpha, 30 | "ALPHA": $ALPHA, 31 | "digit": $digit, 32 | "xdigit": $xdigit, 33 | "punct": $punct, 34 | "alnum": $alnum, 35 | "graph": $graph, 36 | "print": $print, 37 | 38 | # character sets 39 | "iscntrl": set::new($cntrl), 40 | "isspace": set::new($space), 41 | "isupper": set::new($upper), 42 | "islower": set::new($lower), 43 | "isdigit": set::new($digit), 44 | "isxdigit": set::new($xdigit), 45 | "ispunct": set::new($punct), 46 | # "isblank": set::new($blank), 47 | # "isalpha": set::new($alpha), 48 | # "isalnum": set::new($alnum), 49 | # "isgraph": set::new($graph), 50 | # "isprint": set::new($print), 51 | 52 | # translation tables 53 | "tolower": table::new($upper; $lower), 54 | "toupper": table::new($lower; $upper), 55 | } 56 | 57 | # vim:ai:sw=4:ts=4:et:syntax=jq 58 | -------------------------------------------------------------------------------- /examples/mklatin1.jq: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/jq -nf 2 | 3 | include "fadado.github.io/prelude"; 4 | include "fadado.github.io/string"; 5 | import "fadado.github.io/string/table" as table; 6 | import "fadado.github.io/object/set" as set; 7 | 8 | ([range(32),127]|implode) as $cntrl | 9 | " \t\r\n\f\u000b " as $space | 10 | " \t " as $blank | 11 | "ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÑÒÓÔÕÖØÙÚÛÜÝÞÐ___" as $upper | 12 | "abcdefghijklmnopqrstuvwxyzàáâãäåæçèéêëìíîïñòóôõöøùúûüýþ_ÿßð" as $lower | 13 | ($lower+$upper) as $alpha | 14 | ($upper+$lower) as $ALPHA | 15 | "0123456789" as $digit | 16 | ($alpha+$digit) as $alnum | 17 | ($digit+"abcdefABCDEF") as $xdigit | 18 | "(<)=[*>\\!+?]{\",@^|#-_}$.`~%/&:';¡¢£¤¥¦§¨©ª«­®¯°´µ¶¸º»¿¬±×÷¼½¾¹²³" as $punct | 19 | ($upper+$lower+$digit+$punct) as $graph | 20 | ($blank+$graph) as $print | 21 | 22 | { 23 | # strings 24 | "cntrl": $cntrl, 25 | "space": $space, 26 | "blank": $blank, 27 | "upper": $upper, 28 | "lower": $lower, 29 | "alpha": $alpha, 30 | "ALPHA": $ALPHA, 31 | "digit": $digit, 32 | "xdigit": $xdigit, 33 | "punct": $punct, 34 | "alnum": $alnum, 35 | "graph": $graph, 36 | "print": $print, 37 | 38 | # character sets 39 | "iscntrl": set::new($cntrl), 40 | "isspace": set::new($space), 41 | "isupper": set::new($upper), 42 | "islower": set::new($lower), 43 | "isdigit": set::new($digit), 44 | "isxdigit": set::new($xdigit), 45 | "ispunct": set::new($punct), 46 | # "isblank": set::new($blank), 47 | # "isalpha": set::new($alpha), 48 | # "isalnum": set::new($alnum), 49 | # "isgraph": set::new($graph), 50 | # "isprint": set::new($print), 51 | 52 | # translation tables 53 | "tolower": table::new($upper; $lower), 54 | "toupper": table::new($lower; $upper), 55 | } 56 | 57 | # vim:ai:sw=4:ts=4:et:syntax=jq 58 | -------------------------------------------------------------------------------- /examples/newton.jq: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/jq -cnRrf 2 | 3 | import "fadado.github.io/stream" as stream; 4 | import "fadado.github.io/math" as math; 5 | 6 | def nrsqrt: 7 | def square_root($N; $eps; diff): 8 | # 9 | def next: (.+$N/.) / 2; 10 | # 11 | def search(xs): 12 | [limit(2; xs)] as $ab | 13 | if ($ab|diff) <= $eps 14 | then $ab[1] 15 | else search(stream::rest(xs)) 16 | end 17 | ; 18 | # 19 | $N/2 as $init | 20 | search($init|recurse(next)) 21 | ; 22 | . as $n | 23 | 0.00001 as $e | 24 | # within 25 | square_root($n; $e; (.[0]-.[1])|fabs) 26 | # relative 27 | #square_root($n; $e; (.[0]/.[1]-1)|fabs) 28 | ; 29 | 30 | ######################################################################## 31 | 32 | def builtin: 33 | 2|sqrt | "sqrt(2) == \(.)" 34 | ; 35 | 36 | def newton: 37 | 2|nrsqrt | "newton(2) == \(.)" 38 | ; 39 | 40 | # Main 41 | builtin, newton 42 | 43 | # vim:ai:sw=4:ts=4:et:syntax=jq 44 | -------------------------------------------------------------------------------- /examples/nqbrute.jq: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/jq -cnRrf 2 | 3 | include "fadado.github.io/prelude"; 4 | import "fadado.github.io/array/tuple" as tuple; 5 | 6 | # N-Queens by brute force 7 | 8 | def queens($n): 9 | def generate: 10 | [range(0; $n)] 11 | | tuple::permutations 12 | ; 13 | def all_safe: 14 | def ascending: 15 | [range(0; length) as $row | .[$row] as $column | $row+$column] 16 | ; 17 | def descending: 18 | [range(0; length) as $row | .[$row] as $column | $row-$column] 19 | ; 20 | def repeated: 21 | sort 22 | | some( 23 | range(1; length) as $i 24 | | .[$i]==.[$i-1] 25 | ) 26 | ; 27 | (ascending|repeated|not) and (descending|repeated|not) 28 | ; 29 | def and_test: 30 | select(all_safe) 31 | ; 32 | generate | and_test 33 | ; 34 | 35 | queens(8) 36 | 37 | # vim:ai:sw=4:ts=4:et:syntax=jq 38 | -------------------------------------------------------------------------------- /examples/nqsbrute.jq: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/jq -cnRrf 2 | 3 | include "fadado.github.io/prelude"; 4 | import "fadado.github.io/math" as math; 5 | import "fadado.github.io/array/tuple" as tuple; 6 | 7 | # Streams based N-Queens by brute force 8 | 9 | def queens($n): 10 | def generate: 11 | [range(0; $n)] 12 | | tuple::permutations 13 | ; 14 | def all_safe: 15 | every( 16 | range(0; length-1) as $i # keys[] as $i 17 | | .[$i] as $j 18 | | range($i+1; length) as $k 19 | | .[$k] as $l 20 | | (($i-$k)|fabs) != (($j-$l)|fabs) 21 | ) 22 | ; 23 | def and_test: 24 | select(all_safe) 25 | ; 26 | generate | and_test 27 | ; 28 | 29 | queens(8) 30 | 31 | # vim:ai:sw=4:ts=4:et:syntax=jq 32 | -------------------------------------------------------------------------------- /examples/nqsmart.jq: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/jq -cnRrf 2 | 3 | include "fadado.github.io/prelude"; 4 | import "fadado.github.io/array" as array; 5 | 6 | # Smart N-Queens 7 | 8 | def queens($n; $columns): 9 | def safe($j): 10 | length as $i | every( 11 | range($i) as $k 12 | | .[$k] as $l 13 | | (($i-$k)|fabs) != (($j-$l)|fabs) 14 | ) 15 | ; 16 | def qput: 17 | if length == $n # assert(($columns - .) == []) 18 | then . # one solution found 19 | else 20 | # for each available column 21 | ($columns - .)[] as $column 22 | | select(safe($column)) 23 | | array::push($column) 24 | | qput 25 | end 26 | ; 27 | # 28 | [] | qput 29 | ; 30 | 31 | 8 as $N | queens($N; [range($N)]) 32 | 33 | # vim:ai:sw=4:ts=4:et:syntax=jq 34 | -------------------------------------------------------------------------------- /examples/octcode.jq: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/jq -cnRrf 2 | 3 | import "fadado.github.io/array" as array; 4 | 5 | def chars: (./"")[]; 6 | 7 | def octcode: 8 | ("01"|chars) as $d2 | 9 | ("01234567"|chars) as $d1 | 10 | ("01234567"|chars) as $d0 | 11 | $d2+$d1+$d0 12 | ; 13 | 14 | def deccode: 15 | label $pipe | 16 | ("01"|chars) as $d2 | 17 | ("0123456789"|chars) as $d1 | 18 | ("0123456789"|chars) as $d0 | 19 | if $d2 == "1" and $d1 == "2" and $d0 == "8" 20 | then break$pipe 21 | else $d2+$d1+$d0 22 | end 23 | ; 24 | 25 | def hexcode: 26 | ("01234567"|chars) as $d1 | 27 | ("0123456789ABCDEF"|chars) as $d0 | 28 | $d1+$d0 29 | ; 30 | 31 | [ [octcode], [deccode], [hexcode] ] | array::zip 32 | 33 | # vim:ai:sw=4:ts=4:et:syntax=jq 34 | -------------------------------------------------------------------------------- /examples/prolog.jq: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/jq -cnRrf 2 | 3 | include "fadado.github.io/prelude"; 4 | import "fadado.github.io/object/set" as set; 5 | 6 | # Database 7 | 8 | def biblical_family: 9 | { 10 | father: { 11 | terach: set::new(["abraham","nachor","haran"]), 12 | abraham: set::new(["isaac"]), 13 | haran: set::new(["lot","milcah","yiscah"]) 14 | }, 15 | mother: { 16 | sarah: set::new(["isaac"]) 17 | }, 18 | male: set::new(["terach","abraham","nachor","haran","isaac","lot"]), 19 | female: set::new(["sarah","milcah","yiscah"]) 20 | } 21 | ; 22 | 23 | # Facts 24 | 25 | def father($x): 26 | (.father[$x] | keys_unsorted)[] 27 | ; 28 | 29 | def father($x; $y): 30 | select(.father[$x][$y]) 31 | ; 32 | 33 | def mother($x): 34 | (.mother[$x] | keys_unsorted)[] 35 | ; 36 | 37 | def mother($x; $y): 38 | select(.mother[$x][$y]) 39 | ; 40 | 41 | def male($x): 42 | select(.male[$x]) 43 | ; 44 | 45 | def female($x): 46 | select(.female[$x]) 47 | ; 48 | 49 | # Rules 50 | 51 | def parent($x): 52 | father($x) , mother($x) 53 | ; 54 | 55 | def parent($x; $y): 56 | father($x; $y) , mother($x; $y) 57 | ; 58 | 59 | def son($x; $y): 60 | parent($y; $x) | male($x) 61 | ; 62 | 63 | def daughter($x; $y): 64 | parent($y; $x) | female($x) 65 | ; 66 | 67 | def grandfather($x; $z): 68 | father($x) as $y | father($y; $z) 69 | ; 70 | 71 | def grandmother($x; $z): 72 | mother($x) as $y | mother($y; $z) 73 | ; 74 | 75 | def grandparent($x; $y): 76 | parent($x) as $z | parent($z; $y) 77 | ; 78 | 79 | # A query 80 | 81 | biblical_family | 82 | nonempty(grandparent("terach"; "isaac")) 83 | 84 | # vim:ai:sw=4:ts=4:et:syntax=jq 85 | -------------------------------------------------------------------------------- /examples/read.jq: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/jq -cnRrf 2 | 3 | include "fadado.github.io/prelude"; 4 | 5 | import "fadado.github.io/string/ascii" as ascii; 6 | import "fadado.github.io/string" as str; 7 | 8 | # sh continuation lines style 9 | def read_sh: 10 | foreach inputs as $line ( 11 | { continuing: false, logical_line: "" } 12 | ; 13 | if .continuing then 14 | if $line[-1:] == "\\" then # add more text to line 15 | .logical_line += $line[:-1] 16 | else # complete line 17 | .continuing = false | 18 | .logical_line += $line 19 | end 20 | elif $line[-1:] == "\\" then # start continuation line 21 | .continuing = true | 22 | .logical_line = $line[:-1] 23 | else # line complete 24 | .logical_line = $line 25 | end 26 | ; 27 | select(.continuing) | .logical_line 28 | ) 29 | ; 30 | 31 | def XXXread_sh: 32 | def init: 33 | { continuing: false, logical_line: "" } 34 | ; 35 | def update($line): 36 | if .continuing then 37 | if $line[-1:] == "\\" then # add more text to line 38 | .logical_line += $line[:-1] 39 | else # complete line 40 | .continuing = false | 41 | .logical_line += $line 42 | end 43 | elif $line[-1:] == "\\" then # start continuation line 44 | .continuing = true | 45 | .logical_line = $line[:-1] 46 | else # line complete 47 | .logical_line = $line 48 | end 49 | ; 50 | def extract: 51 | select(.continuing) 52 | |.logical_line 53 | ; 54 | foreach inputs as $line 55 | (init; update($line); extract) 56 | ; 57 | 58 | # sh continuation lines style checking bad input 59 | def read_sh_check: 60 | foreach (inputs, null) as $line ( 61 | { continuing: false, logical_line: "" } 62 | ; 63 | if $line==null # EOF 64 | then . # do nothing 65 | elif .continuing then 66 | if $line[-1:] == "\\" then # add more text to line 67 | .logical_line += $line[:-1] 68 | else # complete line 69 | .continuing = false | 70 | .logical_line += $line 71 | end 72 | elif $line[-1:] == "\\" then # start continuation line 73 | .continuing = true | 74 | .logical_line = $line[:-1] 75 | else # line complete 76 | .logical_line = $line 77 | end 78 | ; 79 | if $line==null then # EOF 80 | select(.continuing) 81 | | .logical_line # last line ended in \ 82 | else 83 | reject(.continuing) 84 | | .logical_line 85 | end 86 | ) 87 | ; 88 | 89 | # SMTP and HTTP continuation lines style 90 | def read_headers: 91 | [range(3)] as [$init, $accum, $emit] | 92 | # 93 | foreach (inputs, null) as $line ( 94 | { state: $init, logical_line: null, last_line: null } 95 | ; 96 | if $line==null then # EOF 97 | if .state == $init then . # do nothing 98 | elif .state == $accum then .state = $emit 99 | elif .state == $emit then .logical_line = .last_line 100 | else error("unexpected state") 101 | end 102 | elif .state == $init then 103 | .state = $accum | 104 | .logical_line = $line 105 | elif .state == $accum then 106 | if $line[0:1]|ascii::isblank then 107 | .logical_line += " " + ($line|str::ltrim) 108 | else 109 | .state = $emit | 110 | .last_line = $line 111 | end 112 | elif .state == $emit then 113 | .state = $accum | 114 | .logical_line = .last_line | 115 | if $line[0:1]|ascii::isblank then 116 | .logical_line += " " + ($line|str::ltrim) 117 | else 118 | .state = $emit | 119 | .last_line = $line 120 | end 121 | else error("unexpected state") 122 | end 123 | ; 124 | select(.state == $emit) 125 | | .logical_line 126 | ) 127 | ; 128 | 129 | # 130 | #read_sh 131 | #read_sh_check 132 | read_headers 133 | 134 | # vim:ai:sw=4:ts=4:et:syntax=jq 135 | -------------------------------------------------------------------------------- /examples/script.jq: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/jq -cnrf 2 | 3 | include "fadado.github.io/string"; 4 | 5 | # Entry point 6 | def main($string; $subject): 7 | "Positions of \"" + $string + "\" inside \"" + $subject + "\":", 8 | ($subject|indices($string)[]) 9 | ; 10 | 11 | main("on";"one motion is optional") 12 | 13 | # vim:ai:sw=4:ts=4:et:syntax=jq 14 | -------------------------------------------------------------------------------- /examples/seconds.jq: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/jq -nRrf 2 | 3 | include "fadado.github.io/prelude"; 4 | 5 | def chars: 6 | split("")[] 7 | ; 8 | 9 | def main: 10 | label $pipe 11 | 12 | | ("012"|chars) as $h1 13 | | ("0123456789"|chars) as $h2 14 | 15 | | if $h1 == "2" and $h2 == "4" 16 | then break$pipe end 17 | 18 | | ("012345"|chars) as $m1 19 | | ("0123456789"|chars) as $m2 20 | 21 | | $h1+$h2+":"+$m1+$m2 22 | ; 23 | 24 | main 25 | 26 | # vim:ai:sw=4:ts=4:et:syntax=jq 27 | -------------------------------------------------------------------------------- /examples/sendmoremoney.jq: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/jq -cnRrf 2 | # 3 | # Determinar quins digits (0..9) poden substituir les lletres 4 | # de l'expressió següent per fer-la certa: 5 | # SEND + MORE = MONEY 6 | 7 | def num(a;b;c;d): 8 | a*1000+b*100+c*10+d 9 | ; 10 | def num(a;b;c;d;e): 11 | a*10000+b*1000+c*100+d*10+e 12 | ; 13 | 14 | def brute_force: # too slow 15 | def choose: range(0;10); 16 | label $fence 17 | | choose as $s | select($s > 0) 18 | | choose as $e 19 | | choose as $n 20 | | choose as $d 21 | | choose as $m | select($m > 0) 22 | | choose as $o 23 | | choose as $r 24 | | choose as $y 25 | | select(([$s,$e,$n,$d,$m,$o,$r,$y]|unique|length)==8) 26 | | select(num($s;$e;$n;$d) + num($m;$o;$r;$e) == num($m;$o;$n;$e;$y)) 27 | | ([$s,$e,$n,$d,$m,$o,$r,$e,$m,$o,$n,$e,$y], break $fence) 28 | ; 29 | 30 | def smart: 31 | def choose: range(0;10); 32 | label $fence 33 | | 1 as $m 34 | | 0 as $o 35 | | choose as $s | select($s > 7) 36 | | choose as $e 37 | | choose as $n 38 | | choose as $d 39 | | choose as $r 40 | | choose as $y 41 | | select(([$s,$e,$n,$d,$m,$o,$r,$y]|unique|length)==8) 42 | | select(num($s;$e;$n;$d) + num($m;$o;$r;$e) == num($m;$o;$n;$e;$y)) 43 | | ([$s,$e,$n,$d,$m,$o,$r,$e,$m,$o,$n,$e,$y], break $fence) 44 | ; 45 | 46 | def more_smart: 47 | def choose(m;n): range(m;n+1); 48 | label $fence 49 | | 1 as $m 50 | | 0 as $o 51 | | choose(8;9) as $s 52 | | choose(2;9) as $e 53 | | choose(2;9) as $n 54 | | choose(2;9) as $d 55 | | choose(2;9) as $r 56 | | choose(2;9) as $y 57 | | select(([$s,$e,$n,$d,$m,$o,$r,$y]|unique|length)==8) 58 | | select(num($s;$e;$n;$d) + num($m;$o;$r;$e) == num($m;$o;$n;$e;$y)) 59 | | ([$s,$e,$n,$d,$m,$o,$r,$e,$m,$o,$n,$e,$y], break $fence) 60 | ; 61 | 62 | def smartest: 63 | def choose(m;n;used): ([range(m;n+1)] - used)[]; 64 | label $fence 65 | | 1 as $m 66 | | 0 as $o 67 | | choose(8;9;[]) as $s 68 | | choose(2;9;[$s]) as $e 69 | | choose(2;9;[$s,$e]) as $n 70 | | choose(2;9;[$s,$e,$n]) as $d 71 | | choose(2;9;[$s,$e,$n,$d]) as $r 72 | | choose(2;9;[$s,$e,$n,$d,$r]) as $y 73 | | select(num($s;$e;$n;$d) + num($m;$o;$r;$e) == num($m;$o;$n;$e;$y)) 74 | | ([$s,$e,$n,$d,$m,$o,$r,$e,$m,$o,$n,$e,$y], break $fence) 75 | ; 76 | 77 | now as $start | "Smartest:", smartest, (now - $start), 78 | now as $start | "More smart:", more_smart, (now - $start), 79 | now as $start | "Smart:", smart, (now - $start) 80 | #now as $start | "Brute force:", brute_force, (now - $start) 81 | 82 | # vim:ai:sw=4:ts=4:et:syntax=jq 83 | -------------------------------------------------------------------------------- /examples/series.jq: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/jq -cnRrf 2 | 3 | # Panintervalic dodecaphonic series 4 | # https://en.wikipedia.org/wiki/Twelve-tone_technique 5 | 6 | # Generates all panintervalic dodecaphonic series 7 | def series($n): 8 | def _series($notes; $intervals): 9 | # . is the serie beeing constructed 10 | if $notes == [] 11 | then . 12 | else 13 | $notes[] as $note | # foreach available note 14 | [$note - .[-1] | length] as $i | # compute interval to last note in serie 15 | select($intervals | contains($i) | not) | # retract if interval is in use 16 | .[length] = $note | # extend current serie with new note 17 | _series($notes-[$note]; $intervals+$i) # recurse with one note more added to the serie 18 | end 19 | ; 20 | # 21 | [range($n)] as $notes | # set of available notes (not yet in use) 22 | [] as $intervals | # set of intervals used (between notes in constructed serie) 23 | $notes[] as $note | # for each note... 24 | [$note] as $serie | # serie: a sequence of notes 25 | $serie | _series($notes - [$note]; $intervals) 26 | ; 27 | 28 | def series: series(12); 29 | 30 | series 31 | 32 | # vim:ai:sw=4:ts=4:et:syntax=jq 33 | -------------------------------------------------------------------------------- /examples/shuffle.jq: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/jq -cnRrf 2 | 3 | import "fadado.github.io/array/choice" as choice; 4 | 5 | # test 6 | def main($N): 7 | range(10) as $i 8 | | [range($N)] 9 | | choice::shuffle 10 | | ($i, sort == [range($N)], .) 11 | ; 12 | 13 | main(12) 14 | 15 | # vim:ai:sw=4:ts=4:et:syntax=jq 16 | -------------------------------------------------------------------------------- /examples/triple.jq: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/jq -cnrf 2 | 3 | 100 as $N 4 | | range(1; 1+$N) as $a 5 | | range($a+1; 1+$N) as $b 6 | | range($b+1; 1+$N) as $c 7 | | select($a*$a+$b*$b==$c*$c) 8 | | [$a,$b,$c] 9 | 10 | # vim:ai:sw=4:ts=4:et:syntax=jq 11 | -------------------------------------------------------------------------------- /fadado.github.io/README.md: -------------------------------------------------------------------------------- 1 | # Packages 2 | 3 | # Prelude modules 4 | 5 | ``` 6 | ├── prelude.jq 7 | ├── stream.jq 8 | ├── types.jq 9 | ``` 10 | 11 | # Core packages 12 | 13 | ``` 14 | ├── array 15 | │   ├── array.jq 16 | │   ├── choice.jq 17 | │   ├── kleene.jq 18 | │   ├── set.jq 19 | │   └── tuple.jq 20 | ├── math 21 | │   ├── bitwise.jq 22 | │   ├── chance.jq 23 | │   ├── math.jq 24 | │   └── sequence.jq 25 | ├── object 26 | │   ├── object.jq 27 | │   └── set.jq 28 | ├── string 29 | │   ├── ascii.jq 30 | │   ├── latin1.jq 31 | │   ├── regexp.jq 32 | │   ├── snobol.jq 33 | │   ├── string.jq 34 | │   ├── table.jq 35 | │   └── url.jq 36 | └── word 37 | ├── alphabet.jq 38 | ├── language.jq 39 | ├── scanner.jq 40 | └── word.jq 41 | ``` 42 | 43 | # Application packages 44 | 45 | ``` 46 | ├── json 47 | │   ├── json.jq 48 | │   └── schema.jq 49 | ├── music 50 | │   ├── interval-class-vector.jq 51 | │   ├── interval-pattern.jq 52 | │   ├── interval-table.jq 53 | │   ├── pitch-class.jq 54 | │   ├── pitch-class-set.jq 55 | │   └── pitch.jq 56 | ``` 57 | -------------------------------------------------------------------------------- /fadado.github.io/array/array.jq: -------------------------------------------------------------------------------- 1 | module { 2 | name: "array", 3 | description: "Generic and stack array operations", 4 | namespace: "fadado.github.io", 5 | author: { 6 | name: "Joan Josep Ordinas Rosa", 7 | email: "jordinas@gmail.com" 8 | } 9 | }; 10 | 11 | include "fadado.github.io/prelude"; 12 | import "fadado.github.io/math" as math; 13 | 14 | ######################################################################## 15 | 16 | # Remove all x from array 17 | # Use set::remove/1 to remove only one `x` 18 | def remove($x): #:: [a]|(a) => [a] 19 | indices($x) as $ix 20 | | if $ix != [] 21 | then del(.[$ix[]]) end 22 | ; 23 | 24 | # Is the array sorted? 25 | def sorted: #:: [a] => boolean 26 | every( 27 | range(0;length-1) as $i 28 | | ($i+1) as $j 29 | | .[$i] <= .[$j]) 30 | ; 31 | 32 | # Are all elements equal? 33 | def uniform: #:: [a] => boolean 34 | every( 35 | range(0;length-1) as $i 36 | | ($i+1) as $j 37 | | .[$i] == .[$j]) 38 | ; 39 | 40 | # unknown value for index? 41 | def unknown($i): #:: [a]|(number) => boolean 42 | has($i) and .[$i]==null 43 | ; 44 | 45 | # Select elements with even indices 46 | def evens: #:: [a] => [a] 47 | if length > 0 48 | then [.[range(0;length;2)]] end 49 | ; 50 | 51 | # Select elements with odd indices 52 | def odds: #:: [a] => [a] 53 | if length > 0 54 | then [.[range(1;length;2)]] end 55 | ; 56 | 57 | # Copy here builtin 58 | def reverse: #:: [a] => [a] 59 | if length > 0 60 | then [.[length-1-range(0;length)]] end 61 | ; 62 | 63 | ######################################################################## 64 | # Stack operations 65 | 66 | def push($x): #:: [a]|(a) => [a] 67 | .[length] = $x 68 | ; 69 | 70 | def pop: #:: [a] => [a] 71 | del(.[-1]) # [] if empty 72 | ; 73 | 74 | def top: #:: [a] => a^null 75 | .[-1] # null if empty 76 | ; 77 | 78 | # 79 | # Not optimized `zip` => def zip($a; $b): [$a, $b] | transpose[]; 80 | # 81 | 82 | # 83 | def zip($a; $b): #:: ([a];[b]) => *[a,b] 84 | range(0; math::max($a,$b | length)) 85 | | [$a[.], $b[.]] 86 | ; 87 | 88 | # Generalized `zip` for 2 or more arrays. 89 | # 90 | def zip: #:: [[a],[b]...]| => *[a,b,...] 91 | . as $in 92 | | math::max(.[] | length) as $longest 93 | | length as $n 94 | | foreach range(0;$longest) as $j (null; 95 | reduce range(0;$n) as $i 96 | ([]; . + [$in[$i][$j]])) 97 | ; 98 | 99 | # vim:ai:sw=4:ts=4:et:syntax=jq 100 | -------------------------------------------------------------------------------- /fadado.github.io/array/choice.jq: -------------------------------------------------------------------------------- 1 | module { 2 | name: "array/choice", 3 | description: "Randon tuple choice", 4 | namespace: "fadado.github.io", 5 | author: { 6 | name: "Joan Josep Ordinas Rosa", 7 | email: "jordinas@gmail.com" 8 | }, 9 | }; 10 | 11 | include "fadado.github.io/prelude"; 12 | import "fadado.github.io/math/chance" as chance; 13 | 14 | # Shuffle array contents. 15 | # 16 | def shuffle($seed): #:: [a]|(number) => [a] 17 | # Swaps two array positions 18 | def swap($i; $j): 19 | if $i != $j 20 | then .[$i] as $t | .[$i]=.[$j] | .[$j]=$t end 21 | ; 22 | # Shuffle 23 | . as $array 24 | | length as $len 25 | | [limit($len; chance::rand($seed))] as $r 26 | # https://en.wikipedia.org/wiki/Fisher-Yates_shuffle 27 | # To shuffle an array a of n elements (indices 0..n-1) 28 | # for i from n−1 downto 1 do 29 | | reduce ($len - range(1; $len)) as $i 30 | ($array; 31 | # j ← random integer such that 0 ≤ j ≤ i 32 | ($r[$i] % ($i+1)) as $j 33 | # exchange a[i] and a[j] 34 | | swap($i; $j)) 35 | ; 36 | def shuffle: #:: [a] => [a] 37 | shuffle(chance::randomize) 38 | ; 39 | 40 | # Choose in order k random elements from the input array. 41 | # 42 | def take($k; $seed): #:: [a]|(number;number) => *a 43 | # Print in order k random elements from A[1]..A[n] 44 | # for (i=1; n>0; i++) 45 | # if (rand() < k/n--) { 46 | # print A[i] 47 | # k-- 48 | # } 49 | def _take($a; $r; $m): 50 | def t($n; $k): 51 | select($n >= 1) 52 | | ($m-$n) as $i 53 | | if $r[$i] < ($k/$n) 54 | then $a[$i] , t($n-1; $k-1) 55 | else t($n-1; $k) 56 | end 57 | ; 58 | t($m; $k) 59 | ; 60 | . as $a 61 | | length as $len 62 | | [limit($len; chance::rnd($seed))] as $r 63 | | _take($a; $r; $len) 64 | ; 65 | 66 | def take($k): #:: [a]|(number) => *a 67 | take($k; chance::randomize) 68 | ; 69 | 70 | # vim:ai:sw=4:ts=4:et:syntax=jq 71 | -------------------------------------------------------------------------------- /fadado.github.io/array/kleene.jq: -------------------------------------------------------------------------------- 1 | module { 2 | name: "array/kleene", 3 | description: "Kleene closure for arrays as sets", 4 | namespace: "fadado.github.io", 5 | author: { 6 | name: "Joan Josep Ordinas Rosa", 7 | email: "jordinas@gmail.com" 8 | } 9 | }; 10 | 11 | include "fadado.github.io/prelude"; 12 | 13 | ######################################################################## 14 | # Types used in declarations: 15 | # SET: [a] 16 | # TUPLE: [a] 17 | 18 | # ×, A ×, A × B, A × B × C, … 19 | def product: #:: [SET] => *TUPLE 20 | def _product: 21 | if length == 1 then 22 | .[0][] | [.] 23 | else 24 | .[0][] as $x 25 | | [$x] + (.[1:]|_product) 26 | end 27 | ; 28 | if length == 0 # empty product? 29 | then [] # identity element: empty tuple 30 | elif some(.[] | length==0) # A × ∅ 31 | then empty # ∅ of tuples 32 | else _product 33 | end 34 | ; 35 | 36 | # Aⁿ 37 | # W(n,k) = kⁿ 38 | def power($n): #:: SET|(number) => +TUPLE 39 | # assert $n >= 0 40 | if $n == 0 # A⁰ 41 | then [] # empty tuple 42 | elif length == 0 # A = ∅ 43 | then empty # ∅ of tuples 44 | else 45 | . as $set 46 | | [range(0;$n) | $set] 47 | | product 48 | end 49 | ; 50 | 51 | #def star: #:: string => +string 52 | # def k: "", .[] + k; 53 | # if length == 0 then . else (./"")|k end 54 | #; 55 | 56 | # A*: A⁰ ∪ A¹ ∪ A² ∪ A³ ∪ A⁴ ∪ A⁵ ∪ A⁶ ∪ A⁷ ∪ A⁸ ∪ A⁹… 57 | def star: #:: SET => +TUPLE 58 | if length == 0 # ∅ 59 | then . # ε 60 | else 61 | . as $set 62 | | iterate([]; .[length]=$set[]) 63 | end 64 | # Very slow: 65 | # if length == 0 66 | # then . 67 | # else power(seq) 68 | # end 69 | ; 70 | 71 | # A⁺: A¹ ∪ A² ∪ A³ ∪ A⁴ ∪ A⁵ ∪ A⁶ ∪ A⁷ ∪ A⁸ ∪ A⁹… 72 | def plus: #:: SET => *TUPLE 73 | if length == 0 # ∅ 74 | then empty # ∅ of tuples 75 | else 76 | . as $set 77 | | iterate($set[]|[.]; .[length]=$set[]) 78 | end 79 | # Very slow: 80 | # if length == 0 81 | # then empty 82 | # else power(seq(1)) 83 | # end 84 | ; 85 | 86 | # vim:ai:sw=4:ts=4:et:syntax=jq 87 | -------------------------------------------------------------------------------- /fadado.github.io/array/set.jq: -------------------------------------------------------------------------------- 1 | module { 2 | name: "array/set", 3 | description: "Arrays as sets", 4 | namespace: "fadado.github.io", 5 | author: { 6 | name: "Joan Josep Ordinas Rosa", 7 | email: "jordinas@gmail.com" 8 | } 9 | }; 10 | 11 | include "fadado.github.io/prelude"; 12 | 13 | ######################################################################## 14 | # Types used in declarations: 15 | # SET: [a] 16 | # MULSET: [a] 17 | 18 | ######################################################################## 19 | # Basic algebra of sets 20 | 21 | # ∅ [] 22 | # |S| length 23 | # {x...} [x...] 24 | # {x...} [x...] | unique 25 | # S ⊂ T S|inside(T) 26 | # S ⊃ T S|contains(T) 27 | # S – T S - T 28 | 29 | # Are all elements diferent? 30 | def unlike: #:: [a] => boolean 31 | every( 32 | range(0;length-1) as $i 33 | | range($i+1; length) as $j 34 | | .[$i] != .[$j]) 35 | ; 36 | 37 | # s + e (add element to set) 38 | def insert($x): #:: SET|(a) => SET 39 | if index($x)==null 40 | then .[length] = $x end 41 | ; 42 | 43 | # s – e (remove one element from set) 44 | # Use array::remove/1 to remove all `x` 45 | def remove($x): #:: SET|(a) => SET 46 | indices($x)[0] as $i 47 | | if $i != null 48 | then del(.[$i]) end 49 | ; 50 | 51 | # x ∈ S (x is element of S?) 52 | def element($s): #:: a|(SET) => boolean 53 | [.] | inside($s) 54 | ; 55 | 56 | # S ∋ x (S contains x as member?) 57 | def member($x): #:: SET|(a) => boolean 58 | contains([$x]) 59 | ; 60 | 61 | # S ≡ T (S is equal to T?) 62 | def equal($t): #:: SET|(SET) => boolean 63 | inside($t) and contains($t) 64 | ; 65 | 66 | # S ∩ T ≡ ∅ (no common element?) 67 | def disjoint($t): #:: SET|(SET) => boolean 68 | . - (. - $t) == [] 69 | ; 70 | 71 | # S ∪ T 72 | def union($t): #:: SET|(SET) => SET 73 | . + ($t - .) 74 | # Also: 75 | # (. + $t) | unique 76 | ; 77 | 78 | # S ∩ T 79 | def intersection($t): #:: SET|(SET) => SET 80 | . - (. - $t) 81 | ; 82 | 83 | # (S – T) ∪ (T – S) 84 | def sdifference($t): #:: SET|(SET) => SET 85 | (. - $t) + ($t - .) 86 | ; 87 | 88 | ######################################################################## 89 | # Subsets 90 | 91 | # Size (n-1) subsets 92 | # M(n) = n 93 | def minus1: #:: SET => *SET 94 | if length == 0 # none to pick? 95 | then empty # then fail 96 | else 97 | range(length-1;-1;-1) as $i 98 | | del(.[$i]) 99 | # Alternative compatible with strings 100 | # # either pick first and add to what's left with one removed 101 | # .[0:1] + (.[1:]|minus1) 102 | # # or what's left after picking the first 103 | # , .[1:] 104 | end 105 | ; 106 | 107 | # Size k subsets, Combinations 108 | # C(n,k) = P(n,k)/P(k) = (n!/(n-k)!)/k! = n!/((n-k)!k!) 109 | # C(n,n) = C(n,0) = 1 110 | # C(n,k) = 0 for k > n 111 | def combinations($k): #:: SET|(number) => *SET 112 | def _combs($k): 113 | if $k == 0 114 | then [] 115 | elif length == 0 # none to pick? 116 | then empty # then fail (no results for k > n) 117 | else 118 | # either pick one and add to what's left combinations 119 | .[0:1] + (.[1:]|_combs($k-1)) 120 | # or what's left combined 121 | , (.[1:]|_combs($k)) 122 | end 123 | ; 124 | select(0 <= $k and $k <= length) # not defined for all $k 125 | | _combs($k) 126 | ; 127 | 128 | # All subsets, stable 129 | # S(n) = 2^n 130 | def powerset: #:: SET => +SET 131 | combinations(range(0;length+1)) 132 | ; 133 | 134 | # All subsets, unstable output 135 | def powerset_u: #:: SET => +SET 136 | if length == 0 137 | then [] 138 | else 139 | (.[1:]|powerset_u) as $s 140 | | $s , .[0:1]+$s 141 | end 142 | ; 143 | 144 | ######################################################################## 145 | # Multisets 146 | 147 | # Size k multisets, Combinations with reposition 148 | # M(n,k) = C(n+k-1,k) = (n+k-1)!/(k!(n-1)!) 149 | def mulsets($k): #:: SET|(number) => *MULSET 150 | def _mulset($k): 151 | if $k == 0 152 | then [] 153 | elif length == 0 # none to pick? 154 | then empty # then fail 155 | else 156 | # either pick one and add to other multisets minus one 157 | .[0:1] + _mulset($k-1) 158 | # or what's left multisets 159 | , (.[1:]|_mulset($k)) 160 | end 161 | ; 162 | select(0 <= $k) # not defined for all $k 163 | | _mulset($k) 164 | ; 165 | 166 | # Infinite multisets from a set 167 | def mulsets: #:: SET => *MULSET 168 | mulsets(seq) 169 | ; 170 | 171 | # Multiset permutations (naïve implementation) 172 | #def arrangement: #:: [a] => *[a] 173 | # [permutations] 174 | # | unique[] 175 | #; 176 | 177 | # Multiset combinations (naïve implementation) 178 | #def disposition: #:: [a] => *[a] 179 | # [powerset] 180 | # | unique 181 | # | sort_by(length)[] 182 | #; 183 | 184 | # vim:ai:sw=4:ts=4:et:syntax=jq 185 | -------------------------------------------------------------------------------- /fadado.github.io/array/tuple.jq: -------------------------------------------------------------------------------- 1 | module { 2 | name: "array/tuple", 3 | description: "Permutations on tuples", 4 | namespace: "fadado.github.io", 5 | author: { 6 | name: "Joan Josep Ordinas Rosa", 7 | email: "jordinas@gmail.com" 8 | }, 9 | }; 10 | 11 | ######################################################################## 12 | # Types used in declarations: 13 | # TUPLE: [a] 14 | 15 | # Permutations 16 | # P(n) = n! 17 | def permutations: #:: TUPLE => *TUPLE 18 | def choose: range(0;length); 19 | # 20 | if length == 0 21 | then [] 22 | else 23 | # pick one and add to what's left permuted 24 | choose as $i 25 | | .[$i:$i+1] + (del(.[$i])|permutations) 26 | end 27 | ; 28 | 29 | # Partial permutations, Variations 30 | # V(n,k) = n!/(n-k)! 31 | # V(n,1) = n 32 | # V(n,0) = 1 33 | # V(n,n) = P(n) 34 | def permutations($k): #:: TUPLE|(number) => *TUPLE 35 | def _perm($k): 36 | def choose: range(0;length); 37 | # 38 | if $k == 1 39 | then 40 | .[] | [.] #choose as $i | .[$i:$i+1] 41 | else 42 | # choose one and add to what's left permuted 43 | choose as $i 44 | | .[$i:$i+1] + (del(.[$i])|_perm($k-1)) 45 | end 46 | ; 47 | select(0 <= $k and $k <= length) # not defined for all $k 48 | | if $k == 0 then [] else _perm($k) end 49 | ; 50 | 51 | # Circular permutations (necklaces) 52 | # Q(n) = n!/n = P(n-1) 53 | # Q(n,k) = P(n,k)/k (TODO: implement) 54 | def cycles: #:: TUPLE => *TUPLE 55 | .[0:1] + (.[1:]|permutations) 56 | ; 57 | 58 | # Derangements 59 | # TODO: formula? 60 | def derangements: #:: TUPLE => *TUPLE 61 | def choose($i): #:: [a]|(number) => [[number,a]] 62 | range(0;length) as $j 63 | | select(.[$j][0] != $i) 64 | | [$j, .[$j][1]] 65 | ; 66 | def _derange($i): #:: [a]|(number) => *[a] 67 | if length == 0 68 | then [] 69 | else 70 | # choose one valid for this "column" 71 | # and add to what's left after removing this one deranged 72 | choose($i) as [$j, $x] 73 | | [$x] + (del(.[$j])|_derange($i+1)) 74 | end 75 | ; 76 | select(length >= 2) # no derangements for less than 2 elements 77 | # . (dot) for _derange has still available enumerated elements 78 | | [range(0;length) as $i | [$i,.[$i]]] 79 | | _derange(0) 80 | ; 81 | 82 | # vim:ai:sw=4:ts=4:et:syntax=jq 83 | -------------------------------------------------------------------------------- /fadado.github.io/json/json.jq: -------------------------------------------------------------------------------- 1 | module { 2 | name: "json", 3 | description: "JSON utilities", 4 | namespace: "fadado.github.io", 5 | author: { 6 | name: "Joan Josep Ordinas Rosa", 7 | email: "jordinas@gmail.com" 8 | } 9 | }; 10 | 11 | include "fadado.github.io/prelude"; 12 | include "fadado.github.io/types"; 13 | import "fadado.github.io/string/ascii" as ascii; 14 | import "fadado.github.io/string/regexp" as re; 15 | 16 | # Is a JQ syntactic correct identifier? 17 | def isid: #:: string => boolean 18 | length > 0 19 | and ascii::isword 20 | and (.[0:1] | false==ascii::isdigit) 21 | ; 22 | def isid($s): #:: string => boolean 23 | $s | isid 24 | ; 25 | 26 | ######################################################################## 27 | # JSON to XML rendering 28 | 29 | # kind of XML token: Name (1), Nmtoken (2), other (0) 30 | def _xtok: #:: string => number 31 | "[_:A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\\x{00010000}-\\x{000EFFFF}]" 32 | as $NameStartChar | 33 | ("(:?"+$NameStartChar+"|"+"[-.·0-9\u0300-\u036F\u203F-\u2040])") 34 | as $NameChar | 35 | ("^"+$NameChar+"+"+"$") 36 | as $Nmtoken | 37 | # ("^"+$NameStartChar+$NameChar+"*"+"$") as $Name | 38 | if re::test($Nmtoken) 39 | then if re::test("^"+$NameStartChar) then 1 else 2 end 40 | else 0 41 | end 42 | ; 43 | 44 | # Is an XML Name? 45 | def xname: #:: string => boolean 46 | _xtok == 1 47 | ; 48 | def xname($s): #:: (string) => boolean 49 | $s | _xtok == 1 50 | ; 51 | 52 | # Is an XML Nmtoken? 53 | def xtoken: #:: string => boolean 54 | _xtok == 2 55 | ; 56 | def xtoken($s): #:: (string) => boolean 57 | $s | _xtok == 2 58 | ; 59 | 60 | # JSON to XML 61 | def xmldoc($root; $element; $tab; $doctype): #:: JSON|(string;string;string;string^null) => XML 62 | # construct a valid XML name 63 | def name($x): 64 | "_:A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\\x{00010000}-\\x{000EFFFF}" 65 | as $NameStartChar | 66 | ($NameStartChar+"-.·0-9\u0300-\u036F\u203F-\u2040") 67 | as $NameChar | 68 | $x 69 | | re::gsub("[^"+$NameChar+"]"; "_") 70 | | if false==re::test("^["+$NameStartChar+"]") 71 | then "_"+.[1:] end 72 | ; 73 | # recurse document 74 | def toxml($name; $margin): 75 | if isnull 76 | then "\($margin)<\($name) json:type='null'/>" 77 | elif isboolean or isnumber or isstring 78 | then "\($margin)<\($name) json:type='\(type)'>\(.)" 79 | elif isarray 80 | then 81 | if length==0 then 82 | "\($margin)<\($name) json:type='array'/>" 83 | else 84 | "\($margin)<\($name) json:type='array'>", 85 | (.[] | toxml($element; $margin+$tab)), 86 | "\($margin)" 87 | end 88 | else # isobject 89 | if length==0 then 90 | "\($margin)<\($name) json:type='object'/>" 91 | else 92 | "\($margin)<\($name) json:type='object'>", 93 | (keys_unsorted[] as $k | .[$k] | toxml(name($k); $margin+$tab)), 94 | "\($margin)" 95 | end 96 | end 97 | ; 98 | # expect valid named for root element and array elements 99 | assert(xname($root); "expected valid XML Name (\($root))") | 100 | assert(xname($element); "expected valid XML Name (\($element))")| 101 | # namespace for json: prefix 102 | "xmlns:json='https://github.com/fadado/JBOL'" as $xmlns | 103 | # render document 104 | "", 105 | $doctype//empty, 106 | if isnull 107 | then "" 108 | elif isboolean or isnumber or isstring 109 | then "\(.)" 110 | elif isarray 111 | then 112 | if length==0 then 113 | "" 114 | else 115 | "", 116 | (.[] | toxml($element; $tab)), 117 | "" 118 | end 119 | else # isobject 120 | if length==0 then 121 | "" 122 | else 123 | "", 124 | (keys_unsorted[] as $k | .[$k] | toxml(name($k); $tab)), 125 | "" 126 | end 127 | end 128 | ; 129 | 130 | def xmldoc: #:: JSON => XML 131 | xmldoc("document"; "element"; " "; null) 132 | ; 133 | 134 | # vim:ai:sw=4:ts=4:et:syntax=jq 135 | -------------------------------------------------------------------------------- /fadado.github.io/math/chance.jq: -------------------------------------------------------------------------------- 1 | module { 2 | name: "math/chance", 3 | description: "Basic pseudo-random generators", 4 | namespace: "fadado.github.io", 5 | author: { 6 | name: "Joan Josep Ordinas Rosa", 7 | email: "jordinas@gmail.com" 8 | } 9 | }; 10 | 11 | include "fadado.github.io/prelude"; 12 | 13 | # TODO: adapt to future jq builtins (expected in jq 1.6) 14 | 15 | ######################################################################## 16 | # Random 17 | ######################################################################## 18 | 19 | # Advances a seed producing random bits and a new seed. 20 | # 21 | def _NXTGEN: #:: number => [number,number] 22 | . as $previous_seed 23 | | (((214013 * $previous_seed) + 2531011) % 2147483648) as $seed # mod 2^31 24 | | ($seed / 65536 | trunc) as $value # >> 16 25 | | [ $value, $seed ] 26 | ; 27 | 28 | # Makes a seed from a starting value. 29 | # 30 | def randomize($seed): #:: (number) => number 31 | $seed | _NXTGEN[1] 32 | ; 33 | def randomize: #:: => number 34 | now | _NXTGEN[1] 35 | ; 36 | 37 | # Generates a stream of random 2^15 values. 38 | # 39 | def rand($seed): #:: (number) => *number 40 | $seed|unfold(_NXTGEN) 41 | ; 42 | 43 | # Generates a stream of random [0..1) values. 44 | # 45 | def rnd($seed): #:: (number) => *number 46 | $seed|unfold(_NXTGEN) 47 | | . / 32768 48 | ; 49 | 50 | # Generates a random [0..n) stream. 51 | # 52 | def random($n; $seed): #:: (number;number) => *number 53 | $seed|unfold(_NXTGEN) 54 | | .%($n) 55 | ; 56 | 57 | # vim:ai:sw=4:ts=4:et:syntax=jq 58 | -------------------------------------------------------------------------------- /fadado.github.io/math/math.jq: -------------------------------------------------------------------------------- 1 | module { 2 | name: "math", 3 | description: "Miscelaneous mathematical functions", 4 | namespace: "fadado.github.io", 5 | author: { 6 | name: "Joan Josep Ordinas Rosa", 7 | email: "jordinas@gmail.com" 8 | } 9 | }; 10 | 11 | include "fadado.github.io/prelude"; 12 | include "fadado.github.io/types"; 13 | 14 | ######################################################################## 15 | # Simple utilities 16 | 17 | def even($n): #:: (number) => boolean 18 | $n%2 == 0 19 | ; 20 | 21 | def odd($n): #:: (number) => boolean 22 | $n%2 == 1 23 | ; 24 | 25 | def gcd($m; $n): #:: (number;number) => number 26 | if $n == 0 27 | then $m 28 | else gcd($n; $m % $n) 29 | end 30 | ; 31 | 32 | #def gcd($m; $n): #:: (number;number) => number 33 | # def step: 34 | # .x = .n | 35 | # .n = .m % .n | 36 | # .m = .x 37 | # 38 | # ; 39 | # label $fence 40 | # | {$m, $n} 41 | # | recurse(step) 42 | # | select(.n == 0) 43 | # | .m, break $fence 44 | #; 45 | 46 | def mod($m; $n): #:: (number;number) => number 47 | ($m % $n) 48 | | if . < 0 49 | then . + $n end 50 | ; 51 | 52 | def div($m; $n): #:: (number;number) => number 53 | ($m / $n) | trunc 54 | ; 55 | 56 | def sign($n): #:: (number) => number 57 | $n|if isnan or (false==isnumber) then nan 58 | elif . > 0 then 1 59 | elif . == 0 then 0 60 | else -1 61 | end 62 | ; 63 | 64 | def tobase($b): #:: number|(number) => ?string 65 | def digit: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[.:.+1]; 66 | def div: (. / $b)|trunc; 67 | def mod: . % $b; 68 | def r: if . < $b then digit else (div|r)+(mod|digit) end; 69 | # do nothing if base out of range 70 | select(2 <= $b and $b <= 36) 71 | | r 72 | ; 73 | 74 | # Inspired in https://www.rosettacode.org/wiki/URL_decoding#jq 75 | def frombase($base): #:: string|(number) => number 76 | def downcase: 77 | if 65 <= . and . <= 90 then . + 32 end 78 | ; 79 | def toint: # "a" ~ 97 => 10 ~ 87 80 | if . > 96 then . - 87 else . - 48 end 81 | ; 82 | reduce (explode | reverse[] | downcase | toint) as $c 83 | ({power: 1, answer: 0}; 84 | (.power * $base) as $b 85 | | .answer += (.power * $c) 86 | | .power = $b) 87 | | .answer 88 | ; 89 | 90 | ######################################################################## 91 | # Reductions on streams 92 | 93 | def count(stream): #:: a|(a->*b) => number! 94 | reduce stream as $_ 95 | (0; . + 1) 96 | ; 97 | 98 | def sum(stream): #:: a|(a->*number) => number 99 | reduce stream as $item 100 | (0; . + $item) 101 | ; 102 | 103 | def mul(stream): #:: a|(a->*number) => number 104 | reduce stream as $item 105 | (1; . * $item) 106 | ; 107 | 108 | def max(stream): #:: a|(a->*number) => number 109 | reduce stream as $item 110 | (0-infinite; fmax($item; .)) 111 | ; 112 | 113 | def min(stream): #:: a|(a->*number) => number 114 | reduce stream as $item 115 | (infinite; fmin($item; .)) 116 | ; 117 | 118 | # vim:ai:sw=4:ts=4:et:syntax=jq 119 | -------------------------------------------------------------------------------- /fadado.github.io/music/interval-class-vector.jq: -------------------------------------------------------------------------------- 1 | module { 2 | name: "music/interval-class-vector", 3 | description: "Interval-class count vector", 4 | namespace: "fadado.github.io", 5 | author: { 6 | name: "Joan Josep Ordinas Rosa", 7 | email: "jordinas@gmail.com" 8 | } 9 | }; 10 | 11 | include "fadado.github.io/prelude"; 12 | import "fadado.github.io/array/set" as set; 13 | import "fadado.github.io/math" as math; 14 | import "fadado.github.io/music/pitch-class" as pc; 15 | 16 | ######################################################################## 17 | # Names used in type declarations 18 | # 19 | # PCLASS (pitch-class): 0..11 20 | # PCSET (pitch-class set): [PCLASS] 21 | # IC (interval-class): 0..6 22 | # VECTOR: 6[number]; indices are IC-1 23 | 24 | # Interval-class vector 25 | def new: #:: PCSET => VECTOR 26 | def _tally: 27 | . as $pcs 28 | | length as $n 29 | | range(0; $n-1) as $i 30 | | range($i+1; $n) as $j 31 | | $pcs[$i]|pc::interval_class($pcs[$j]) 32 | ; 33 | # interval class vector 34 | reduce _tally as $interval_class 35 | ([0,0,0,0,0,0]; .[$interval_class-1] += 1) 36 | ; 37 | def new($pcs): #:: (PCSET) => VECTOR 38 | $pcs | new 39 | ; 40 | 41 | # Hexadecimal format 42 | def format: #:: VECTOR => string 43 | reduce (.[] | math::tobase(16)) as $s 44 | (""; .+$s) 45 | ; 46 | 47 | # Howard Hanson format 48 | def name: #:: VECTOR => string 49 | . as $v # vector 50 | | ["d","s","n","m","p","t"] as $n # name 51 | | ["⁰","¹","²","³","⁴","⁵","⁶","⁷","⁸","⁹","¹⁰","¹¹","¹²"] as $d # digit 52 | | reduce ( 53 | (4,3,2,1,0,5) as $i 54 | | $v[$i] 55 | | if . == 0 56 | then empty 57 | elif . == 1 58 | then $n[$i] 59 | else $n[$i] , $d[.] end 60 | ) as $s (""; . + $s) 61 | ; 62 | 63 | # Multiplicity of interval-class in one pitch-class set 64 | def multiplicity($i): #:: VECTOR|(IC) => number 65 | .[$i-1] 66 | ; 67 | 68 | # Deep scale property 69 | def deep_scale: #:: VECTOR => boolean 70 | set::unlike 71 | ; 72 | 73 | # vim:ai:sw=4:ts=4:et:syntax=jq 74 | -------------------------------------------------------------------------------- /fadado.github.io/music/interval-pattern.jq: -------------------------------------------------------------------------------- 1 | module { 2 | name: "music/interval-pattern", 3 | description: "Ordered pitch-class set intervals pattern", 4 | namespace: "fadado.github.io", 5 | author: { 6 | name: "Joan Josep Ordinas Rosa", 7 | email: "jordinas@gmail.com" 8 | } 9 | }; 10 | 11 | include "fadado.github.io/prelude"; 12 | import "fadado.github.io/music/pitch-class" as pc; 13 | 14 | ######################################################################## 15 | # Names used in type declarations 16 | # 17 | # PCLASS (pitch-class): 0..11 18 | # PCSET (pitch-class set): [PCLASS] 19 | # PCI (pitch-class interval): 0..11 (has not direction) 20 | # PATTERN: [PCI] 21 | 22 | ######################################################################## 23 | # Intervals succession pattern 24 | # Known as directed-interval vector, interval succession, interval string, 25 | # etc. 26 | 27 | def new: #:: PCSET => PATTERN 28 | . as $pcset 29 | | [range(0;length), 0] as $ndx 30 | | [range(0;length) as $i | $pcset[$ndx[$i]]|pc::interval($pcset[$ndx[$i+1]])] 31 | # | assert(add == 12) 32 | ; 33 | 34 | # Hexadecimal format 35 | def format: #:: PATTERN => string 36 | reduce (.[] | math::tobase(16)) as $s 37 | (""; .+$s) 38 | ; 39 | 40 | # vim:ai:sw=4:ts=4:et:syntax=jq 41 | -------------------------------------------------------------------------------- /fadado.github.io/music/interval-table.jq: -------------------------------------------------------------------------------- 1 | module { 2 | name: "music/interval-table", 3 | description: "Intervals table", 4 | namespace: "fadado.github.io", 5 | author: { 6 | name: "Joan Josep Ordinas Rosa", 7 | email: "jordinas@gmail.com" 8 | } 9 | }; 10 | 11 | include "fadado.github.io/prelude"; 12 | import "fadado.github.io/music/pitch-class" as pc; 13 | 14 | ######################################################################## 15 | # Names used in type declarations 16 | # 17 | # PCLASS (pitch-class): 0..11 18 | # PCSET (pitch-class set): [PCLASS] 19 | # TABLE: [[PCI]] 20 | 21 | ######################################################################## 22 | # pitch-class step/size table 23 | 24 | # indices: generic intervals - 1 25 | # values: a list of specific intervals 26 | def new: #:: PCSET => TABLE 27 | def _table: 28 | . as $pcs 29 | | length as $n 30 | | range(0; $n-1) as $i 31 | | range($i+1; $n) as $j 32 | | ($j - $i) as $d 33 | | $pcs[$i]|pc::interval($pcs[$j]) as $c 34 | | ([$d,$c], [$n-$d,12-$c]) 35 | ; 36 | [range(0;length-2)|[]] as $t 37 | | reduce _table as [$d,$c] ($t; .[$d-1] += [$c]) 38 | | map(unique) 39 | ; 40 | 41 | # TODO 42 | # define myhill, maximally_even 43 | 44 | # vim:ai:sw=4:ts=4:et:syntax=jq 45 | -------------------------------------------------------------------------------- /fadado.github.io/music/pitch-class-set.jq: -------------------------------------------------------------------------------- 1 | module { 2 | name: "music/pitch-class-set", 3 | description: "Pitch-class sets functions", 4 | namespace: "fadado.github.io", 5 | author: { 6 | name: "Joan Josep Ordinas Rosa", 7 | email: "jordinas@gmail.com" 8 | } 9 | }; 10 | 11 | include "fadado.github.io/prelude"; 12 | include "fadado.github.io/types"; 13 | import "fadado.github.io/math" as math; 14 | import "fadado.github.io/word" as word; 15 | import "fadado.github.io/music/pitch-class" as pc; 16 | 17 | ######################################################################## 18 | # Names used in type declarations 19 | # 20 | # PCLASS (pitch-class): 0..11 21 | # PCI (pitch-class interval): 0..11 22 | # PCSET (pitch-class set): [PCLASS] 23 | # 24 | # Useful primitives: 25 | # + length :: PCSET => number 26 | # + reverse :: PCSET => PCSET 27 | # + index :: PCSET|(PCLASS) => number^null 28 | # + fromjson :: string => PCSET 29 | # + tojson :: PCSET => string 30 | # + set operations defined in package array 31 | 32 | # Produces the pitch-class set 33 | def new: #:: => PCSET 34 | if isarray 35 | then 36 | unique | map(pc::new) 37 | elif isstring then 38 | if test("^[0-9te]+$") 39 | then # compact string 40 | (./"") | map(pc::new) 41 | else # JSON array 42 | fromjson 43 | end 44 | else typerror("array or string") 45 | end 46 | ; 47 | def new($x): #:: () => PCSET 48 | $x | new 49 | ; 50 | 51 | # Format a pitch-class set as a string 52 | def format: #:: PCSET => string 53 | reduce (.[] | pc::format) as $s 54 | (""; .+$s) 55 | ; 56 | 57 | # Howard Hanson format for pitch-class-sets 58 | def name($flats): #:: PCSET|(boolean) => string 59 | . as $pcset 60 | | [range(0;length), 0] as $ndx 61 | | [range(0;length) as $i | $pcset[$ndx[$i]]|pc::interval($pcset[$ndx[$i+1]])] as $p # pattern 62 | | map(pc::name($flats)) as $n # name 63 | | ["₀","₁","₂","₃","₄","₅","₆","₇","₈","₉","₁₀","₁₁","₁₂"] as $d # digit 64 | | reduce range(0;length) as $i 65 | (""; . + $n[$i] + $d[$p[$i]]) 66 | # | .[:-1] 67 | ; 68 | def name: #:: PCSET => string 69 | name(false) 70 | ; 71 | 72 | ######################################################################## 73 | 74 | # Produces a transposed pitch-class set. 75 | def transpose($i): #:: PCSET|(PCI) => PCSET 76 | map(pc::transpose($i)) 77 | ; 78 | 79 | # Counts the number of transpositions for a pitch-class set. 80 | def transpositions: #:: PCSET => number 81 | 12 / math::gcd(12; length) 82 | ; 83 | 84 | # Produces an inverted pitch-class set. 85 | def invert: #:: PCSET => PCSET 86 | map(pc::invert) 87 | ; 88 | def invert($i): #:: PCSET|(PCI) => PCSET 89 | map(pc::invert($i)) 90 | ; 91 | 92 | ######################################################################## 93 | # Pure set operations (plus all in array/set!) 94 | 95 | # ~p 96 | def complement: #:: PCSET => PCSET 97 | . as $pcset 98 | | [ range(0;12) | reject([.]|inside($pcset)) ] 99 | ; 100 | 101 | ######################################################################## 102 | # Set class 103 | 104 | # normal form 105 | def normal: #:: PCSET => PCSET 106 | if length < 2 107 | then . 108 | else 109 | # ensure order and uniquenes 110 | unique 111 | 112 | # store last index 113 | | (length-1) as $last 114 | 115 | # build rotations 116 | | [ . , foreach range(0;$last) as $_ 117 | (.; word::rotate | .[$last] += 12) ] 118 | 119 | # get minimal distance in all rotations 120 | | (map(.[$last]-.[0]) | min) as $m 121 | 122 | # remove rotations with last distance > min 123 | | map(select(.[$last]-.[0] == $m)) 124 | 125 | # choose normal order 126 | | label $fence 127 | | recurse( 128 | range(1; $last+1) as $i 129 | | (.[0][$i] - .[0][0]) as $x 130 | | (.[1][$i] - .[1][0]) as $y 131 | | if $x < $y 132 | then del(.[1]) 133 | elif $x > $y 134 | then del(.[0]) 135 | elif $i == $last # $x == $y 136 | then del(.[1]) 137 | else empty # try next 138 | end) 139 | | select(length == 1) 140 | | . , break $fence 141 | 142 | # normalize to 0..12 143 | | [.[0][] | . % 12] 144 | end 145 | ; 146 | 147 | # proto prime 148 | def proto_: #:: PCSET => PCSET 149 | # assume . is in normal form 150 | if .[0] != 0 151 | then transpose((12-.[0])) end 152 | ; 153 | def proto: #:: PCSET => PCSET 154 | normal | proto_ 155 | ; 156 | 157 | # forte prime 158 | def prime_($i): #:: PCSET|(PCSET) => PCSET 159 | # assume . and $i are in proto form 160 | if . < $i then . else $i end 161 | ; 162 | def prime_: #:: PCSET => PCSET 163 | prime_(invert|normal|proto_) 164 | ; 165 | 166 | def prime: #:: PCSET => PCSET 167 | normal | proto_ | prime_ 168 | ; 169 | 170 | # vim:ai:sw=4:ts=4:et:syntax=jq 171 | -------------------------------------------------------------------------------- /fadado.github.io/music/pitch-class.jq: -------------------------------------------------------------------------------- 1 | module { 2 | name: "music/pitch-class", 3 | description: "Pitch-class functions", 4 | namespace: "fadado.github.io", 5 | author: { 6 | name: "Joan Josep Ordinas Rosa", 7 | email: "jordinas@gmail.com" 8 | } 9 | }; 10 | 11 | include "fadado.github.io/prelude"; 12 | include "fadado.github.io/types"; 13 | import "fadado.github.io/math" as math; 14 | import "fadado.github.io/music/pitch" as pitch; 15 | 16 | ######################################################################## 17 | # Names used in type declarations 18 | # 19 | # PCLASS (pitch-class): 0..11; as string: 0..9,t,e 20 | # PCI (pitch-class interval): 0..11 (has not direction) 21 | # IC (interval-class): 0..6 (assume interval inversion equivalence) 22 | 23 | # Produces the pitch-class corresponding to a pitch 24 | def new: #:: => PCLASS 25 | if isnumber then 26 | pitch::new % 12 27 | elif isstring then 28 | if test("^[0-9te]$") 29 | then 30 | if .=="t" # ten 31 | then 10 32 | elif .=="e" # eleven 33 | then 11 34 | else tonumber # 0..9 35 | end 36 | else 37 | # . must be a note name with octave 38 | pitch::new % 12 39 | end 40 | else typerror("number or string") 41 | end 42 | ; 43 | def new($x): #:: () => PCLASS 44 | $x | new 45 | ; 46 | 47 | # Format a pitch-class as a string 48 | def format: #:: PCLASS => string 49 | ["0","1","2","3","4","5","6","7","8","9","t","e"][.] 50 | ; 51 | 52 | # Produces the note name 53 | def name($flats): #:: PCLASS|(boolean) => string 54 | if $flats 55 | then ["C","D♭","D","E♭","E","F","G♭","G","A♭","A","B♭","B"][.] 56 | else ["C","C♯","D","D♯","E","F","F♯","G","G♯","A","A♯","B"][.] 57 | end 58 | ; 59 | def name: #:: PCLASS => string 60 | ["C","C♯","D","D♯","E","F","F♯","G","G♯","A","A♯","B"][.] 61 | ; 62 | 63 | ######################################################################## 64 | 65 | # Transposes a pitch-class 66 | def transpose($i): #:: PCLASS|(PCI) => PCLASS 67 | math::mod(. + $i; 12) 68 | ; 69 | 70 | # Inverts a pitch-class 71 | def invert: #:: PCLASS => PCLASS 72 | if . != 0 then 12 - . end 73 | ; 74 | def invert($i): #:: PCLASS|(PCI) => PCLASS 75 | math::mod($i - .; 12) 76 | # invert | transpose($i) 77 | # math::mod(-. + $i; 12) 78 | # math::mod(12 - . + $i; 12) 79 | ; 80 | 81 | # Produces the pitch-class interval (0..11) between two pitch-classes 82 | def interval($pc): #:: PCLASS|(PCLASS) => PCI 83 | math::mod($pc - .; 12) 84 | ; 85 | 86 | # Produces the interval-class (0..6) for a pitch-class interval 87 | def iclass: #:: PCI => IC 88 | if . > 6 then 12 - . end # . > tritone? 89 | ; 90 | 91 | # Produces the interval-class (0..6) between two pitch-classes 92 | def interval_class($pc): #:: PCLASS|(PCLASS) => IC 93 | math::mod($pc - .; 12) | if . > 6 then 12 - . end 94 | # interval($pc) | iclass 95 | # math::mod(fmin($pc - .; . - $pc); 12) 96 | ; 97 | 98 | # vim:ai:sw=4:ts=4:et:syntax=jq 99 | -------------------------------------------------------------------------------- /fadado.github.io/music/pitch.jq: -------------------------------------------------------------------------------- 1 | module { 2 | name: "music/pitch", 3 | description: "Pitch functions", 4 | namespace: "fadado.github.io", 5 | author: { 6 | name: "Joan Josep Ordinas Rosa", 7 | email: "jordinas@gmail.com" 8 | } 9 | }; 10 | 11 | include "fadado.github.io/prelude"; 12 | include "fadado.github.io/types"; 13 | import "fadado.github.io/string/regexp" as re; 14 | import "fadado.github.io/math" as math; 15 | 16 | ######################################################################## 17 | # Names used in type declarations 18 | # 19 | # PITCH: 0..127; as string: (C0..G10), MIDI based octave count 20 | # PI (pitch interval): -127..127 (has direction) 21 | # 22 | # Useful primitives: 23 | 24 | # Produces a new pitch 25 | def new: #:: => PITCH 26 | if isnumber then 27 | if 0 <= . and . <= 127 # . is a pitch in the range 0..127 28 | then . 29 | else error("Pitch out of range: \(.)") 30 | end 31 | elif isstring then 32 | if test("^[A-G][♯♭]?(?:[0-9]|10)$") # . is a note name with octave 33 | then 34 | match("^(?[A-G])(?[♯♭])?(?[0-9]|10)$") 35 | | re::tomap as $m 36 | | {"C":0,"D":2,"E":4,"F":5,"G":7,"A":9,"B":11}[$m["n"]] as $n 37 | | $m["o"]|tonumber * 12 38 | + if $m["a"]=="♯" 39 | then $n+1 40 | elif $m["a"]=="♭" 41 | then $n-1 42 | else $n 43 | end 44 | else 45 | error("Malformed pitch: \(.)") 46 | end 47 | else typerror("number or string") 48 | end 49 | ; 50 | def new($x): #:: () => PITCH 51 | $x | new 52 | ; 53 | 54 | # Produces the note name 55 | def name($flats): #:: PITCH|(boolean) => string 56 | if $flats 57 | then ["C","D♭","D","E♭","E","F","G♭","G","A♭","A","B♭","B"][. % 12] 58 | else ["C","C♯","D","D♯","E","F","F♯","G","G♯","A","A♯","B"][. % 12] 59 | end 60 | ; 61 | def name: #:: PITCH => string 62 | ["C","C♯","D","D♯","E","F","F♯","G","G♯","A","A♯","B"][. % 12] 63 | ; 64 | 65 | # Produces the note octave 66 | def octave: #:: PITCH => number 67 | math::div(.;12) 68 | ; 69 | 70 | # Formats a pitch (C0..G10) 71 | def format: #:: PITCH => string 72 | "\(name)\(octave)" 73 | ; 74 | 75 | ######################################################################## 76 | 77 | ## Add a directed pitch interval to the pitch 78 | def transpose($i): #:: PITCH|(PI) => PITCH 79 | . + $i 80 | | if false==(0 <= . and . <= 127) 81 | then error("Pitch out of range: \(.)") end 82 | ; 83 | 84 | # Produces the pitch interval (-127..127) between two pitches 85 | def interval($p): #:: PITCH|(PITCH) => PI 86 | $p - . 87 | ; 88 | 89 | # vim:ai:sw=4:ts=4:et:syntax=jq 90 | -------------------------------------------------------------------------------- /fadado.github.io/object/object.jq: -------------------------------------------------------------------------------- 1 | module { 2 | name: "object", 3 | description: "Object utilities", 4 | namespace: "fadado.github.io", 5 | author: { 6 | name: "Joan Josep Ordinas Rosa", 7 | email: "jordinas@gmail.com" 8 | } 9 | }; 10 | 11 | # unknown value for index? 12 | def unknown($k): #:: object|(string) => boolean 13 | has($k) and .[$k]==null 14 | ; 15 | 16 | # vim:ai:sw=4:ts=4:et:syntax=jq 17 | -------------------------------------------------------------------------------- /fadado.github.io/object/set.jq: -------------------------------------------------------------------------------- 1 | module { 2 | name: "object/set", 3 | description: "Objects managed as sets", 4 | namespace: "fadado.github.io", 5 | author: { 6 | name: "Joan Josep Ordinas Rosa", 7 | email: "jordinas@gmail.com" 8 | } 9 | }; 10 | 11 | include "fadado.github.io/prelude"; 12 | include "fadado.github.io/types"; 13 | 14 | ######################################################################## 15 | # Objects as sets 16 | # 17 | # SET: {"element": boolean, ...} 18 | # 19 | # ∅ {} 20 | # |s| length 21 | # {e...} set([e...]) 22 | # s + e s | .e=true 23 | # s + e s += {e: true} 24 | # s – e s | del(.e) 25 | # e ∈ s e | in(s) 26 | # e ∉ s e | in(s) | not 27 | # s ∋ e s | has(e) 28 | # s ∋ e s | .e 29 | # s ∌ e s | .e == null 30 | # s ≡ t s == t 31 | # s ≢ t s != t 32 | # s ∪ t s + t 33 | # s ∪ t s * t 34 | # s ∩ t s | intersection(t) 35 | # s – t s | difference(t) 36 | 37 | # Set construction from strings and arrays 38 | # 39 | def new: #:: string^[a]| => {boolean} 40 | if isstring then 41 | reduce (./"")[] as $element 42 | ({}; . += {($element):true}) 43 | elif isarray then 44 | reduce .[] as $element 45 | ({}; . += {($element|tostring):true}) 46 | else typerror("string or array") 47 | end 48 | ; 49 | def new($elements): #:: (string^[a]) => {boolean} 50 | $elements | new 51 | ; 52 | 53 | # Common sets operations 54 | # 55 | def intersection($other): #:: {boolean}|({boolean}) => {boolean} 56 | mapobj(select(.name | in($other))) 57 | ; 58 | 59 | def difference($other): #:: {boolean}|({boolean}) => {boolean} 60 | mapobj(reject(.name | in($other))) 61 | ; 62 | 63 | # vim:ai:sw=4:ts=4:et:syntax=jq 64 | -------------------------------------------------------------------------------- /fadado.github.io/stream.jq: -------------------------------------------------------------------------------- 1 | module { 2 | name: "stream", 3 | description: "Operations on generators considered as streams", 4 | namespace: "fadado.github.io", 5 | author: { 6 | name: "Joan Josep Ordinas Rosa", 7 | email: "jordinas@gmail.com" 8 | } 9 | }; 10 | 11 | include "fadado.github.io/prelude"; 12 | 13 | ######################################################################## 14 | # Generators as streams 15 | 16 | # Primitives: `first`, `last`, `limit`, `nth` 17 | 18 | # id: id x x | . 19 | # concat: g ++ h g , h 20 | # map: map f g g | f 21 | # compose: f · g g | f 22 | # apply: f a a | f 23 | # filter: filter p g g | select(p) 24 | 25 | # Is `.` inside? 26 | def member($a; stream): #:: a|(a;*a) => boolean 27 | some($a == stream) 28 | ; 29 | def member(stream): #:: a|(*a) => boolean 30 | . as $a | some($a == stream) 31 | ; 32 | 33 | # Select common content. 34 | def intersect(s; t): #:: a|(*a;*a) => *a 35 | s as $a | select(some($a == t)) 36 | ; 37 | 38 | # Are the streams (`s` and `t`) sharing contents? 39 | def sharing(s; t): #:: a|(*a;*a) => boolean 40 | some(s == t) 41 | ; 42 | 43 | # Unique for streams. 44 | def distinct(stream): 45 | foreach stream as $x ( 46 | {}; 47 | ($x|type[0:1]+tostring) as $k 48 | | if has($k) 49 | then .["~"]=false 50 | else .[$k]=$x | .["~"]=true end; 51 | select(.["~"]) | $x) 52 | ; 53 | 54 | # Remove the first element of a stream. 55 | def rest(stream): #:: a|(a->*b) => *b 56 | foreach stream as $item 57 | (1; .-1; select(. < 0) | $item) 58 | ; 59 | 60 | # One result? 61 | def singleton(stream): #:: a|(a->*b) => boolean 62 | nonempty(stream) and isempty(rest(stream)) 63 | ; 64 | 65 | # Produces enumerated items from `stream`. 66 | def enum(stream): #:: a|(a->*b) => *[number,b] 67 | foreach stream as $item 68 | (-1; .+1; [.,$item]) 69 | ; 70 | 71 | # Take from `stream` while `predicate` is true 72 | def take(stream; predicate): #:: a|(a->*b;b->boolean) => *b 73 | label $xit 74 | | stream 75 | | if predicate then . else break$xit end 76 | ; 77 | 78 | # Returns the suffix of `stream` after the first `n` elements, or 79 | # `empty` after all elements are dropped 80 | def drop($n; stream): #:: a|(number;a->*b) => *b 81 | select($n >= 0) # not defined for n < 0 or n >= #stream 82 | | if $n == 0 83 | then stream 84 | else 85 | foreach stream as $item 86 | ($n; .-1; select(. < 0) | $item) 87 | end 88 | ; 89 | 90 | # Analogous to `array[start; stop]` applied to streams. 91 | def slice($i; $j; stream): #:: a|(number;number;a->*b) => *b 92 | select($i < $j) 93 | | limit($j-$i; drop($i; stream)) 94 | ; 95 | 96 | # vim:ai:sw=4:ts=4:et:syntax=jq 97 | -------------------------------------------------------------------------------- /fadado.github.io/string/ascii.jq: -------------------------------------------------------------------------------- 1 | module { 2 | name: "string/ascii", 3 | description: "Functions in the ctype.h style for the ASCII encoding", 4 | namespace: "fadado.github.io", 5 | author: { 6 | name: "Joan Josep Ordinas Rosa", 7 | email: "jordinas@gmail.com" 8 | } 9 | }; 10 | 11 | include "fadado.github.io/prelude"; 12 | import "fadado.github.io/string/ascii" as $C; 13 | 14 | def cntrl: #:: string 15 | $C::C[0].cntrl 16 | ; 17 | def space: #:: string 18 | $C::C[0].space 19 | ; 20 | def blank: #:: string 21 | $C::C[0].blank 22 | ; 23 | def upper: #:: string 24 | $C::C[0].upper 25 | ; 26 | def lower: #:: string 27 | $C::C[0].lower 28 | ; 29 | def alpha: #:: string 30 | $C::C[0].alpha # alphabet, first lower 31 | ; 32 | def ALPHA: #:: string 33 | $C::C[0].ALPHA # alphabet, first upper 34 | ; 35 | def digit: #:: string 36 | $C::C[0].digit 37 | ; 38 | def xdigit: #:: string 39 | $C::C[0].xdigit 40 | ; 41 | def punct: #:: string 42 | $C::C[0].punct 43 | ; 44 | def alnum: #:: string 45 | $C::C[0].alnum 46 | ; 47 | def graph: #:: string 48 | $C::C[0].graph 49 | ; 50 | def print: #:: string 51 | $C::C[0].print 52 | ; 53 | 54 | def isascii: #:: string => boolean 55 | every(explode[] <= 127) 56 | ; 57 | def iscntrl: #:: string => boolean 58 | every(explode[] | . < 32 or . == 127) 59 | ; 60 | def isspace: #:: string => boolean 61 | every($C::C[0].isspace[(./"")[]]//false) 62 | ; 63 | def isblank: #:: string => boolean 64 | every((./"")[] | . == " " or . == "\t") 65 | ; 66 | def isupper: #:: string => boolean 67 | every($C::C[0].isupper[(./"")[]]//false) 68 | ; 69 | def islower: #:: string => boolean 70 | every($C::C[0].islower[(./"")[]]//false) 71 | ; 72 | def isdigit: #:: string => boolean 73 | every($C::C[0].isdigit[(./"")[]]//false) 74 | ; 75 | def isxdigit: #:: string => boolean 76 | every($C::C[0].isxdigit[(./"")[]]//false) 77 | ; 78 | def ispunct: #:: string => boolean 79 | every($C::C[0].ispunct[(./"")[]]//false) 80 | ; 81 | def isalpha: #:: string => boolean 82 | every((./"")[] | $C::C[0].isupper[.]//false or $C::C[0].islower[.]//false) 83 | ; 84 | def isalnum: #:: string => boolean 85 | every((./"")[] | $C::C[0].isupper[.]//false or $C::C[0].islower[.]//false or $C::C[0].isdigit[.]//false) 86 | ; 87 | def isgraph: #:: string => boolean 88 | every((./"")[] | $C::C[0].isupper[.]//false or $C::C[0].islower[.]//false or $C::C[0].isdigit[.]//false or $C::C[0].ispunct[.]//false) 89 | ; 90 | def isprint: #:: string => boolean 91 | every((./"")[] | $C::C[0].isupper[.]//false or $C::C[0].islower[.]//false or $C::C[0].isdigit[.]//false or $C::C[0].ispunct[.]//false or . == " " or . == "\t") 92 | ; 93 | def isword: #:: string => boolean 94 | every((./"")[] | $C::C[0].isupper[.]//false or $C::C[0].islower[.]//false or $C::C[0].isdigit[.]//false or . == "_") 95 | ; 96 | 97 | def tolower: #:: string => string 98 | mapstr($C::C[0].tolower[.]//.) 99 | ; 100 | 101 | def toupper: #:: string => string 102 | mapstr($C::C[0].toupper[.]//.) 103 | ; 104 | 105 | # Translation tables 106 | # 107 | def tt_tolower: #:: {string} 108 | $C::C[0].tolower 109 | ; 110 | def tt_toupper: #:: {string} 111 | $C::C[0].toupper 112 | ; 113 | 114 | # vim:ai:sw=4:ts=4:et:syntax=jq 115 | -------------------------------------------------------------------------------- /fadado.github.io/string/ascii.json: -------------------------------------------------------------------------------- 1 | { 2 | "cntrl": "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f\u007f", 3 | "space": "\t\n\u000b\f\r ", 4 | "blank": "\t ", 5 | "upper": "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 6 | "lower": "abcdefghijklmnopqrstuvwxyz", 7 | "ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 8 | "alpha": "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", 9 | "digit": "0123456789", 10 | "xdigit":"0123456789ABCDEFabcdef", 11 | "alnum": "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 12 | "punct": "(<)=[*>\\!+?]{\",@^|#-_}$.`~%/&:';", 13 | "graph": "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz(<)=[*>\\!+?]{\",@^|#-_}$.`~%/&:';", 14 | "print": " \t0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz(<)=[*>\\!+?]{\",@^|#-_}$.`~%/&:';", 15 | "isspace": { 16 | " ": true, 17 | "\t": true, 18 | "\r": true, 19 | "\n": true, 20 | "\f": true, 21 | "\u000b": true 22 | }, 23 | "isupper": { 24 | "A": true, 25 | "B": true, 26 | "C": true, 27 | "D": true, 28 | "E": true, 29 | "F": true, 30 | "G": true, 31 | "H": true, 32 | "I": true, 33 | "J": true, 34 | "K": true, 35 | "L": true, 36 | "M": true, 37 | "N": true, 38 | "O": true, 39 | "P": true, 40 | "Q": true, 41 | "R": true, 42 | "S": true, 43 | "T": true, 44 | "U": true, 45 | "V": true, 46 | "W": true, 47 | "X": true, 48 | "Y": true, 49 | "Z": true 50 | }, 51 | "islower": { 52 | "a": true, 53 | "b": true, 54 | "c": true, 55 | "d": true, 56 | "e": true, 57 | "f": true, 58 | "g": true, 59 | "h": true, 60 | "i": true, 61 | "j": true, 62 | "k": true, 63 | "l": true, 64 | "m": true, 65 | "n": true, 66 | "o": true, 67 | "p": true, 68 | "q": true, 69 | "r": true, 70 | "s": true, 71 | "t": true, 72 | "u": true, 73 | "v": true, 74 | "w": true, 75 | "x": true, 76 | "y": true, 77 | "z": true 78 | }, 79 | "isdigit": { 80 | "0": true, 81 | "1": true, 82 | "2": true, 83 | "3": true, 84 | "4": true, 85 | "5": true, 86 | "6": true, 87 | "7": true, 88 | "8": true, 89 | "9": true 90 | }, 91 | "isxdigit": { 92 | "0": true, 93 | "1": true, 94 | "2": true, 95 | "3": true, 96 | "4": true, 97 | "5": true, 98 | "6": true, 99 | "7": true, 100 | "8": true, 101 | "9": true, 102 | "a": true, 103 | "b": true, 104 | "c": true, 105 | "d": true, 106 | "e": true, 107 | "f": true, 108 | "A": true, 109 | "B": true, 110 | "C": true, 111 | "D": true, 112 | "E": true, 113 | "F": true 114 | }, 115 | "ispunct": { 116 | "(": true, 117 | "<": true, 118 | ")": true, 119 | "=": true, 120 | "[": true, 121 | "*": true, 122 | ">": true, 123 | "\\": true, 124 | "!": true, 125 | "+": true, 126 | "?": true, 127 | "]": true, 128 | "{": true, 129 | "\"": true, 130 | ",": true, 131 | "@": true, 132 | "^": true, 133 | "|": true, 134 | "#": true, 135 | "-": true, 136 | "_": true, 137 | "}": true, 138 | "$": true, 139 | ".": true, 140 | "`": true, 141 | "~": true, 142 | "%": true, 143 | "/": true, 144 | "&": true, 145 | ":": true, 146 | "'": true, 147 | ";": true 148 | }, 149 | "tolower": { 150 | "A": "a", 151 | "B": "b", 152 | "C": "c", 153 | "D": "d", 154 | "E": "e", 155 | "F": "f", 156 | "G": "g", 157 | "H": "h", 158 | "I": "i", 159 | "J": "j", 160 | "K": "k", 161 | "L": "l", 162 | "M": "m", 163 | "N": "n", 164 | "O": "o", 165 | "P": "p", 166 | "Q": "q", 167 | "R": "r", 168 | "S": "s", 169 | "T": "t", 170 | "U": "u", 171 | "V": "v", 172 | "W": "w", 173 | "X": "x", 174 | "Y": "y", 175 | "Z": "z" 176 | }, 177 | "toupper": { 178 | "a": "A", 179 | "b": "B", 180 | "c": "C", 181 | "d": "D", 182 | "e": "E", 183 | "f": "F", 184 | "g": "G", 185 | "h": "H", 186 | "i": "I", 187 | "j": "J", 188 | "k": "K", 189 | "l": "L", 190 | "m": "M", 191 | "n": "N", 192 | "o": "O", 193 | "p": "P", 194 | "q": "Q", 195 | "r": "R", 196 | "s": "S", 197 | "t": "T", 198 | "u": "U", 199 | "v": "V", 200 | "w": "W", 201 | "x": "X", 202 | "y": "Y", 203 | "z": "Z" 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /fadado.github.io/string/csv.jq: -------------------------------------------------------------------------------- 1 | module { 2 | name: "string/csv", 3 | description: "Comma Separated Values scanner", 4 | namespace: "fadado.github.io", 5 | author: { 6 | name: "Joan Josep Ordinas Rosa", 7 | email: "jordinas@gmail.com" 8 | } 9 | }; 10 | 11 | # From: https://tools.ietf.org/html/rfc4180 12 | # 13 | # file = [header CRLF] record *(CRLF record) [CRLF] 14 | # header = name *(COMMA name) 15 | # record = field *(COMMA field) 16 | # name = field 17 | # field = (escaped / non-escaped) 18 | # escaped = DQUOTE *(TEXTDATA / COMMA / CR / LF / 2DQUOTE) DQUOTE 19 | # non-escaped = *TEXTDATA 20 | # COMMA = %x2C 21 | # CR = %x0D 22 | # DQUOTE = %x22 23 | # LF = %x0A 24 | # CRLF = CR LF 25 | # TEXTDATA = %x20-21 / %x23-2B / %x2D-7E 26 | 27 | def fromcsv: # . is a record 28 | def csv($str; $len): 29 | # combinators (primitive: `|`, `,`, `recurse`: sequence, alternate and Kleene*) 30 | def plus(scanner): scanner | recurse(scanner); # Kleene+ 31 | def optional(scanner): first(scanner , .); 32 | # scanners: position -> position 33 | def char(test): select(. < $len and ($str[.:.+1] | test)) | .+1; 34 | def many($alphabet): select(. < $len) | last(plus(char(inside($alphabet)))) // empty; 35 | def CR: char(. == "\r"); 36 | def LF: char(. == "\n"); 37 | def COMMA: char(. == ","); 38 | def DQUOTE: char(. == "\""); 39 | def DQUOTE2: DQUOTE | DQUOTE; 40 | def TEXTDATA: char(.=="\t" or .>=" " and .!="," and .!="\"" and .!="\u007F"); 41 | def SPACE: optional(many(" \t")); 42 | def non_escaped: recurse(TEXTDATA); 43 | def escaped: DQUOTE | recurse(first(TEXTDATA , COMMA , CR , LF , DQUOTE2)) | DQUOTE; 44 | # Parse fields and records 45 | def field: . as $i | first((escaped|[true,$i,.]) , (non_escaped|[false,$i,.])); 46 | def record: 47 | def r: 48 | field as [$e,$i,$j] 49 | | if $e then $str[$i+1:$j-1] else $str[$i:$j] end 50 | , ($j | SPACE | COMMA | SPACE | r); 51 | 0|r 52 | ; 53 | # Collect record fields 54 | [ record ] 55 | ; 56 | rtrimstr("\r\n") as $str | csv($str; $str|length) 57 | ; 58 | 59 | def fromcsv(stream): # stream of records 60 | stream | fromcsv 61 | ; 62 | 63 | # vim:ai:sw=4:ts=4:et:syntax=jq 64 | -------------------------------------------------------------------------------- /fadado.github.io/string/latin1.jq: -------------------------------------------------------------------------------- 1 | module { 2 | name: "string/latin1", 3 | description: "Functions in the ctype style for the ISO-8859-1 encoding", 4 | namespace: "fadado.github.io", 5 | author: { 6 | name: "Joan Josep Ordinas Rosa", 7 | email: "jordinas@gmail.com" 8 | } 9 | }; 10 | 11 | include "fadado.github.io/prelude"; 12 | import "fadado.github.io/string/latin1" as $C; 13 | 14 | def cntrl: #:: string 15 | $C::C[0].cntrl 16 | ; 17 | def space: #:: string 18 | $C::C[0].space 19 | ; 20 | def blank: #:: string 21 | $C::C[0].blank 22 | ; 23 | def upper: #:: string 24 | $C::C[0].upper 25 | ; 26 | def lower: #:: string 27 | $C::C[0].lower 28 | ; 29 | def alpha: #:: string 30 | $C::C[0].alpha # alphabet, first lower 31 | ; 32 | def ALPHA: #:: string 33 | $C::C[0].ALPHA # alphabet, first upper 34 | ; 35 | def digit: #:: string 36 | $C::C[0].digit 37 | ; 38 | def xdigit: #:: string 39 | $C::C[0].xdigit 40 | ; 41 | def punct: #:: string 42 | $C::C[0].punct 43 | ; 44 | def alnum: #:: string 45 | $C::C[0].alnum 46 | ; 47 | def graph: #:: string 48 | $C::C[0].graph 49 | ; 50 | def print: #:: string 51 | $C::C[0].print 52 | ; 53 | 54 | def isascii: #:: string => boolean 55 | every(explode[] <= 127) 56 | ; 57 | def islatin1: #:: string => boolean 58 | every(explode[] | . > 159 and . < 256) 59 | ; 60 | def isvacant: #:: string => boolean 61 | every(explode[] | . > 127 and . < 160) 62 | ; 63 | def iscntrl: #:: string => boolean 64 | every(explode[] | . < 32 or . == 127) 65 | ; 66 | def isspace: #:: string => boolean 67 | every($C::C[0].isspace[(./"")[]]//false) 68 | ; 69 | def isblank: #:: string => boolean 70 | every((./"")[] | . == " " or . == "\t" or . == "\u00a0") 71 | ; 72 | def isupper: #:: string => boolean 73 | every($C::C[0].isupper[(./"")[]]//false) 74 | ; 75 | def islower: #:: string => boolean 76 | every($C::C[0].islower[(./"")[]]//false) 77 | ; 78 | def isdigit: #:: string => boolean 79 | every($C::C[0].isdigit[(./"")[]]//false) 80 | ; 81 | def isxdigit: #:: string => boolean 82 | every($C::C[0].isxdigit[(./"")[]]//false) 83 | ; 84 | def ispunct: #:: string => boolean 85 | every($C::C[0].ispunct[(./"")[]]//false) 86 | ; 87 | def isalpha: #:: string => boolean 88 | every((./"")[] | $C::C[0].isupper[.]//false or $C::C[0].islower[.]//false) 89 | ; 90 | def isalnum: #:: string => boolean 91 | every((./"")[] | $C::C[0].isupper[.]//false or $C::C[0].islower[.]//false or $C::C[0].isdigit[.]//false) 92 | ; 93 | def isgraph: #:: string => boolean 94 | every((./"")[] | $C::C[0].isupper[.]//false or $C::C[0].islower[.]//false or $C::C[0].isdigit[.]//false or $C::C[0].ispunct[.]//false) 95 | ; 96 | def isprint: #:: string => boolean 97 | every((./"")[] | $C::C[0].isupper[.]//false or $C::C[0].islower[.]//false or $C::C[0].isdigit[.]//false or $C::C[0].ispunct[.]//false or . == " " or . == "\t" or . == "\u00a0") 98 | ; 99 | def isword: #:: string => boolean 100 | every((./"")[] | $C::C[0].isupper[.]//false or $C::C[0].islower[.]//false or $C::C[0].isdigit[.]//false or . == "_" or . == "·") 101 | ; 102 | 103 | def tolower: #:: string => string 104 | mapstr($C::C[0].tolower[.]//.) 105 | ; 106 | 107 | def toupper: #:: string => string 108 | mapstr($C::C[0].toupper[.]//.) 109 | ; 110 | 111 | # Translation tables 112 | # 113 | def tt_tolower: #:: {string} 114 | $C::C[0].tolower 115 | ; 116 | def tt_toupper: #:: {string} 117 | $C::C[0].toupper 118 | ; 119 | 120 | # vim:ai:sw=4:ts=4:et:syntax=jq 121 | -------------------------------------------------------------------------------- /fadado.github.io/string/roman.jq: -------------------------------------------------------------------------------- 1 | module { 2 | name: "string/roman", 3 | description: "Roman numerals encoding and decoding", 4 | namespace: "fadado.github.io", 5 | author: { 6 | name: "Joan Josep Ordinas Rosa", 7 | email: "jordinas@gmail.com" 8 | } 9 | }; 10 | 11 | include "fadado.github.io/prelude"; 12 | 13 | def encode: #:: number => string 14 | def _encode($digit; $up): 15 | def div: (. / 10) | trunc; 16 | def mod: . % 10; 17 | def shift: mapstr($up[.]); 18 | def roman: 19 | if . < 10 20 | then $digit[.] 21 | else (div|roman|shift) + $digit[mod] 22 | end 23 | ; 24 | roman 25 | ; 26 | assert(0 < . and . < 4000; "Roman numeral out of range") 27 | | ["","I","II","III","IV","V","VI","VII","VIII","IX"] as $digit 28 | | {"I":"X","V":"L","X":"C","L":"D","C":"M"} as $up 29 | | _encode($digit; $up) 30 | ; 31 | 32 | def encode($n): #:: (number) => string 33 | $n|encode 34 | ; 35 | 36 | def decode: #:: string => number 37 | def step($sym; $val; $len): 38 | def r: 39 | if .roman | startswith($sym) 40 | then .decimal += $val | .roman |= .[$len:] | r 41 | else . 42 | end 43 | ; 44 | r 45 | ; 46 | reduce (["M",1000], ["CM",900], ["D",500], ["CD",400], 47 | ["C",100], ["XC",90], ["L",50], ["XL",40], 48 | ["X",10], ["IX",9], ["V",5], ["IV",4], 49 | ["I",1]) 50 | as [$sym,$val] 51 | ({roman:., decimal:0}; step($sym; $val; $sym|length)) 52 | | .decimal 53 | 54 | ; 55 | 56 | def decode($roman): #:: (string) => number 57 | $roman|decode 58 | ; 59 | 60 | # vim:ai:sw=4:ts=4:et:syntax=jq 61 | -------------------------------------------------------------------------------- /fadado.github.io/string/string.jq: -------------------------------------------------------------------------------- 1 | module { 2 | name: "string", 3 | description: "Common string operations, some in the Icon language style", 4 | namespace: "fadado.github.io", 5 | author: { 6 | name: "Joan Josep Ordinas Rosa", 7 | email: "jordinas@gmail.com" 8 | } 9 | }; 10 | 11 | include "fadado.github.io/prelude"; 12 | import "fadado.github.io/math" as math; 13 | 14 | # 15 | def remove($s): #:: string|(string) => string 16 | mapstr(reject(inside($s))) 17 | ; 18 | 19 | ######################################################################## 20 | # Fast concat and join only for string arrays 21 | 22 | def join: #:: [string] => string 23 | reduce .[] as $s (""; . + $s) 24 | ; 25 | 26 | def join($separator): #:: [string]|(string) => string 27 | def sep: 28 | if .==null 29 | then "" 30 | else .+$separator 31 | end 32 | ; 33 | reduce .[] as $s (null; sep + $s) 34 | // "" 35 | ; 36 | 37 | ######################################################################## 38 | # Conversions code <=> character 39 | 40 | def ord($s): #:: (string) => number 41 | $s[0:1] | explode[0] 42 | ; 43 | 44 | def char($n): #:: (number) => string 45 | [$n] | implode 46 | ; 47 | 48 | ######################################################################## 49 | # Pad strings 50 | 51 | def left($n; $t): #:: string|(number;string) => string 52 | if $n > length 53 | then ($t*($n-length)) + . end 54 | ; 55 | def left($n): #:: string|(number) => string 56 | left($n; " ") 57 | ; 58 | 59 | def right($n; $t): #:: string|(number;string) => string 60 | if $n > length 61 | then . + ($t*($n-length)) end 62 | ; 63 | def right($n): #:: string|(number) => string 64 | right($n; " ") 65 | ; 66 | 67 | def center($n; $t): #:: string|(number;string) => string 68 | if $n > length then 69 | math::div($n-length; 2) as $i 70 | | ($t*$i) + . + ($t*$i) 71 | | if length != $n then .+$t end 72 | end 73 | ; 74 | def center($n): #:: string|(number) => string 75 | center($n; " ") 76 | ; 77 | 78 | ######################################################################## 79 | # Classical trim and strip 80 | 81 | def _lndx(predicate): # left index or empty if not found 82 | label $fence 83 | | range(0;length-1) as $i 84 | | reject(.[$i:$i+1] | predicate) 85 | | ($i , break $fence) 86 | ; 87 | 88 | def _rndx(predicate): # rigth index or empty if not found 89 | label $fence 90 | | range(length-1;0;-1) as $i 91 | | reject(.[$i:$i+1] | predicate) 92 | | ($i+1 , break $fence) 93 | ; 94 | 95 | def lstrip($s): #:: string|(string) => string 96 | if length != 0 and (.[0:1] | inside($s)) then 97 | (_lndx(inside($s)) // -1) as $i | 98 | if $i < 0 then "" else .[$i:] end 99 | end 100 | ; 101 | 102 | def rstrip($s): #:: string|(string) => string 103 | if length != 0 and (.[-1:length] | inside($s)) then 104 | (_rndx(inside($s)) // -1) as $i | 105 | if $i < 0 then "" else .[0:$i] end 106 | end 107 | ; 108 | 109 | def strip($s): #:: string|(string) => string 110 | if length != 0 and ((.[0:1] | inside($s)) or (.[-1:length] | inside($s))) then 111 | (_lndx(inside($s)) // -1) as $i | 112 | (_rndx(inside($s)) // -1) as $j | 113 | if $i < 0 and $j < 0 then "" 114 | elif $j < 0 then .[$i:] 115 | elif $i < 0 then .[:$j] 116 | else .[$i:$j] 117 | end 118 | end 119 | ; 120 | 121 | def trim: #:: string| => string 122 | strip(" \t\r\n\f") 123 | ; 124 | def ltrim: #:: string| => string 125 | lstrip(" \t\r\n\f") 126 | ; 127 | def rtrim: #:: string| => string 128 | rstrip(" \t\r\n\f") 129 | ; 130 | 131 | # vim:ai:sw=4:ts=4:et:syntax=jq 132 | -------------------------------------------------------------------------------- /fadado.github.io/string/table.jq: -------------------------------------------------------------------------------- 1 | module { 2 | name: "string/table", 3 | description: "Translation tables in the SNOBOL language style", 4 | namespace: "fadado.github.io", 5 | author: { 6 | name: "Joan Josep Ordinas Rosa", 7 | email: "jordinas@gmail.com" 8 | } 9 | }; 10 | 11 | include "fadado.github.io/prelude"; 12 | import "fadado.github.io/string/ascii" as ascii; 13 | import "fadado.github.io/object/set" as set; 14 | 15 | ######################################################################## 16 | # Translation tables 17 | 18 | # Translate/remove tables 19 | def new($from; $to): #:: (string;string) => {string} 20 | ($from/"") as $f 21 | | ($to/"") as $t 22 | | reduce range(0;$f|length) as $i 23 | ({}; . += {($f[$i]):($t[$i]//"")}) 24 | ; 25 | 26 | # Preserve tables 27 | def preserve($from; $preserve): #:: (string;string) => {string} 28 | set::new($preserve) as $p 29 | | reduce ($from/"")[] as $f 30 | ({}; . += (if $p[$f] then null else {($f):""} end)) 31 | ; 32 | 33 | # Translate characters in input string using translation table 34 | def translate($table): #:: string|({string}) => string 35 | mapstr($table[.]//.) 36 | ; 37 | 38 | def translate($from; $to): #:: string|(string;string) => string 39 | new($from; $to) as $table 40 | | mapstr($table[.]//.) 41 | ; 42 | 43 | # Translation table to rotate by 13 places 44 | def rot13: #:: {string} 45 | def rotate: .[13:] + .[:13]; 46 | new(ascii::ALPHA; 47 | (ascii::upper|rotate) + (ascii::lower|rotate)) 48 | ; 49 | 50 | # rot13: s|translate(rot13) 51 | # toggle: s|translate(new(latin1::ALPHA; latin1::alpha)) 52 | # remove: s|translate("to delete"; "") 53 | # preserve: s|translate(s|translate("to preserve"; "")); "") 54 | # preserve: s|translate(preserve(s; "to preserve")) 55 | 56 | # vim:ai:sw=4:ts=4:et:syntax=jq 57 | -------------------------------------------------------------------------------- /fadado.github.io/string/url.jq: -------------------------------------------------------------------------------- 1 | module { 2 | name: "string/url", 3 | description: "URL string operations", 4 | namespace: "fadado.github.io", 5 | author: { 6 | name: "Joan Josep Ordinas Rosa", 7 | email: "jordinas@gmail.com" 8 | } 9 | }; 10 | 11 | import "fadado.github.io/math" as math; 12 | 13 | # Inspired in https://www.rosettacode.org/wiki/URL_decoding#jq 14 | def decode: #:: string| => string 15 | . as $in 16 | | label $fence 17 | 18 | | length as $length 19 | | {i: 0, answer: ""} 20 | 21 | | recurse( 22 | if $in[.i:.i+1] == "%" 23 | then 24 | .answer += ([$in[.i+1:.i+3] | math::frombase(16)] | implode) 25 | | .i += 3 26 | else 27 | .answer += $in[.i:.i+1] 28 | | .i += 1 29 | end) 30 | | select(.i >= $length) 31 | | . , break $fence 32 | 33 | | .answer 34 | ; 35 | 36 | # vim:ai:sw=4:ts=4:et:syntax=jq 37 | -------------------------------------------------------------------------------- /fadado.github.io/types.jq: -------------------------------------------------------------------------------- 1 | module { 2 | name: "types", 3 | description: "Type predicates and utilities", 4 | namespace: "fadado.github.io", 5 | author: { 6 | name: "Joan Josep Ordinas Rosa", 7 | email: "jordinas@gmail.com" 8 | } 9 | }; 10 | 11 | # 12 | # Data type predicates 13 | # 14 | 15 | # querying . 16 | def isnull: #:: a => boolean 17 | type == "null" 18 | ; 19 | def isboolean: #:: a => boolean 20 | type == "boolean" 21 | ; 22 | def isnumber: #:: a => boolean 23 | type == "number" 24 | ; 25 | def isinteger: #:: a => boolean 26 | type == "number" and . == trunc 27 | ; 28 | def isreal: #:: a => boolean 29 | type == "number" and . != trunc 30 | ; 31 | def isstring: #:: a => boolean 32 | type == "string" 33 | ; 34 | def ischar: #:: a => boolean 35 | type == "string" and length == 1 36 | ; 37 | def isarray: #:: a => boolean 38 | type == "array" 39 | ; 40 | def isobject: #:: a => boolean 41 | type == "object" 42 | ; 43 | def isscalar: #:: a => boolean 44 | type| . == "null" or . == "boolean" or . == "number" or . == "string" 45 | ; 46 | def isiterable: #:: a => boolean 47 | type| . == "array" or . == "object" 48 | ; 49 | def isvoid: #:: a => boolean 50 | isiterable and length == 0 51 | ; 52 | def nonvoid: #:: a => boolean 53 | isiterable and length > 0 54 | ; 55 | 56 | # querying parameter 57 | def isnull($a): #:: (a) => boolean 58 | $a|type == "null" 59 | ; 60 | def isboolean($a): #:: (a) => boolean 61 | $a|type == "boolean" 62 | ; 63 | def isnumber($a): #:: (a) => boolean 64 | $a|type == "number" 65 | ; 66 | def isinteger($a): #:: (a) => boolean 67 | $a|type == "number" and . == trunc 68 | ; 69 | def isreal($a): #:: (a) => boolean 70 | $a|type == "number" and . != trunc 71 | ; 72 | def isstring($a): #:: (a) => boolean 73 | $a|type == "string" 74 | ; 75 | def ischar($a): #:: (a) => boolean 76 | $a|type == "string" and length == 1 77 | ; 78 | def isarray($a): #:: (a) => boolean 79 | $a|type == "array" 80 | ; 81 | def isobject($a): #:: (a) => boolean 82 | $a|type == "object" 83 | ; 84 | def isscalar($a): #:: (a) => boolean 85 | $a|type| . == "null" or . == "boolean" or . == "number" or . == "string" 86 | ; 87 | def isiterable($a): #:: (a) => boolean 88 | $a|type| . == "array" or . == "object" 89 | ; 90 | def isvoid($a): #:: (a) => boolean 91 | $a|isiterable and length == 0 92 | ; 93 | def nonvoid($a): #:: (a) => boolean 94 | $a|isiterable and length > 0 95 | ; 96 | 97 | # coerce to bool (exactly true or false) 98 | def tobool: #:: a => boolean 99 | if . then true else false end 100 | ; 101 | # coerce also empty to false 102 | def tobool(a): #:: (a) => boolean 103 | # (first(a)|tobool) // false 104 | if first(a) // false then true else false end 105 | ; 106 | 107 | # Path expressions 108 | def ispath(x): 109 | try (isempty(path(x)) or true) 110 | catch false 111 | ; 112 | 113 | def isvalue(x): 114 | try (path(x)//null | false) 115 | catch true 116 | ; 117 | 118 | # Type errors 119 | 120 | def typerror: 121 | type | error("Type error: unexpected \(.)") 122 | ; 123 | 124 | def typerror($s): 125 | type | error("Type error: expected" + $s + ", not \(.)") 126 | ; 127 | 128 | # vim:ai:sw=4:ts=4:et:syntax=jq 129 | -------------------------------------------------------------------------------- /fadado.github.io/word/alphabet.jq: -------------------------------------------------------------------------------- 1 | module { 2 | name: "word/alphabet", 3 | description: "Alphabet operations", 4 | namespace: "fadado.github.io", 5 | author: { 6 | name: "Joan Josep Ordinas Rosa", 7 | email: "jordinas@gmail.com" 8 | } 9 | }; 10 | 11 | include "fadado.github.io/prelude"; 12 | include "fadado.github.io/types"; 13 | import "fadado.github.io/array/kleene" as kleene; 14 | 15 | ######################################################################## 16 | # Types used in declarations: 17 | # ALPHABET: [a]^string 18 | # WORD: [a]^string 19 | 20 | ######################################################################## 21 | # Operations on alphabets 22 | 23 | # Σⁿ: size n words over an alphabet 24 | # W(n,k) = kⁿ 25 | def power($n): #:: ALPHABET|(number) => *WORD 26 | # assert $n >= 0 27 | if $n == 0 # Σ⁰ 28 | then .[0:0] # ε 29 | elif length == 0 # Σ = ∅ 30 | then empty # ∅ of words 31 | elif isstring then 32 | explode | kleene::power($n) | implode 33 | elif isarray then 34 | kleene::power($n) 35 | else typerror 36 | end 37 | ; 38 | 39 | # Σ*: Σ⁰ ∪ Σ¹ ∪ Σ² ∪ Σ³ ∪ Σ⁴ ∪ Σ⁵ ∪ Σ⁶ ∪ Σ⁷ ∪ Σ⁸ ∪ Σ⁹… 40 | def star: #:: ALPHABET => *WORD 41 | if length == 0 # ∅ 42 | then . # ε 43 | elif isstring then 44 | (./"") as $set 45 | | iterate(""; .+$set[]) 46 | elif isarray then 47 | . as $set 48 | | iterate([]; .[length]=$set[]) 49 | else typerror 50 | end 51 | ; 52 | 53 | # Σ⁺: Σ¹ ∪ Σ² ∪ Σ³ ∪ Σ⁴ ∪ Σ⁵ ∪ Σ⁶ ∪ Σ⁷ ∪ Σ⁸ ∪ Σ⁹… 54 | def plus: #:: ALPHABET => *WORD 55 | if length == 0 # ∅ 56 | then empty # ∅ of words 57 | elif isstring then 58 | (./"") as $set 59 | | iterate($set[]; .+$set[]) 60 | elif isarray then 61 | . as $set 62 | | iterate($set[]|[.]; .[length]=$set[]) 63 | else typerror 64 | end 65 | ; 66 | 67 | # vim:ai:sw=4:ts=4:et:syntax=jq 68 | -------------------------------------------------------------------------------- /fadado.github.io/word/factor.jq: -------------------------------------------------------------------------------- 1 | module { 2 | name: "word/factor", 3 | description: "Operations on word factors", 4 | namespace: "fadado.github.io", 5 | author: { 6 | name: "Joan Josep Ordinas Rosa", 7 | email: "jordinas@gmail.com" 8 | } 9 | }; 10 | 11 | ######################################################################## 12 | # Types used in declarations: 13 | # WORD: [a]^string 14 | 15 | ######################################################################## 16 | # Match one word 17 | 18 | # Prefix? 19 | def isprefix($u): #:: WORD|(WORD) => boolean 20 | ($u|length) as $j 21 | | $j <= length and .[0:$j] == $u 22 | ; 23 | 24 | # Suffix? 25 | def issuffix($u): #:: WORD|(WORD) => boolean 26 | ($u|length) as $j 27 | | $j == 0 or $j <= length and .[-$j:] == $u 28 | ; 29 | 30 | # Factor? 31 | def isfactor($u): #:: WORD|(WORD) => boolean 32 | ($u|length) as $j 33 | | 0 < $j and index($u)!=null 34 | ; 35 | 36 | # Proper prefix? 37 | def ispprefix($u): #:: WORD|(WORD) => boolean 38 | length > ($u|length) and isprefix($u) 39 | ; 40 | 41 | # Proper suffix? 42 | def ispsuffix($u): #:: WORD|(WORD) => boolean 43 | length > ($u|length) and issuffix($u) 44 | ; 45 | 46 | # Proper factor? 47 | def ispfactor($u): #:: WORD|(WORD) => boolean 48 | ($u|length) as $j 49 | | 0 < $j and $j < length and index($u)!=null 50 | ; 51 | 52 | ######################################################################## 53 | # Word streams 54 | 55 | # Sets of prefixes (without the empty word) 56 | def prefixes: #:: WORD => *WORD 57 | .[:range(1;length+1)] 58 | ; 59 | 60 | # Sets of suffixes (without the empty word) 61 | def suffixes: #:: WORD => *WORD 62 | .[range(length-1;-1;-1):] 63 | ; 64 | 65 | # Sets of factors, (without the empty word) 66 | def factors: #:: WORD => *WORD 67 | # length order: 68 | range(1;length+1) as $j 69 | | range(0;length-$j+1) as $i 70 | | .[$i:$i+$j] 71 | # different order: 72 | # range(0;length+1) as $i 73 | # | range($i+1; length+1) as $j 74 | # | .[$i:$j] 75 | ; 76 | 77 | # vim:ai:sw=4:ts=4:et:syntax=jq 78 | -------------------------------------------------------------------------------- /fadado.github.io/word/language.jq: -------------------------------------------------------------------------------- 1 | module { 2 | name: "word/language", 3 | description: "Language operations", 4 | namespace: "fadado.github.io", 5 | author: { 6 | name: "Joan Josep Ordinas Rosa", 7 | email: "jordinas@gmail.com" 8 | } 9 | }; 10 | 11 | include "fadado.github.io/prelude"; 12 | include "fadado.github.io/types"; 13 | import "fadado.github.io/array/kleene" as kleene; 14 | 15 | ######################################################################## 16 | # Types used in declarations: 17 | # WORD: [a]^string 18 | # LANGUAGE: [WORD] 19 | # ε: "" for string WORD, [] for array WORD 20 | 21 | # Fibbonacci strings language 22 | # fibstr("a"; "b") => "a","b","ab","bab","abbab"… 23 | def fibstr($w; $u): #:: (WORD;WORD) => +WORD 24 | [$w,$u] 25 | | recurse([.[-1], .[-2]+.[-1]]) 26 | | .[-2] 27 | ; 28 | 29 | ######################################################################## 30 | # Operations on languages 31 | 32 | # L, L1 × L2, L1 × L2 × L3, ... 33 | def concat: #:: [LANGUAGE] => *WORD 34 | def _concat: 35 | if length == 1 then 36 | .[0][] 37 | else 38 | .[0][] as $x 39 | | $x + (.[1:]|_concat) 40 | # reverse order: .[0][] + (.[1:]|_concat) 41 | end 42 | ; 43 | if length == 0 # × 44 | then error("language::concat cannot determine ε for empty languages") 45 | elif some(.[] | length==0) # L × ∅ 46 | then empty # ∅ of words 47 | else _concat 48 | end 49 | ; 50 | 51 | # Lⁿ 52 | def power($n): #:: LANGUAGE|(number) => *WORD 53 | # assert $n >= 0 54 | if $n == 0 then # L⁰ 55 | if length > 0 56 | then .[0][0:0] # ε 57 | else error("language::power cannot determine ε for empty languages") 58 | end 59 | elif length == 0 # L = ∅ 60 | then empty # ∅ of words 61 | elif isstring(.[0]) or isarray(.[0]) then 62 | . as $lang 63 | | [range(0;$n) | $lang] 64 | | concat 65 | else .[0]|typerror("string or array") 66 | end 67 | ; 68 | 69 | # L* 70 | def star: #:: LANGUAGE => *WORD 71 | if length == 0 then # ∅ (empty language) 72 | error("language::star cannot determine ε for empty languages") 73 | elif isstring(.[0]) then 74 | . as $lang | iterate(""; .+$lang[]) 75 | elif isarray(.[0]) then 76 | . as $lang | iterate([]; .+$lang[]) 77 | else .[0]|typerror("string or array") 78 | end 79 | ; 80 | 81 | # L⁺ 82 | def plus: #:: LANGUAGE => *WORD 83 | if length == 0 then # ∅ (empty language) 84 | empty # ∅ 85 | elif isstring(.[0]) or isarray(.[0]) then 86 | . as $lang | iterate($lang[]; .+$lang[]) 87 | else .[0]|typerror("string or array") 88 | end 89 | ; 90 | 91 | # vim:ai:sw=4:ts=4:et:syntax=jq 92 | -------------------------------------------------------------------------------- /fadado.github.io/word/word.jq: -------------------------------------------------------------------------------- 1 | module { 2 | name: "word", 3 | description: "Generic operations on strings and arrays", 4 | namespace: "fadado.github.io", 5 | author: { 6 | name: "Joan Josep Ordinas Rosa", 7 | email: "jordinas@gmail.com" 8 | } 9 | }; 10 | 11 | include "fadado.github.io/types"; 12 | import "fadado.github.io/math" as math; 13 | 14 | ######################################################################## 15 | # Types used in declarations: 16 | # WORD: [a]^string 17 | # SYMBOL: singleton WORD 18 | 19 | ######################################################################## 20 | # Generic operations on strings and arrays 21 | 22 | # Word w: [...] or "..." 23 | # Empty word: [] or "" 24 | # Concatenate: w + u 25 | # Length of w: w|length 26 | 27 | # Generic alphabet 28 | def alphabet: #:: WORD => WORD 29 | if isstring 30 | then explode | unique | implode 31 | elif isarray 32 | then unique 33 | else typerror 34 | end 35 | ; 36 | 37 | # Merge two words pairwise 38 | def blend($x; $y): #:: (WORD;WORD) => WORD 39 | def _blend($a; $b; $e): 40 | reduce range(0; math::max($x,$y | length)) as $i 41 | ($e; .+$x[$i]+$y[$i]) 42 | ; 43 | if isstring($x) then 44 | _blend($x/""; $y/""; "") 45 | elif isarray then 46 | _blend($x; $y; []) 47 | else typerror 48 | end 49 | ; 50 | 51 | # Number of u's in w 52 | def count($u): #:: WORD|(WORD) => number 53 | indices($u) | length 54 | ; 55 | 56 | # Generic reverse 57 | def mirror: #:: WORD => WORD 58 | if length == 0 59 | then . 60 | elif isstring 61 | then explode | [.[length-1-range(0;length)]] | implode 62 | elif isarray 63 | then [.[length-1-range(0;length)]] 64 | else typerror 65 | end 66 | ; 67 | 68 | # Rotate in both directions 69 | def rotate($n): #:: WORD|(number) => WORD 70 | .[$n:] + .[:$n] 71 | ; 72 | def rotate: #:: WORD => WORD 73 | .[1:] + .[:1] 74 | ; 75 | 76 | # Splice word: replace or delete slice 77 | def splice($i; $j; $u): #:: WORD|(number;number;WORD^null) => WORD 78 | if $i > $j or $j > length 79 | then . # cannot splice 80 | else .[:$i] + $u + .[$j:] # with $u == null: delete! 81 | end 82 | ; 83 | def splice($w; $i; $j; $u): #:: (WORD;number;number;WORD) => WORD 84 | $w|splice($i;$j;$u) 85 | ; 86 | 87 | # Replace sub-words 88 | def sub($s; $r): #:: WORD|(WORD;WORD) => WORD 89 | ($s|length) as $n 90 | | if length == 0 or $n == 0 91 | then . 92 | else 93 | index($s) as $i 94 | | if $i == null 95 | then . 96 | else splice($i; $i+$n; $r) end 97 | end 98 | ; 99 | 100 | def gsub($s; $r): #:: WORD|(WORD;WORD) => WORD 101 | ($s|length) as $n 102 | | if length == 0 or $n == 0 103 | then . 104 | else 105 | reduce (indices($s)|reverse[]) as $i 106 | (.; splice($i; $i+$n; $r)) 107 | end 108 | ; 109 | 110 | ######################################################################## 111 | # Word iteration 112 | 113 | # Product, catenate: w + u 114 | 115 | # Generates wⁿ (one word: w concatenated n times) 116 | def power($n): #:: WORD|(number) => WORD 117 | # assert $n >= 0 118 | if isstring then 119 | if $n == 0 or length == 0 120 | then "" 121 | else . * $n end 122 | elif isarray then 123 | if $n == 0 or length == 0 124 | then [] 125 | else 126 | . as $word 127 | | reduce range(0;$n) as $_ 128 | ([]; . + $word) 129 | end 130 | else typerror 131 | end 132 | ; 133 | 134 | # Generates w*: w⁰ ∪ w¹ ∪ w² ∪ w³ ∪ w⁴ ∪ w⁵ ∪ w⁶ ∪ w⁷ ∪ w⁸ ∪ w⁹… 135 | def star: #:: WORD => +WORD 136 | . as $word 137 | | if isstring then "" else [] end 138 | | recurse(. + $word) 139 | ; 140 | 141 | # Generates w⁺: w¹ ∪ w² ∪ w³ ∪ w⁴ ∪ w⁵ ∪ w⁶ ∪ w⁷ ∪ w⁸ ∪ w⁹… 142 | def plus: #:: WORD => +WORD 143 | . as $word 144 | | recurse(. + $word) 145 | ; 146 | 147 | # vim:ai:sw=4:ts=4:et:syntax=jq 148 | -------------------------------------------------------------------------------- /schemata/README.md: -------------------------------------------------------------------------------- 1 | # JSON schema examples 2 | 3 | ## Meta-schemas 4 | 5 | The meta-schemas are the schemas which define the JSON Schema and Hyper-Schema 6 | formats. 7 | 8 | * `hyper-schema.schema.json` 9 | * `schema.schema.json` 10 | 11 | The meta-schema `hyper-schema_expanded.schema.json` is an standalone equivalent 12 | to `hyper-schema.schema.json` with the internal reference to 13 | `schema.schema.json` expanded. 14 | 15 | ## Standard schemas 16 | 17 | These sample schemas describe simple data structures which can be expressed as 18 | JSON. 19 | 20 | * `address.schema.json` 21 | * `calendar.schema.json` 22 | * `card.schema.json` 23 | * `geo.schema.json` 24 | -------------------------------------------------------------------------------- /schemata/address.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "description": "An Address following the convention of http://microformats.org/wiki/hcard", 4 | "type": "object", 5 | "properties": { 6 | "post-office-box": { "type": "string" }, 7 | "extended-address": { "type": "string" }, 8 | "street-address": { "type": "string" }, 9 | "locality":{ "type": "string" }, 10 | "region": { "type": "string" }, 11 | "postal-code": { "type": "string" }, 12 | "country-name": { "type": "string"} 13 | }, 14 | "required": ["locality", "region", "country-name"], 15 | "dependencies": { 16 | "post-office-box": ["street-address"], 17 | "extended-address": ["street-address"] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /schemata/calendar.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "description": "A representation of an event", 4 | "type": "object", 5 | "required": [ "dtstart", "summary" ], 6 | "properties": { 7 | "dtstart": { 8 | "format": "date-time", 9 | "type": "string", 10 | "description": "Event starting time" 11 | }, 12 | "dtend": { 13 | "format": "date-time", 14 | "type": "string", 15 | "description": "Event ending time" 16 | }, 17 | "summary": { "type": "string" }, 18 | "location": { "type": "string" }, 19 | "url": { "type": "string", "format": "uri" }, 20 | "duration": { 21 | "format": "time", 22 | "type": "string", 23 | "description": "Event duration" 24 | }, 25 | "rdate": { 26 | "format": "date-time", 27 | "type": "string", 28 | "description": "Recurrence date" 29 | }, 30 | "rrule": { 31 | "type": "string", 32 | "description": "Recurrence rule" 33 | }, 34 | "category": { "type": "string" }, 35 | "description": { "type": "string" }, 36 | "geo": { "$ref": "http: //json-schema.org/geo" } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /schemata/card.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "description": "A representation of a person, company, organization, or place", 4 | "type": "object", 5 | "required": ["familyName", "givenName"], 6 | "properties": { 7 | "fn": { 8 | "description": "Formatted Name", 9 | "type": "string" 10 | }, 11 | "familyName": { "type": "string" }, 12 | "givenName": { "type": "string" }, 13 | "additionalName": { "type": "array", "items": { "type": "string" } }, 14 | "honorificPrefix": { "type": "array", "items": { "type": "string" } }, 15 | "honorificSuffix": { "type": "array", "items": { "type": "string" } }, 16 | "nickname": { "type": "string" }, 17 | "url": { "type": "string", "format": "uri" }, 18 | "email": { 19 | "type": "object", 20 | "properties": { 21 | "type": { "type": "string" }, 22 | "value": { "type": "string", "format": "email" } 23 | } 24 | }, 25 | "tel": { 26 | "type": "object", 27 | "properties": { 28 | "type": { "type": "string" }, 29 | "value": { "type": "string", "format": "phone" } 30 | } 31 | }, 32 | "adr": { "$ref": "http://json-schema.org/address" }, 33 | "geo": { "$ref": "http://json-schema.org/geo" }, 34 | "tz": { "type": "string" }, 35 | "photo": { "type": "string" }, 36 | "logo": { "type": "string" }, 37 | "sound": { "type": "string" }, 38 | "bday": { "type": "string", "format": "date" }, 39 | "title": { "type": "string" }, 40 | "role": { "type": "string" }, 41 | "org": { 42 | "type": "object", 43 | "properties": { 44 | "organizationName": { "type": "string" }, 45 | "organizationUnit": { "type": "string" } 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /schemata/geo.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "description": "A geographical coordinate", 4 | "type": "object", 5 | "properties": { 6 | "latitude": { "type": "number" }, 7 | "longitude": { "type": "number" } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /schemata/hyper-schema.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/hyper-schema#", 3 | "id": "http://json-schema.org/draft-04/hyper-schema#", 4 | "title": "JSON Hyper-Schema", 5 | "allOf": [ 6 | { 7 | "$ref": "http://json-schema.org/draft-04/schema#" 8 | } 9 | ], 10 | "properties": { 11 | "additionalItems": { 12 | "anyOf": [ 13 | { 14 | "type": "boolean" 15 | }, 16 | { 17 | "$ref": "#" 18 | } 19 | ] 20 | }, 21 | "additionalProperties": { 22 | "anyOf": [ 23 | { 24 | "type": "boolean" 25 | }, 26 | { 27 | "$ref": "#" 28 | } 29 | ] 30 | }, 31 | "dependencies": { 32 | "additionalProperties": { 33 | "anyOf": [ 34 | { 35 | "$ref": "#" 36 | }, 37 | { 38 | "type": "array" 39 | } 40 | ] 41 | } 42 | }, 43 | "items": { 44 | "anyOf": [ 45 | { 46 | "$ref": "#" 47 | }, 48 | { 49 | "$ref": "#/definitions/schemaArray" 50 | } 51 | ] 52 | }, 53 | "definitions": { 54 | "additionalProperties": { 55 | "$ref": "#" 56 | } 57 | }, 58 | "patternProperties": { 59 | "additionalProperties": { 60 | "$ref": "#" 61 | } 62 | }, 63 | "properties": { 64 | "additionalProperties": { 65 | "$ref": "#" 66 | } 67 | }, 68 | "allOf": { 69 | "$ref": "#/definitions/schemaArray" 70 | }, 71 | "anyOf": { 72 | "$ref": "#/definitions/schemaArray" 73 | }, 74 | "oneOf": { 75 | "$ref": "#/definitions/schemaArray" 76 | }, 77 | "not": { 78 | "$ref": "#" 79 | }, 80 | 81 | "links": { 82 | "type": "array", 83 | "items": { 84 | "$ref": "#/definitions/linkDescription" 85 | } 86 | }, 87 | "fragmentResolution": { 88 | "type": "string" 89 | }, 90 | "media": { 91 | "type": "object", 92 | "properties": { 93 | "type": { 94 | "description": "A media type, as described in RFC 2046", 95 | "type": "string" 96 | }, 97 | "binaryEncoding": { 98 | "description": "A content encoding scheme, as described in RFC 2045", 99 | "type": "string" 100 | } 101 | } 102 | }, 103 | "pathStart": { 104 | "description": "Instances' URIs must start with this value for this schema to apply to them", 105 | "type": "string", 106 | "format": "uri" 107 | } 108 | }, 109 | "definitions": { 110 | "schemaArray": { 111 | "type": "array", 112 | "items": { 113 | "$ref": "#" 114 | } 115 | }, 116 | "linkDescription": { 117 | "title": "Link Description Object", 118 | "type": "object", 119 | "required": [ "href", "rel" ], 120 | "properties": { 121 | "href": { 122 | "description": "a URI template, as defined by RFC 6570, with the addition of the $, ( and ) characters for pre-processing", 123 | "type": "string" 124 | }, 125 | "rel": { 126 | "description": "relation to the target resource of the link", 127 | "type": "string" 128 | }, 129 | "title": { 130 | "description": "a title for the link", 131 | "type": "string" 132 | }, 133 | "targetSchema": { 134 | "description": "JSON Schema describing the link target", 135 | "$ref": "#" 136 | }, 137 | "mediaType": { 138 | "description": "media type (as defined by RFC 2046) describing the link target", 139 | "type": "string" 140 | }, 141 | "method": { 142 | "description": "method for requesting the target of the link (e.g. for HTTP this might be \"GET\" or \"DELETE\")", 143 | "type": "string" 144 | }, 145 | "encType": { 146 | "description": "The media type in which to submit data along with the request", 147 | "type": "string", 148 | "default": "application/json" 149 | }, 150 | "schema": { 151 | "description": "Schema describing the data to submit along with the request", 152 | "$ref": "#" 153 | } 154 | } 155 | } 156 | }, 157 | "links": [ 158 | { 159 | "rel": "self", 160 | "href": "{+id}" 161 | }, 162 | { 163 | "rel": "full", 164 | "href": "{+($ref)}" 165 | } 166 | ] 167 | } 168 | 169 | -------------------------------------------------------------------------------- /schemata/schema.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "http://json-schema.org/draft-04/schema#", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "description": "Core schema meta-schema", 5 | "definitions": { 6 | "schemaArray": { 7 | "type": "array", 8 | "minItems": 1, 9 | "items": { "$ref": "#" } 10 | }, 11 | "positiveInteger": { 12 | "type": "integer", 13 | "minimum": 0 14 | }, 15 | "positiveIntegerDefault0": { 16 | "allOf": [ { "$ref": "#/definitions/positiveInteger" }, { "default": 0 } ] 17 | }, 18 | "simpleTypes": { 19 | "enum": [ "array", "boolean", "integer", "null", "number", "object", "string" ] 20 | }, 21 | "stringArray": { 22 | "type": "array", 23 | "items": { "type": "string" }, 24 | "minItems": 1, 25 | "uniqueItems": true 26 | } 27 | }, 28 | "type": "object", 29 | "properties": { 30 | "id": { 31 | "type": "string", 32 | "format": "uri" 33 | }, 34 | "$schema": { 35 | "type": "string", 36 | "format": "uri" 37 | }, 38 | "title": { 39 | "type": "string" 40 | }, 41 | "description": { 42 | "type": "string" 43 | }, 44 | "default": {}, 45 | "multipleOf": { 46 | "type": "number", 47 | "minimum": 0, 48 | "exclusiveMinimum": true 49 | }, 50 | "maximum": { 51 | "type": "number" 52 | }, 53 | "exclusiveMaximum": { 54 | "type": "boolean", 55 | "default": false 56 | }, 57 | "minimum": { 58 | "type": "number" 59 | }, 60 | "exclusiveMinimum": { 61 | "type": "boolean", 62 | "default": false 63 | }, 64 | "maxLength": { "$ref": "#/definitions/positiveInteger" }, 65 | "minLength": { "$ref": "#/definitions/positiveIntegerDefault0" }, 66 | "pattern": { 67 | "type": "string", 68 | "format": "regex" 69 | }, 70 | "additionalItems": { 71 | "anyOf": [ 72 | { "type": "boolean" }, 73 | { "$ref": "#" } 74 | ], 75 | "default": {} 76 | }, 77 | "items": { 78 | "anyOf": [ 79 | { "$ref": "#" }, 80 | { "$ref": "#/definitions/schemaArray" } 81 | ], 82 | "default": {} 83 | }, 84 | "maxItems": { "$ref": "#/definitions/positiveInteger" }, 85 | "minItems": { "$ref": "#/definitions/positiveIntegerDefault0" }, 86 | "uniqueItems": { 87 | "type": "boolean", 88 | "default": false 89 | }, 90 | "maxProperties": { "$ref": "#/definitions/positiveInteger" }, 91 | "minProperties": { "$ref": "#/definitions/positiveIntegerDefault0" }, 92 | "required": { "$ref": "#/definitions/stringArray" }, 93 | "additionalProperties": { 94 | "anyOf": [ 95 | { "type": "boolean" }, 96 | { "$ref": "#" } 97 | ], 98 | "default": {} 99 | }, 100 | "definitions": { 101 | "type": "object", 102 | "additionalProperties": { "$ref": "#" }, 103 | "default": {} 104 | }, 105 | "properties": { 106 | "type": "object", 107 | "additionalProperties": { "$ref": "#" }, 108 | "default": {} 109 | }, 110 | "patternProperties": { 111 | "type": "object", 112 | "additionalProperties": { "$ref": "#" }, 113 | "default": {} 114 | }, 115 | "dependencies": { 116 | "type": "object", 117 | "additionalProperties": { 118 | "anyOf": [ 119 | { "$ref": "#" }, 120 | { "$ref": "#/definitions/stringArray" } 121 | ] 122 | } 123 | }, 124 | "enum": { 125 | "type": "array", 126 | "minItems": 1, 127 | "uniqueItems": true 128 | }, 129 | "type": { 130 | "anyOf": [ 131 | { "$ref": "#/definitions/simpleTypes" }, 132 | { 133 | "type": "array", 134 | "items": { "$ref": "#/definitions/simpleTypes" }, 135 | "minItems": 1, 136 | "uniqueItems": true 137 | } 138 | ] 139 | }, 140 | "allOf": { "$ref": "#/definitions/schemaArray" }, 141 | "anyOf": { "$ref": "#/definitions/schemaArray" }, 142 | "oneOf": { "$ref": "#/definitions/schemaArray" }, 143 | "not": { "$ref": "#" } 144 | }, 145 | "dependencies": { 146 | "exclusiveMaximum": [ "maximum" ], 147 | "exclusiveMinimum": [ "minimum" ] 148 | }, 149 | "default": {} 150 | } 151 | -------------------------------------------------------------------------------- /tests/array.test: -------------------------------------------------------------------------------- 1 | ######################################################################### 2 | # Generic array operations 3 | # 4 | # Tests are groups of three lines: program, input, expected output. 5 | # Blank lines and lines starting with # are ignored. 6 | ######################################################################### 7 | 8 | index(3) 9 | [1,2,3,4,5] 10 | 2 11 | 12 | reverse 13 | [1,2,3,4,5] 14 | [5,4,3,2,1] 15 | 16 | include "array"; sorted 17 | [1,9,3,4,5] 18 | false 19 | 20 | include "array"; sorted 21 | [3] 22 | true 23 | 24 | include "array"; sorted 25 | [] 26 | true 27 | 28 | include "array"; sorted 29 | [1,2,3,4,5] 30 | true 31 | 32 | include "array"; unknown(2) 33 | [1] 34 | false 35 | 36 | include "array"; false==has(2) 37 | [1] 38 | true 39 | 40 | include "array"; unknown(2) 41 | [1,3,null,4] 42 | true 43 | 44 | include "array"; false==has(2) 45 | [1,3,null,4] 46 | false 47 | 48 | include "array"; remove(2) 49 | [5,4,3] 50 | [5,4,3] 51 | 52 | include "array"; remove(2) 53 | [5,2,4,3,2,2] 54 | [5,4,3] 55 | 56 | include "array"; evens 57 | [0,1,2,3,4,5,6,7,8,9] 58 | [0,2,4,6,8] 59 | 60 | include "array"; odds 61 | [0,1,2,3,4,5,6,7,8,9] 62 | [1,3,5,7,9] 63 | 64 | # 65 | # zip 66 | # 67 | 68 | include "array"; zip([range(1)]; [range(0)]) 69 | null 70 | [0,null] 71 | 72 | include "array"; zip([range(0)]; [range(0)]) 73 | null 74 | # empty 75 | 76 | include "array"; [] | zip 77 | null 78 | # empty 79 | 80 | include "array"; first(zip([range(10)]; [range(20;30)])) 81 | null 82 | [0,20] 83 | 84 | include "array"; last(zip([range(5)]; [range(20;23)])) 85 | null 86 | [4,null] 87 | 88 | include "array"; last([[range(5)], [range(20;23)]] | zip) 89 | null 90 | [4,null] 91 | 92 | include "array"; nth(5; [[range(5)], [range(100)], [range(20;23)]] | zip) 93 | null 94 | [null,5,null] 95 | 96 | # vim:ai:sw=2:ts=2:et:syntax=jq 97 | -------------------------------------------------------------------------------- /tests/array_choice.test: -------------------------------------------------------------------------------- 1 | 2 | include "array/choice"; shuffle(222) != [] 3 | [1,2,3] 4 | true 5 | -------------------------------------------------------------------------------- /tests/array_kleene.test: -------------------------------------------------------------------------------- 1 | ######################################################################### 2 | # Kleene closures 3 | # 4 | # Tests are groups of three lines: program, input, expected output. 5 | # Blank lines and lines starting with # are ignored. 6 | ######################################################################### 7 | 8 | # 9 | # product 10 | # 11 | 12 | # × 13 | include "array/kleene"; [product]|length 14 | [] 15 | 1 16 | 17 | # A × 18 | include "array/kleene"; product 19 | [[1,2,3]] 20 | [1] 21 | [2] 22 | [3] 23 | 24 | # A × ∅ 25 | include "array/kleene"; product 26 | [[1,2,3],[]] 27 | # empty 28 | 29 | # A × ∅ 30 | include "array/kleene"; product 31 | [[],[1,2,3]] 32 | # empty 33 | 34 | # A × B 35 | include "array/kleene"; [product] 36 | [[1,2,3],[4]] 37 | [[1,4],[2,4],[3,4]] 38 | 39 | # A⁵ 40 | include "array/kleene"; [product] | length == (5*5*5*5*5) 41 | [[1,2,3,4,5], [1,2,3,4,5], [1,2,3,4,5], [1,2,3,4,5], [1,2,3,4,5]] 42 | true 43 | 44 | # A × B 45 | include "array/kleene"; [[[1,2,3],[4]]|product] 46 | null 47 | [[1,4],[2,4],[3,4]] 48 | 49 | # A × B × C 50 | include "array/kleene"; [[[1,2,3],[4],[5,6]]|product] 51 | null 52 | [[1,4,5],[1,4,6],[2,4,5],[2,4,6],[3,4,5],[3,4,6]] 53 | 54 | # 55 | # power 56 | # 57 | 58 | include "array/kleene"; power(0) 59 | [1,2,3] 60 | [] 61 | 62 | include "array/kleene"; [power(1)] 63 | [1,2,3] 64 | [[1],[2],[3]] 65 | 66 | include "array/kleene"; [power(2)] 67 | [1,2,3] 68 | [[1,1],[1,2],[1,3],[2,1],[2,2],[2,3],[3,1],[3,2],[3,3]] 69 | 70 | # A⁵ 71 | include "array/kleene"; [power(5)] | length == (5*5*5*5*5) 72 | [[1,2,3,4,5], [1,2,3,4,5], [1,2,3,4,5], [1,2,3,4,5], [1,2,3,4,5]] 73 | true 74 | 75 | # 76 | # closures 77 | # 78 | 79 | include "array/kleene"; [limit(13;star)] 80 | [1,2,3] 81 | [[],[1],[2],[3],[1,1],[1,2],[1,3],[2,1],[2,2],[2,3],[3,1],[3,2],[3,3]] 82 | 83 | include "array/kleene"; [[limit(13;star)],[[],limit(12;plus)]] | .[0]==.[1] 84 | [1,2,3] 85 | true 86 | 87 | include "array/kleene"; [[limit(13;star)][1:],[limit(12;plus)]] | .[0]==.[1] 88 | [1,2,3] 89 | true 90 | 91 | include "array/kleene"; [limit(12;plus)] 92 | [1,2,3] 93 | [[1],[2],[3],[1,1],[1,2],[1,3],[2,1],[2,2],[2,3],[3,1],[3,2],[3,3]] 94 | 95 | include "array/kleene"; include "array/kleene"; [power(2)]==[(limit(20;star)|select(length==2))] 96 | [1,2,3] 97 | true 98 | 99 | # vim:ai:sw=2:ts=2:et:syntax=jq 100 | -------------------------------------------------------------------------------- /tests/array_set.test: -------------------------------------------------------------------------------- 1 | ######################################################################### 2 | # Set array operations 3 | # 4 | # Tests are groups of three lines: program, input, expected output. 5 | # Blank lines and lines starting with # are ignored. 6 | ######################################################################### 7 | 8 | include "array/set"; unlike 9 | [1,2,3,4,5] 10 | true 11 | 12 | include "array/set"; index(3) 13 | [2,4,5,6,7,3,2] 14 | 5 15 | 16 | include "array/set"; .[[3]] 17 | [2,4,5,6,7,3,2] 18 | [5] 19 | 20 | include "array/set"; .[[2]] 21 | [2,4,5,6,7,3,2] 22 | [0,6] 23 | 24 | include "array/set"; insert(2) | insert(1) 25 | [5,4,3] 26 | [5,4,3,2,1] 27 | 28 | include "array/set"; remove(2) 29 | [5,2,4,3,2,2] 30 | [5,4,3,2,2] 31 | 32 | include "array/set"; remove(2) 33 | [5,4,3] 34 | [5,4,3] 35 | 36 | include "array/set"; remove(2) 37 | [5,2,4,3] 38 | [5,4,3] 39 | 40 | include "array"; index(2) 41 | [5,2,4,3] 42 | 1 43 | 44 | include "array/set"; 3 | element([4,2,5,3,2,1]) 45 | null 46 | true 47 | 48 | include "array/set"; [4,2,5,3,2,1] | member(2) 49 | null 50 | true 51 | 52 | include "array/set"; equal([5,4,3,2,1]) 53 | [1,2,3,4,5] 54 | true 55 | 56 | include "array/set"; union([1,2,3,4,5,6,7,8,9,0]) 57 | [1,2,3,4,5] 58 | [1,2,3,4,5,6,7,8,9,0] 59 | 60 | include "array/set"; intersection([1,2,3,4,5,6,7,8,9,0]) 61 | [1,2,3,4,5] 62 | [1,2,3,4,5] 63 | 64 | include "array/set"; . - [6,7,8,9,0] 65 | [1,2,3,4,5,6,7,8,9] 66 | [1,2,3,4,5] 67 | 68 | include "array/set"; . - [6,7,8,9,0] 69 | [1,2,3,4,5,6,7,8,9] 70 | [1,2,3,4,5] 71 | 72 | ######################################################################## 73 | # Subsets 74 | 75 | include "array/set"; [combinations(2)]==[minus1] 76 | [1,2,3] 77 | true 78 | 79 | include "array/set"; [combinations(9)]==[minus1] 80 | [1,2,3,4,5,6,7,8,9,10] 81 | true 82 | 83 | include "array/set"; [combinations(2)] 84 | [1,2,3] 85 | [[1,2],[1,3],[2,3]] 86 | 87 | include "array/set"; [combinations(2)]==[(limit(10;powerset)|select(length==2))] 88 | [1,2,3] 89 | true 90 | 91 | #include "array/set"; [(limit(10;powerset)|select(length==2))] == ([(limit(10;powerset_u)|select(length==2))]) 92 | #[1,2,3] 93 | #true 94 | 95 | ######################################################################## 96 | # Multi-sets 97 | 98 | include "array/set"; [mulsets(2)] 99 | [1,2,3] 100 | [[1,1],[1,2],[1,3],[2,2],[2,3],[3,3]] 101 | 102 | include "array/set"; [mulsets(2)]==[(limit(10;mulsets)|select(length==2))] 103 | [1,2,3] 104 | true 105 | 106 | 107 | # vim:ai:sw=2:ts=2:et:syntax=jq 108 | -------------------------------------------------------------------------------- /tests/array_tuple.test: -------------------------------------------------------------------------------- 1 | ######################################################################### 2 | # Tuples 3 | # 4 | # Tests are groups of three lines: program, input, expected output. 5 | # Blank lines and lines starting with # are ignored. 6 | ######################################################################### 7 | 8 | include "array/tuple"; [permutations(2)] 9 | [] 10 | [] 11 | 12 | include "array/tuple"; [permutations(2)] 13 | [1] 14 | [] 15 | 16 | include "array/tuple"; [permutations(1)] 17 | [1] 18 | [[1]] 19 | 20 | include "array/tuple"; [permutations(1)] 21 | [1,2] 22 | [[1],[2]] 23 | 24 | include "array/tuple"; [permutations(2)] 25 | [1,2] 26 | [[1,2],[2,1]] 27 | 28 | include "array/tuple"; [permutations(2)] 29 | [1,2,3] 30 | [[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]] 31 | 32 | include "array/tuple"; [permutations] 33 | [1,2,3] 34 | [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] 35 | 36 | include "array/tuple"; [derangements] 37 | [1,2,3] 38 | [[2,3,1],[3,1,2]] 39 | 40 | include "array/tuple"; [cycles] 41 | [1,2,3] 42 | [[1,2,3],[1,3,2]] 43 | 44 | #include "array/tuple"; [arrangement] 45 | #[1,1,2] 46 | #[[1,1,2],[1,2,1],[2,1,1]] 47 | 48 | #include "array/tuple"; [disposition] 49 | #[1,1,2] 50 | #[[],[1],[2],[1,1],[1,2],[1,1,2]] 51 | 52 | # vim:ai:sw=2:ts=2:et:syntax=jq 53 | -------------------------------------------------------------------------------- /tests/json.test: -------------------------------------------------------------------------------- 1 | ######################################################################### 2 | # JSON 3 | # 4 | # Tests are groups of three lines: program, input, expected output. 5 | # Blank lines and lines starting with # are ignored. 6 | ######################################################################### 7 | 8 | include "json"; isid 9 | "Legal" 10 | true 11 | 12 | include "json"; isid 13 | "_Legal_" 14 | true 15 | 16 | include "json"; isid 17 | "_Legal123" 18 | true 19 | 20 | include "json"; isid 21 | "1Legal" 22 | false 23 | 24 | include "json"; isid 25 | " Legal" 26 | false 27 | 28 | include "json"; xname 29 | "Legal:name" 30 | true 31 | 32 | include "json"; xname 33 | " NoLegal" 34 | false 35 | 36 | include "json"; xname 37 | "$NoLegal" 38 | false 39 | 40 | include "json"; xtoken 41 | "·Legal" 42 | true 43 | 44 | # vim:ai:sw=2:ts=2:et:syntax=jq 45 | -------------------------------------------------------------------------------- /tests/math.test: -------------------------------------------------------------------------------- 1 | ######################################################################### 2 | # Math utilities 3 | # 4 | # Tests are groups of three lines: program, input, expected output. 5 | # Blank lines and lines starting with # are ignored. 6 | ######################################################################### 7 | 8 | include "math"; even(.) 9 | 2 10 | true 11 | 12 | include "math"; odd(.) 13 | 2 14 | false 15 | 16 | include "math"; even(.) 17 | 3 18 | false 19 | 20 | include "math"; odd(.) 21 | 3 22 | true 23 | 24 | include "math"; tobase(16) 25 | 32 26 | "20" 27 | 28 | include "math"; tobase(2) 29 | 128 30 | "10000000" 31 | 32 | include "math"; tobase(16) 33 | 255 34 | "FF" 35 | 36 | include "math"; sign(nan) | isnan 37 | null 38 | true 39 | 40 | include "math"; sign(.) 41 | -3 42 | -1 43 | 44 | include "math"; sign(.) 45 | 3 46 | 1 47 | 48 | include "math"; sign(.) 49 | 0 50 | 0 51 | 52 | include "math"; gcd(.[0];.[1]) 53 | [24,60] 54 | 12 55 | 56 | include "math"; gcd(0;0) 57 | null 58 | 0 59 | 60 | include "math"; gcd(-12;6) 61 | null 62 | 6 63 | 64 | include "math"; fabs 65 | -3 66 | 3 67 | 68 | include "math"; fabs 69 | -3.3 70 | 3.3 71 | 72 | include "math"; mod(12;6) 73 | null 74 | 0 75 | 76 | include "math"; mod(12;5) 77 | null 78 | 2 79 | 80 | include "math"; mod(12;11) 81 | null 82 | 1 83 | 84 | include "math"; div(7;2) 85 | null 86 | 3 87 | 88 | # reductions ########################################################### 89 | 90 | include "math"; sum(range(.)) == 0 91 | 0 92 | true 93 | 94 | include "math"; [range(.)]|add == sum(range(10)) 95 | 10 96 | true 97 | 98 | include "math"; mul(range(.)) == 1 99 | 0 100 | true 101 | 102 | include "math"; mul(range(.m;.n)) == (2*3*4*5*6*7*8*9) 103 | {"m":2,"n":10} 104 | true 105 | 106 | include "math"; min(empty) == infinite 107 | null 108 | true 109 | 110 | include "math"; max(empty) == 0-infinite 111 | null 112 | true 113 | 114 | include "math"; max(range(.m;.n)) == 9 115 | {"m":2,"n":10} 116 | true 117 | 118 | include "math"; max(-1,-2,-3) == -1 119 | null 120 | true 121 | 122 | include "math"; max(0-infinite, 9) == 9 123 | null 124 | true 125 | 126 | include "math"; count(empty) == 0 127 | null 128 | true 129 | 130 | include "math"; count(range(3)) == 3 131 | null 132 | true 133 | 134 | include "math"; count(empty) == 0 135 | null 136 | true 137 | 138 | include "math"; count(range(3;10)) == 9-3+1 139 | null 140 | true 141 | 142 | include "math"; count(range(8), range(4;11)) == count(range(8)) + count(range(4;11)) 143 | null 144 | true 145 | 146 | # reverse implemented using reduce 147 | def rev: reduce .[] as $x ([]; [$x] + .); (.|rev) == (.|reverse) 148 | [1,2,3,4,5] 149 | true 150 | 151 | # length implemented using reduce 152 | def len: reduce .[] as $x (0; . + 1); (.|len) == (.|length) 153 | [1,2,3,4,5] 154 | true 155 | 156 | # vim:ai:sw=2:ts=2:et:syntax=jq 157 | -------------------------------------------------------------------------------- /tests/math_bitwise.test: -------------------------------------------------------------------------------- 1 | ######################################################################### 2 | # Bitwise operations 3 | # 4 | # Tests are groups of three lines: program, input, expected output. 5 | # Blank lines and lines starting with # are ignored. 6 | ######################################################################### 7 | 8 | ######################################################################## 9 | 10 | include "math/bitwise"; (53|exp2-1) as $universe | [range(53)|exp2]|add == $universe 11 | null 12 | true 13 | 14 | include "math/bitwise"; (53|exp2-1) as $universe | [range(53)] == [positions($universe)] 15 | null 16 | true 17 | 18 | include "math/bitwise"; mask(2) 19 | null 20 | 3 21 | 22 | include "math/bitwise"; getbit(0;7) + getbit(1;7) + getbit(2;7) + getbit(3;7) + getbit(11;7) 23 | null 24 | 3 25 | 26 | include "math/bitwise"; setbit(3;0) 27 | null 28 | 8 29 | 30 | include "math/bitwise"; setbit(3;clrbit(3;-1)) 31 | null 32 | -1 33 | 34 | include "math/bitwise"; clrbit(1;7) 35 | null 36 | 5 37 | 38 | include "math/bitwise"; setbit(1;clrbit(1;-1)) 39 | null 40 | -1 41 | 42 | include "math/bitwise"; tglbit(1;7) 43 | null 44 | 5 45 | 46 | include "math/bitwise"; tglbit(1;5) 47 | null 48 | 7 49 | 50 | ######################################################################## 51 | # Common LISP imported functions 52 | ######################################################################## 53 | 54 | include "math/bitwise"; [range(53)|lognot(lognot(.))] == [range(53)] 55 | null 56 | true 57 | 58 | include "math/bitwise"; [logand(0;0),logand(0;1),logand(1;0),logand(1;1)] 59 | null 60 | [0,0,0,1] 61 | 62 | include "math/bitwise"; [logior(0;0),logior(0;1),logior(1;0),logior(1;1)] 63 | null 64 | [0,1,1,1] 65 | 66 | include "math/bitwise"; [logxor(0;0),logxor(0;1),logxor(1;0),logxor(1;1)] 67 | null 68 | [0,1,1,0] 69 | 70 | include "math/bitwise"; [getbit(0;logeqv(0;0),logeqv(0;1),logeqv(1;0),logeqv(1;1))] 71 | null 72 | [1,0,0,1] 73 | 74 | include "math/bitwise"; [getbit(0;lognand(0;0),lognand(0;1),lognand(1;0),lognand(1;1))] 75 | null 76 | [1,1,1,0] 77 | 78 | include "math/bitwise"; [getbit(0;lognor(0;0),lognor(0;1),lognor(1;0),lognor(1;1))] 79 | null 80 | [1,0,0,0] 81 | 82 | include "math/bitwise"; [getbit(0;logandc1(0;0),logandc1(0;1),logandc1(1;0),logandc1(1;1))] 83 | null 84 | [0,1,0,0] 85 | 86 | include "math/bitwise"; [getbit(0;logandc2(0;0),logandc2(0;1),logandc2(1;0),logandc2(1;1))] 87 | null 88 | [0,0,1,0] 89 | 90 | include "math/bitwise"; [getbit(0;logorc1(0;0),logorc1(0;1),logorc1(1;0),logorc1(1;1))] 91 | null 92 | [1,1,0,1] 93 | 94 | include "math/bitwise"; [getbit(0;logorc2(0;0),logorc2(0;1),logorc2(1;0),logorc2(1;1))] 95 | null 96 | [1,0,1,1] 97 | 98 | include "math/bitwise"; logtest(1;mask(12)) 99 | null 100 | true 101 | 102 | include "math/bitwise"; [range(53)|logbitp(.;lognot(1+2+4+8+16+32))] == [range(53)|logbitp(.;1+2+4+8+16+32)|not] 103 | null 104 | true 105 | 106 | include "math/bitwise"; ash(1;3) 107 | null 108 | 6 109 | 110 | include "math/bitwise"; ash(-1;6) 111 | null 112 | 3 113 | 114 | include "math/bitwise"; [[0,1,3,4,7,-1,-4,-7,-8][]|integer_length(.)] == [0,1,2,3,3,0,2,3,3] 115 | null 116 | true 117 | 118 | include "math/bitwise"; integer_length(mask(.)) == . 119 | 53 120 | true 121 | 122 | include "math/bitwise"; logcount(mask(.)) == . 123 | 3 124 | true 125 | 126 | include "math/bitwise"; logcount(mask(.)) == . 127 | 50 128 | true 129 | 130 | # byte... 131 | 132 | include "math/bitwise"; byte_size(byte(10;20)) 133 | null 134 | 10 135 | 136 | include "math/bitwise"; byte_position(byte(10;20)) 137 | null 138 | 20 139 | 140 | include "math/bitwise"; deposit_field(7; byte(2;1); 0e0) 141 | null 142 | 6 143 | 144 | include "math/bitwise"; deposit_field(mask(53); byte(4;0); 0e0) 145 | null 146 | 15 147 | 148 | 149 | include "math/bitwise"; ldb(byte(4;4); mask(10)) 150 | null 151 | 15 152 | 153 | include "math/bitwise"; ldb(byte(4;4); mask(10)) 154 | null 155 | 15 156 | 157 | include "math/bitwise"; ldb_test(byte(4;4); mask(8)) 158 | null 159 | true 160 | 161 | include "math/bitwise"; mask_field(byte(1;5); mask(53)) 162 | null 163 | 32 164 | 165 | include "math/bitwise"; mask_field(byte(4;4); mask(8)) 166 | null 167 | 240 168 | 169 | include "math/bitwise"; dpb(15; byte(4;4); 128) 170 | null 171 | 240 172 | 173 | include "math/bitwise"; dpb(1; byte(1;10); 0e0) 174 | null 175 | 1024 176 | 177 | include "math/bitwise"; dpb(mask(53)-1; byte(2;10); 0e0) 178 | null 179 | 2048 180 | 181 | include "math/bitwise"; dpb(1; byte(2;10); 2048) 182 | null 183 | 1024 184 | 185 | # vim:ai:sw=2:ts=2:et:syntax=jq 186 | -------------------------------------------------------------------------------- /tests/math_chance.test: -------------------------------------------------------------------------------- 1 | ######################################################################### 2 | # Chance 3 | # 4 | # Tests are groups of three lines: program, input, expected output. 5 | # Blank lines and lines starting with # are ignored. 6 | ######################################################################### 7 | 8 | include "math/chance"; first(rnd(randomize)) | 0 < . and . < 1 9 | null 10 | true 11 | 12 | include "math/chance"; first(rand(randomize)) | 0 < . and . < pow(2; 15) 13 | null 14 | true 15 | 16 | include "math/chance"; . as $n | first(random($n; randomize)) | 0 <= . and . < $n 17 | 10 18 | true 19 | 20 | # vim:ai:sw=2:ts=2:et:syntax=jq 21 | -------------------------------------------------------------------------------- /tests/math_sequence.test: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # a sequence is an enumerated collection of objects... 3 | # 4 | # Tests are groups of three lines: program, input, expected output. 5 | # Blank lines and lines starting with # are ignored. 6 | ######################################################################## 7 | 8 | # N, Z+, Z- ############################################################# 9 | 10 | include "math/sequence"; limit(1; naturals) 11 | null 12 | 0 13 | 14 | include "math/sequence"; [limit(3; naturals)] 15 | null 16 | [0,1,2] 17 | 18 | include "math/sequence"; [limit(3; odds)] 19 | null 20 | [1,3,5] 21 | 22 | include "math/sequence"; [limit(3; evens)] 23 | null 24 | [0,2,4] 25 | 26 | include "math/sequence"; [limit(10; odds)] == [limit(10; naturals|select(.%2!=0))] 27 | null 28 | true 29 | 30 | include "math/sequence"; [limit(10; evens)] == [limit(10; naturals|select(.%2==0))] 31 | null 32 | true 33 | 34 | include "math/sequence"; [limit(10; positives)] == [limit(10; range(1;11))] 35 | null 36 | true 37 | 38 | include "math/sequence"; [limit(10; negatives)] == [limit(10; range(-1;-33;-1))] 39 | null 40 | true 41 | 42 | # 43 | # seq 44 | # 45 | 46 | include "math/sequence"; [limit(6; range(7; infinite))] 47 | null 48 | [7,8,9,10,11,12] 49 | 50 | include "math/sequence"; [limit(6; arithmetic(7; 2))] 51 | null 52 | [7,9,11,13,15,17] 53 | 54 | include "math/sequence"; [limit(6; arithmetic(0; -1))] 55 | null 56 | [0,-1,-2,-3,-4,-5] 57 | 58 | ######################################################################### 59 | # 60 | ######################################################################### 61 | 62 | # Products of `n` 63 | include "math/sequence"; [limit(11; multiples)] 64 | 7 65 | [0,7,14,21,28,35,42,49,56,63,70] 66 | 67 | # Powers of `n` 68 | include "math/sequence"; limit(1; powers) 69 | 0 70 | 1 71 | 72 | include "math/sequence"; limit(1; powers) 73 | 2 74 | 1 75 | 76 | include "math/sequence"; [limit(2; powers)] 77 | 2 78 | [1,2] 79 | 80 | include "math/sequence"; [limit(3; powers)] 81 | 2 82 | [1,2,4] 83 | 84 | include "math/sequence"; [limit(10; powers)] 85 | 2 86 | [1,2,4,8,16,32,64,128,256,512] 87 | 88 | include "math/sequence"; nth(10; powers(7)) == pow(7; 10) 89 | null 90 | true 91 | 92 | include "math/sequence"; [limit(.; powers)] 93 | 7 94 | [1,7,49,343,2401,16807,117649] 95 | 96 | include "math/sequence"; [limit(11; powers(2))]==[limit(11; powers2)] 97 | null 98 | true 99 | 100 | # Factorial 101 | include "math/sequence"; [limit(6; factorials)] 102 | null 103 | [1,1,2,6,24,120] 104 | 105 | include "math/sequence"; [limit(13; triangulars)] 106 | null 107 | [0,1,3,6,10,15,21,28,36,45,55,66,78] 108 | 109 | # Fibonacci 110 | include "math/sequence"; [limit(21; fibonacci)] 111 | null 112 | [0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765] 113 | 114 | include "math/sequence"; nth(70; fibonacci) 115 | null 116 | 190392490709135 117 | 118 | include "math/sequence"; include "stream"; limit(2; drop(70; fibonacci)) 119 | null 120 | 190392490709135 121 | 308061521170129 122 | 123 | ## Golden ratio 124 | include "math/sequence"; include "stream"; [limit(2; drop(70; fibonacci))] | (.[1]/.[0])*100000|floor|./100000 125 | null 126 | 1.61803 127 | 128 | # squares, cubes 129 | include "math/sequence"; [limit(6; squares)] 130 | null 131 | [0,1,4,9,16,25] 132 | 133 | include "math/sequence"; [limit(6; cubes)] 134 | null 135 | [0,1,8,27,64,125] 136 | 137 | # 138 | include "math/sequence"; [limit(20; primes)] 139 | null 140 | [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71] 141 | 142 | include "math/sequence"; [limit(3; reciprocals(positives))] 143 | null 144 | [1,0.5,0.3333333333333333] 145 | 146 | include "math/sequence"; [limit(3; harmonic)] 147 | null 148 | [1,0.5,0.3333333333333333] 149 | 150 | include "math/sequence"; [range(4)]+[0,1,2,3] == [limit(8;moduli(4))] 151 | null 152 | true 153 | 154 | include "math/sequence"; include "stream"; [limit(8; moduli(4))] == [limit(8; repeat(range(4)))] 155 | null 156 | true 157 | 158 | include "math/sequence"; include "stream"; [limit(16; leibniz)] 159 | null 160 | [0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4] 161 | 162 | # replicate ############################################################ 163 | 164 | include "math/sequence"; [range(4)|8] 165 | null 166 | [8,8,8,8] 167 | 168 | include "math/sequence"; [range(4)|"a"] | join("") 169 | null 170 | "aaaa" 171 | 172 | # divisors ############################################################# 173 | 174 | include "math/sequence"; range(1;11) as $n | [divisors($n)] 175 | null 176 | [] 177 | [1] 178 | [1] 179 | [1,2] 180 | [1] 181 | [1,2,3] 182 | [1] 183 | [1,2,4] 184 | [1,3] 185 | [1,2,5] 186 | 187 | include "math/sequence"; range(1;11) as $n | [divisors1($n)] 188 | null 189 | [] 190 | [] 191 | [] 192 | [2] 193 | [] 194 | [2,3] 195 | [] 196 | [2,4] 197 | [3] 198 | [2,5] 199 | 200 | # partitions ########################################################### 201 | 202 | include "math/sequence"; [partition(3)] 203 | null 204 | [[1,1,1], [2,1], [3]] 205 | 206 | include "math/sequence"; partition(0) 207 | null 208 | [] 209 | 210 | include "math/sequence"; partition(1) 211 | null 212 | [1] 213 | 214 | # vim:ai:sw=2:ts=2:et:syntax=jq 215 | -------------------------------------------------------------------------------- /tests/music_interval-class-vector.test: -------------------------------------------------------------------------------- 1 | ######################################################################### 2 | # Interval-class vector tests 3 | # 4 | # Tests are groups of three lines: program, input, expected output. 5 | # Blank lines and lines starting with # are ignored. 6 | ######################################################################### 7 | 8 | include "music/interval-class-vector"; new 9 | [0,1,5,6,8] 10 | [2,1,1,2,3,1] 11 | 12 | include "music/interval-class-vector"; new | format 13 | [0,1,5,6,8] 14 | "211231" 15 | 16 | include "music/interval-class-vector"; new | format 17 | [0,1,2,3,4,5,6,7,8,9,10,11] 18 | "CCCCC6" 19 | 20 | include "music/interval-class-vector"; new | format 21 | [1,2,3,4,5,6,7,8,9,10,11] 22 | "AAAAA5" 23 | 24 | include "music/interval-class-vector"; new | name 25 | [0,4,7] 26 | "pmn" 27 | 28 | include "music/interval-class-vector"; new | name 29 | [0,3,7] 30 | "pmn" 31 | 32 | include "music/interval-class-vector"; new | name 33 | [0,4,7,11] 34 | "p²m²nd" 35 | 36 | include "music/interval-class-vector"; new 37 | [0,4,7] 38 | [0,0,1,1,1,0] 39 | 40 | include "music/interval-class-vector"; new 41 | [4,7,11] 42 | [0,0,1,1,1,0] 43 | 44 | include "music/interval-class-vector"; new | map(tostring) | add 45 | [0,1,3,7] 46 | "111111" 47 | 48 | include "music/interval-class-vector"; new | map(tostring) | add 49 | [4,7,11] 50 | "001110" 51 | 52 | include "music/interval-class-vector"; new | (unique | length == 6) 53 | [0,2,4,5,7,9,11] 54 | true 55 | 56 | include "music/interval-class-vector"; (.[0]|new) == (.[1]|new) 57 | [[0,1,3,7],[0,1,4,6]] 58 | true 59 | 60 | include "music/interval-class-vector"; include "array"; new | uniform 61 | [0,1,3,7] 62 | true 63 | 64 | include "music/interval-class-vector"; include "array"; new | uniform 65 | [0,1,4,6] 66 | true 67 | 68 | include "music/interval-class-vector"; new|multiplicity(1) 69 | [0,1,4,6] 70 | 1 71 | 72 | include "music/interval-class-vector"; new|deep_scale 73 | [0,2,4,5,7,9,11] 74 | true 75 | 76 | include "music/interval-class-vector"; new|deep_scale 77 | [0,4,7] 78 | false 79 | 80 | # vim:ai:sw=2:ts=2:et:syntax=jq 81 | -------------------------------------------------------------------------------- /tests/music_interval-pattern.test: -------------------------------------------------------------------------------- 1 | ######################################################################### 2 | # Interval patterns 3 | # 4 | # Tests are groups of three lines: program, input, expected output. 5 | # Blank lines and lines starting with # are ignored. 6 | ######################################################################### 7 | 8 | include "music/interval-pattern"; new | add 9 | [0,2,4,5,7,9,11] 10 | 12 11 | 12 | include "music/interval-pattern"; new 13 | [0,2,4,5,7,9,11] 14 | [2,2,1,2,2,2,1] 15 | 16 | include "music/interval-pattern"; new | map(tostring) | join("") 17 | [0,2,4,5,7,9,11] 18 | "2212221" 19 | 20 | include "music/interval-pattern"; new 21 | [5,7,9,10,0,2,4] 22 | [2,2,1,2,2,2,1] 23 | 24 | include "music/interval-pattern"; new | map(tostring) | join("") 25 | [5,7,9,10,0,2,4] 26 | "2212221" 27 | 28 | # vim:ai:sw=2:ts=2:et:syntax=jq 29 | -------------------------------------------------------------------------------- /tests/music_interval-table.test: -------------------------------------------------------------------------------- 1 | ######################################################################### 2 | # Music 3 | # 4 | # Tests are groups of three lines: program, input, expected output. 5 | # Blank lines and lines starting with # are ignored. 6 | ######################################################################### 7 | 8 | # tritone 9 | include "music/interval-table"; new 10 | [6] 11 | [] 12 | 13 | # augmented 14 | include "music/interval-table"; new 15 | [2,6,10] 16 | [[4],[8]] 17 | 18 | # tones 19 | include "music/interval-table"; new 20 | [0,2,4,6,8,10] 21 | [[2],[4],[6],[8],[10]] 22 | 23 | # diatonic 24 | include "music/interval-table"; new 25 | [0,2,4,5,7,9,11] 26 | [[1,2],[3,4],[5,6],[6,7],[8,9],[10,11]] 27 | 28 | # octatonic 29 | include "music/interval-table"; new 30 | [0,2,3,5,6,8,9,11] 31 | [[1,2],[3],[4,5],[6],[7,8],[9],[10,11]] 32 | 33 | 34 | # vim:ai:sw=2:ts=2:et:syntax=jq 35 | -------------------------------------------------------------------------------- /tests/music_pitch-class-set.test: -------------------------------------------------------------------------------- 1 | ######################################################################### 2 | # Music 3 | # 4 | # Tests are groups of three lines: program, input, expected output. 5 | # Blank lines and lines starting with # are ignored. 6 | ######################################################################### 7 | 8 | ######################################################################### 9 | # pitch-class sets 10 | 11 | include "array/set"; member(7) 12 | [0,1,2,3,4,5,6,7,8,9,10,11] 13 | true 14 | 15 | include "array/set"; member(7) 16 | [0,1,2,11] 17 | false 18 | 19 | include "music/pitch-class-set"; complement 20 | [0,2,4,5,7,9,11] 21 | [1,3,6,8,10] 22 | 23 | include "array/set"; union([1,3,6,8,10]) | sort 24 | [0,2,4,5,7,9,11] 25 | [0,1,2,3,4,5,6,7,8,9,10,11] 26 | 27 | include "array/set"; intersection([1,3,6,8,10]) 28 | [0,2,4,5,7,9,11] 29 | [] 30 | 31 | include "array/set"; intersection([1,3,6,8,10]) 32 | [0,1,2,3,4,5,6,7,8,9,10,11] 33 | [1,3,6,8,10] 34 | 35 | include "array/set"; inside([0,1,2,3,4,5,6,7,8,9,10,11]) 36 | [1,3,6,8,10] 37 | true 38 | 39 | include "array/set"; inside([1,2,3,4]) 40 | [1,3,5] 41 | false 42 | 43 | include "music/pitch-class-set"; transpositions 44 | [0,2,4,5,7,9,11] 45 | 12 46 | 47 | include "music/pitch-class-set"; [range(12)] | format 48 | null 49 | "0123456789te" 50 | 51 | include "music/pitch-class-set"; [range(12)] | tojson 52 | null 53 | "[0,1,2,3,4,5,6,7,8,9,10,11]" 54 | 55 | include "music/pitch-class-set"; new 56 | "0123456789te" 57 | [0,1,2,3,4,5,6,7,8,9,10,11] 58 | 59 | include "music/pitch-class-set"; new 60 | "[0,1,2,3,4,5,6,7,8,9,10,11]" 61 | [0,1,2,3,4,5,6,7,8,9,10,11] 62 | 63 | # vim:ai:sw=2:ts=2:et:syntax=jq 64 | -------------------------------------------------------------------------------- /tests/music_pitch-class.test: -------------------------------------------------------------------------------- 1 | ######################################################################### 2 | # Music 3 | # 4 | # Tests are groups of three lines: program, input, expected output. 5 | # Blank lines and lines starting with # are ignored. 6 | ######################################################################### 7 | 8 | ######################################################################### 9 | # pitch-class 10 | 11 | include "music/pitch-class"; [range(60;72) | new] 12 | null 13 | [0,1,2,3,4,5,6,7,8,9,10,11] 14 | 15 | include "music/pitch-class"; invert(0) 16 | 2 17 | 10 18 | 19 | include "music/pitch-class"; invert(0) 20 | 10 21 | 2 22 | 23 | include "music/pitch-class"; transpose(10) 24 | 1 25 | 11 26 | 27 | include "music/pitch-class"; transpose(10) 28 | 2 29 | 0 30 | 31 | include "array/set"; element([range(12)]) 32 | 7 33 | true 34 | 35 | include "array/set"; element([range(6)]) 36 | 7 37 | false 38 | 39 | # Diatonic pitch-classes are in chromatic set 40 | include "array/set"; [.[] | element([range(12)])] | all 41 | [0,2,4,5,7,9,11] 42 | true 43 | 44 | # Sharp pitch-classes are not in diatonic set 45 | include "array/set"; [.[] | element([0,2,4,5,7,9,11]) | not] | all 46 | [1,3,6,8,10] 47 | true 48 | 49 | include "music/pitch-class"; [range(60;72) | new | format] 50 | null 51 | ["0","1","2","3","4","5","6","7","8","9","t","e"] 52 | 53 | include "music/pitch-class"; [.[] | new] 54 | ["0","1","2","3","4","5","6","7","8","9","t","e"] 55 | [0,1,2,3,4,5,6,7,8,9,10,11] 56 | 57 | include "music/pitch-class"; name 58 | 0 59 | "C" 60 | 61 | include "music/pitch-class"; name 62 | 7 63 | "G" 64 | 65 | include "music/pitch-class"; [.[] | new ] 66 | ["C0","C♯0","D0","D♯0","E0","F0","F♯0","G0","G♯0","A0","A♯0","B0"] 67 | [0,1,2,3,4,5,6,7,8,9,10,11] 68 | 69 | include "music/pitch-class"; [.[] | new | name ] 70 | ["C1","C♯1","D1","D♯1","E1","F1","F♯1","G1","G♯1","A1","A♯1","B1"] 71 | ["C","C♯","D","D♯","E","F","F♯","G","G♯","A","A♯","B"] 72 | 73 | include "music/pitch-class"; [range(60;72) | new | name] 74 | null 75 | ["C","C♯","D","D♯","E","F","F♯","G","G♯","A","A♯","B"] 76 | 77 | include "music/pitch-class"; interval(7) 78 | 0 79 | 7 80 | 81 | include "music/pitch-class"; interval_class(7) 82 | 0 83 | 5 84 | 85 | # vim:ai:sw=2:ts=2:et:syntax=jq 86 | -------------------------------------------------------------------------------- /tests/music_pitch.test: -------------------------------------------------------------------------------- 1 | ######################################################################### 2 | # Music 3 | # 4 | # Tests are groups of three lines: program, input, expected output. 5 | # Blank lines and lines starting with # are ignored. 6 | ######################################################################### 7 | 8 | ######################################################################### 9 | # pitch 10 | 11 | include "music/pitch"; new 12 | "C0" 13 | 0 14 | 15 | include "music/pitch"; new 16 | "G10" 17 | 127 18 | 19 | include "music/pitch"; map(new) 20 | ["C♯0","C♯1","C♯2"] 21 | [1,13,25] 22 | 23 | include "music/pitch"; map(new | format) 24 | ["D♭0","D♭1","D♭2"] 25 | ["C♯0","C♯1","C♯2"] 26 | 27 | include "music/pitch"; map(new) 28 | ["D♭0","D♭1","D♭2"] 29 | [1,13,25] 30 | 31 | include "music/pitch"; new 32 | "C5" 33 | 60 34 | 35 | include "music/pitch"; format 36 | 60 37 | "C5" 38 | 39 | #include "music/pitch"; transpose(3) 40 | #60 41 | #63 42 | # 43 | #include "music/pitch"; transpose(-3) 44 | #60 45 | #57 46 | 47 | # vim:ai:sw=2:ts=2:et:syntax=jq 48 | -------------------------------------------------------------------------------- /tests/object.test: -------------------------------------------------------------------------------- 1 | ######################################################################### 2 | # Object 3 | # 4 | # Tests are groups of three lines: program, input, expected output. 5 | # Blank lines and lines starting with # are ignored. 6 | ######################################################################### 7 | 8 | include "object"; unknown("x") 9 | {"a":1,"b":2} 10 | false 11 | 12 | include "object"; unknown("x") 13 | {"x":1,"b":2} 14 | false 15 | 16 | include "object"; unknown("x") 17 | {"x":null,"b":2} 18 | true 19 | 20 | # vim:ai:sw=2:ts=2:et:syntax=jq 21 | -------------------------------------------------------------------------------- /tests/object_set.test: -------------------------------------------------------------------------------- 1 | ######################################################################### 2 | # Sets 3 | # 4 | # Tests are groups of three lines: program, input, expected output. 5 | # Blank lines and lines starting with # are ignored. 6 | ######################################################################### 7 | 8 | ######################################################################### 9 | # Set functions 10 | 11 | # 12 | # Set construction 13 | # 14 | 15 | # The empty set: ∅ 16 | length 17 | { } 18 | 0 19 | 20 | length 21 | [ ] 22 | 0 23 | 24 | # Build sets from arrays or strings 25 | include "object/set"; new(.) 26 | [3,2,1,"k"] 27 | {"1":true,"2":true,"3":true,"k":true} 28 | 29 | include "object/set"; new(.) 30 | "aeiuo" 31 | {"a":true,"e":true,"i":true,"o":true,"u":true} 32 | 33 | include "object/set"; new(.) 34 | "uioaaeiouoaeiouae" 35 | {"a":true,"e":true,"i":true,"o":true,"u":true} 36 | 37 | # Set members as a list 38 | include "object/set"; new(.) | keys 39 | "aeiuo" 40 | ["a","e","i","o","u"] 41 | 42 | # Set cardinality 43 | include "object/set"; new(.) | length 44 | "aeiuo" 45 | 5 46 | 47 | # 48 | # Adding and deleting members 49 | # 50 | 51 | include "object/set"; new(.) | del(.e) 52 | "aeiuo" 53 | {"a":true,"i":true,"o":true,"u":true} 54 | 55 | include "object/set"; new(.) | del(.e) | .e = true 56 | "aeiuo" 57 | {"a":true,"e":true,"i":true,"o":true,"u":true} 58 | 59 | include "object/set"; new(.) | (.o,.u) = true 60 | "aei" 61 | {"a":true,"e":true,"i":true,"o":true,"u":true} 62 | 63 | include "object/set"; new(.) | del(.e,.a,.u) 64 | "aeiuo" 65 | {"i":true,"o":true} 66 | 67 | include "object/set"; new(.) | del(.e) | . += {e:true} 68 | "aeiuo" 69 | {"a":true,"e":true,"i":true,"o":true,"u":true} 70 | 71 | include "object/set"; new(.) | del(.e,.a,.u) | . += {u:true,a:true,e:true} 72 | "aeiuo" 73 | {"a":true,"e":true,"i":true,"o":true,"u":true} 74 | 75 | include "object/set"; new(.) | del(.e,.a,.u) | (.e,.a,.u) = true 76 | "aeiuo" 77 | {"a":true,"e":true,"i":true,"o":true,"u":true} 78 | 79 | include "object/set"; new(.) | .x = true 80 | "abc" 81 | {"a":true,"b":true,"c":true,"x":true} 82 | 83 | # 84 | # Set membership 85 | # 86 | 87 | # ∋ 88 | include "object/set"; new(.) | has("k") 89 | [3,2,1,"k"] 90 | true 91 | 92 | contains(["k"]) 93 | [3,2,1,"k"] 94 | true 95 | 96 | # ∋ 97 | include "object/set"; new(.) | .k 98 | [3,2,1,"k"] 99 | true 100 | 101 | # ∋ 102 | include "object/set"; new(.) | has("h") 103 | [3,2,1,"k"] 104 | false 105 | 106 | contains(["h"]) 107 | [3,2,1,"k"] 108 | false 109 | 110 | # ∋ 111 | include "object/set"; new(.) | if .h then true else false end 112 | [3,2,1,"k"] 113 | false 114 | 115 | # ∋ 116 | include "object/set"; new(.) | .h 117 | [3,2,1,"k"] 118 | null 119 | 120 | # ∌ 121 | include "object/set"; new(.) | .h|not 122 | [3,2,1,"k"] 123 | true 124 | 125 | contains(["h"])|not 126 | [3,2,1,"k"] 127 | true 128 | 129 | # ∌ 130 | include "object/set"; new(.) | .h==null 131 | [3,2,1,"k"] 132 | true 133 | 134 | # ∈ 135 | include "object/set"; new(.) as $s | "k" | in($s) 136 | [3,2,1,"k"] 137 | true 138 | 139 | inside([3,2,1,"k"]) 140 | ["k"] 141 | true 142 | 143 | # ∈ 144 | include "object/set"; new(.) as $s | "h" | in($s) 145 | [3,2,1,"k"] 146 | false 147 | 148 | inside([3,2,1,"k"]) 149 | ["h"] 150 | false 151 | 152 | # ∈ 153 | include "object/set"; new(.) as $s | "h" | in($s) 154 | [3,2,1,"k"] 155 | false 156 | 157 | # ∉ 158 | include "object/set"; new(.) as $s | "h" | in($s)|not 159 | [3,2,1,"k"] 160 | true 161 | 162 | inside([3,2,1,"k"])|not 163 | ["h"] 164 | true 165 | 166 | # null => false 167 | include "object/set"; new(.) | .h//false 168 | [3,2,1,"k"] 169 | false 170 | 171 | # 172 | # Common sets operations 173 | # 174 | 175 | # ∪ 176 | include "object/set"; new("aiu") + new("eo") 177 | null 178 | {"a":true,"e":true,"i":true,"o":true,"u":true} 179 | 180 | # ∪ 181 | include "object/set"; new("aiu") * new("eo") 182 | null 183 | {"a":true,"e":true,"i":true,"o":true,"u":true} 184 | 185 | # ∩ 186 | include "object/set"; new("aeiouxyz") | intersection(new("xyz12345")) 187 | null 188 | {"x":true,"y":true,"z":true} 189 | 190 | # ∩ ∅ 191 | include "object/set"; (new("xyz") | intersection(new("abc"))) == {} 192 | null 193 | true 194 | 195 | # ∩ ∪ 196 | include "object/set"; new("aiu") + new("eo") | intersection(new("eo")) 197 | null 198 | {"o":true,"e":true} 199 | 200 | include "object/set"; new("aeiou") | intersection(new("eo")) + new("aiu") 201 | null 202 | {"a":true,"e":true,"i":true,"o":true,"u":true} 203 | 204 | # – 205 | include "object/set"; new("aeiou") | difference(new("abc")) 206 | null 207 | {"e":true,"i":true,"o":true,"u":true} 208 | 209 | ["a","e","i","o","c","u"] - ["a","b","c"] 210 | null 211 | ["e","i","o","u"] 212 | 213 | # ⊂ 214 | include "object/set"; new("xyz") as $a | new("xyz12345") as $b | $a != $b and ($a | intersection($b)) == $a 215 | null 216 | true 217 | 218 | # ⊂ 219 | include "object/set"; new("xyz") | inside(new("xyz12345")) 220 | null 221 | true 222 | 223 | ["x","y","z"] | inside(["x","1","z","y","2","3","4","5"]) 224 | null 225 | true 226 | 227 | # ⊃ 228 | include "object/set"; new("xyz123") as $a | new("xyz") as $b | $a != $b and ($a | intersection($b)) == $b 229 | null 230 | true 231 | 232 | include "object/set"; new(.) | contains(new("k")) 233 | [3,2,1,"k"] 234 | true 235 | 236 | # ≡ ∅ 237 | . == { } 238 | { } 239 | true 240 | 241 | # ≡ 242 | include "object/set"; new("xyz") == {"y":true,"x":true,"z":true} 243 | null 244 | true 245 | 246 | # ≢ ∅ 247 | include "object/set"; new("xyz") != { } 248 | null 249 | true 250 | 251 | # vim:ai:sw=2:ts=2:et:syntax=jq 252 | -------------------------------------------------------------------------------- /tests/prelude.test: -------------------------------------------------------------------------------- 1 | ######################################################################### 2 | # Prelude 3 | # 4 | # Tests are groups of three lines: program, input, expected output. 5 | # Blank lines and lines starting with # are ignored. 6 | ######################################################################### 7 | 8 | #include "prelude"; reduce .[] as $n (0; .+$n) == fold(.[0]+.[1]; 0; .[]) 9 | #[1,2,3,4] 10 | #true 11 | 12 | isempty(empty) 13 | null 14 | true 15 | 16 | isempty(range(.)) 17 | 0 18 | true 19 | 20 | isempty(.) 21 | true 22 | false 23 | 24 | isempty(.) 25 | false 26 | false 27 | 28 | isempty(.) 29 | null 30 | false 31 | 32 | isempty(range(.)) 33 | 8 34 | false 35 | 36 | isempty(.[]) 37 | [1,2,3] 38 | false 39 | 40 | isempty(.) 41 | 0 42 | false 43 | 44 | isempty(infinite) 45 | null 46 | false 47 | 48 | isempty(nan) 49 | null 50 | false 51 | 52 | include "prelude"; every(empty) 53 | null 54 | true 55 | 56 | include "prelude"; some(empty) 57 | null 58 | false 59 | 60 | ######################################################################## 61 | # operators 62 | ######################################################################## 63 | 64 | # . | A ≡ A 65 | (. | .+2) == .+2 66 | 7 67 | true 68 | 69 | # A | . ≡ A 70 | (.+2 | .) == .+2 71 | 7 72 | true 73 | 74 | # empty | A ≡ empty 75 | (empty | .+2)//null == null 76 | 7 77 | true 78 | 79 | # A | empty ≡ empty 80 | (.+2 | empty)//null == null 81 | 7 82 | true 83 | 84 | # empty , A ≡ A 85 | (empty , 5) == 5 86 | null 87 | true 88 | 89 | # A , empty ≡ A 90 | (5 , empty) == 5 91 | null 92 | true 93 | 94 | # A , (B , C) ≡ (A , B) , C 95 | [2 , (3 , 5)] == [(2 , 3) , 5] 96 | null 97 | true 98 | 99 | # A | (B | C) ≡ (A | B) | C 100 | [.*2 | (.*3 | .*5)] == [(.*2 | .*3) | .*5] 101 | 7 102 | true 103 | 104 | # (A , B) | C ≡ (A | C) , (B | C) 105 | [(2 , 3) | .*5 ] == [(2 | .*5), (3 | .*5) ] 106 | null 107 | true 108 | 109 | # NOT ALWAYS??? 110 | # A | (B , C) = (A | B) , (A | C) 111 | [2 | (.*3 , .*5)] == [(2 | .*3) , (2 | .*5)] 112 | null 113 | true 114 | 115 | ######################################################################## 116 | 117 | [3 , 5 | .*2] == [(3 , 5) | .*2] 118 | null 119 | true 120 | 121 | [5 | .*2 , .*3] == [5 | (.*2 , .*3)] 122 | null 123 | true 124 | 125 | [5 | .*2 , .*3] == [(5 | .*2) , (5 | .*3)] 126 | null 127 | true 128 | 129 | [3 , 5 | .*2] == [(3 | .*2) , (5 | .*2)] 130 | null 131 | true 132 | 133 | ######################################################################## 134 | 135 | [limit(7;range(77))] == [while(.<7;.+1)] 136 | 0 137 | true 138 | 139 | [limit(7;range(77))] == [recurse(.+1;.<7)] 140 | 0 141 | true 142 | 143 | [while(.<7;.+1)] == [recurse(.+1;.<7)] 144 | 0 145 | true 146 | 147 | include "prelude"; [recurse(.+1;.<7)] == [label $x | recurse(.+1) | if .<7 then . else break $x end] 148 | 0 149 | true 150 | 151 | ######################################################################## 152 | 153 | include "prelude"; [0|limit(10;recurse(.+1))] == [limit(10;iterate(0;.+1))] 154 | 0 155 | true 156 | 157 | include "prelude"; [limit(10;recurse(.+1))] == [limit(10;iterate(0;.+1))] 158 | 0 159 | true 160 | 161 | include "prelude"; [0|limit(10;recurse(.+1))] == [limit(10;iterate(0;.+1))] 162 | 0 163 | true 164 | 165 | include "prelude"; [limit(10;recurse(.+1))] == [limit(10;iterate(0;.+1))] 166 | 0 167 | true 168 | 169 | include "prelude"; [limit(11;1|iterate(.+2,.*2))] 170 | null 171 | [1,3,2,5,6,4,4,7,10,8,12] 172 | 173 | include "prelude"; [limit(11;1|recurse(.+2,.*2))] 174 | null 175 | [1,3,5,7,9,11,13,15,17,19,21] 176 | 177 | include "prelude"; [limit(6; seq|pow(.;2))] 178 | null 179 | [0,1,4,9,16,25] 180 | 181 | include "prelude"; [range(0;10;2)] 182 | null 183 | [0,2,4,6,8] 184 | 185 | include "prelude"; [range(10;0;-2)] 186 | null 187 | [10,8,6,4,2] 188 | 189 | include "prelude"; mapstr(select(.!="g")) 190 | "agogico" 191 | "aoico" 192 | 193 | include "prelude"; [0|while(.<10;.+1)] == [0|recurse(.+1;select(.<10))] 194 | null 195 | true 196 | 197 | #include "prelude"; [0|while(.<10;.+1)] == [0|try(recurse(.+1)|guard(.<10))] 198 | #null 199 | #true 200 | 201 | #include "prelude"; [0|until(.>10;.+1)] == [0|try(recurse(.+1) | select(.>10) | fence)] 202 | #null 203 | #true 204 | 205 | # vim:ai:sw=2:ts=2:et:syntax=jq 206 | -------------------------------------------------------------------------------- /tests/stream.test: -------------------------------------------------------------------------------- 1 | ######################################################################### 2 | # Generators 3 | # 4 | # Tests are groups of three lines: program, input, expected output. 5 | # Blank lines and lines starting with # are ignored. 6 | ######################################################################### 7 | 8 | include "stream"; [take(range(1000); .<7)] 9 | null 10 | [0,1,2,3,4,5,6] 11 | 12 | include "stream"; singleton(range(10)) 13 | null 14 | false 15 | 16 | include "stream"; singleton(empty) 17 | null 18 | false 19 | 20 | include "stream"; singleton(range(1)) 21 | null 22 | true 23 | 24 | include "stream"; member(range(10)) 25 | 6 26 | true 27 | 28 | IN(range(10)) 29 | 66 30 | false 31 | 32 | include "stream"; member(range(10)) 33 | 6 34 | true 35 | 36 | IN(range(10)) 37 | 66 38 | false 39 | 40 | include "stream"; member(6; range(10)) 41 | null 42 | true 43 | 44 | include "stream"; member(66; range(10)) 45 | null 46 | false 47 | 48 | include "stream"; sharing((33,7,44); range(10)) 49 | null 50 | true 51 | 52 | IN((33,7,44); range(10)) 53 | null 54 | true 55 | 56 | [empty, range(8)] == [range(8), empty] 57 | null 58 | true 59 | 60 | [empty, range(8), empty] == [range(8)] 61 | null 62 | true 63 | 64 | [(range(2), range(3)), range(4)] == [range(2), (range(3), range(4))] 65 | null 66 | true 67 | 68 | isempty(empty) 69 | null 70 | true 71 | 72 | # 73 | # all, any 74 | # 75 | include "stream"; []|all 76 | null 77 | true 78 | 79 | include "stream"; [true]|all 80 | null 81 | true 82 | 83 | include "stream"; [true,false]|all 84 | null 85 | false 86 | 87 | include "stream"; [range(10)]|all(.<10) 88 | null 89 | true 90 | 91 | include "stream"; []|any 92 | null 93 | false 94 | 95 | include "stream"; [true]|any 96 | null 97 | true 98 | 99 | include "stream"; [1,2,3]|any(.>2) 100 | null 101 | true 102 | 103 | include "stream"; [1,2,3]|any(.>3) 104 | null 105 | false 106 | 107 | # 108 | # first, drop, nth 109 | # 110 | 111 | first([][]) 112 | null 113 | # empty 114 | 115 | first(range(5;100)) 116 | null 117 | 5 118 | 119 | include "stream"; nth(0; range(5;100)) 120 | null 121 | 5 122 | 123 | include "stream"; [limit(4;range(10))] == [0,1,2,3] 124 | null 125 | true 126 | 127 | include "stream"; drop(5; range(8)) 128 | null 129 | 5 130 | 6 131 | 7 132 | 133 | include "stream"; drop(1000; range(1002)) 134 | null 135 | 1000 136 | 1001 137 | 138 | include "stream"; drop(10; range(1)) 139 | null 140 | # empty 141 | 142 | include "stream"; drop(10; range(10)) 143 | null 144 | # empty 145 | 146 | 147 | include "stream"; [slice(10; 13; range(1002))] 148 | null 149 | [10,11,12] 150 | 151 | # 152 | # repeat 153 | # 154 | 155 | [limit(6; repeat(range(3)))] 156 | null 157 | [0,1,2,0,1,2] 158 | 159 | [limit(6; repeat(range(3)))] 160 | null 161 | [0,1,2,0,1,2] 162 | 163 | [limit(6; repeat([0,1,2][]))] 164 | null 165 | [0,1,2,0,1,2] 166 | 167 | [limit(4; repeat(8))] 168 | null 169 | [8,8,8,8] 170 | 171 | # 172 | # enum 173 | # 174 | 175 | include "stream"; enum(split("")[]) 176 | "ae" 177 | [0,"a"] 178 | [1,"e"] 179 | 180 | include "stream"; last(enum(split("")[])) 181 | "aeiou" 182 | [4,"u"] 183 | 184 | include "stream"; nth(11; enum(split("")[])) 185 | "abcdefghijklmnopqrstuvwxyz" 186 | [11,"l"] 187 | 188 | include "stream"; [distinct(1,2,1,1,2,3,2,1,2,2,3,2,1)] 189 | null 190 | [1,2,3] 191 | 192 | # vim:ai:sw=2:ts=2:et:syntax=jq 193 | -------------------------------------------------------------------------------- /tests/string.test: -------------------------------------------------------------------------------- 1 | ######################################################################### 2 | # Text functions 3 | # 4 | # Classic string functions are easy to implement. Also functions 5 | # inspired in the Icon Language. 6 | # 7 | # Tests are groups of three lines: program, input, expected output. 8 | # Blank lines and lines starting with # are ignored. 9 | ######################################################################### 10 | 11 | include "string"; remove("aeiou") 12 | "loquenomesabiayo" 13 | "lqnmsby" 14 | 15 | # 16 | # Icon style 17 | # 18 | include "string"; center(8) 19 | "AEI" 20 | " AEI " 21 | 22 | include "string"; center(8) 23 | "AE" 24 | " AE " 25 | 26 | include "string"; center(9) 27 | "AEI" 28 | " AEI " 29 | 30 | include "string"; center(9) 31 | "AE" 32 | " AE " 33 | 34 | include "string"; left(10) 35 | "AEIOU" 36 | " AEIOU" 37 | 38 | include "string"; right(10) 39 | "AEIOU" 40 | "AEIOU " 41 | 42 | # trim 43 | include "string"; ltrim 44 | "" 45 | "" 46 | 47 | include "string"; rtrim 48 | "" 49 | "" 50 | 51 | include "string"; ltrim 52 | " " 53 | "" 54 | 55 | include "string"; rtrim 56 | " " 57 | "" 58 | 59 | include "string"; ltrim 60 | "aaaaa" 61 | "aaaaa" 62 | 63 | include "string"; rtrim 64 | "aaaaa" 65 | "aaaaa" 66 | 67 | include "string"; ltrim 68 | " aaaaa" 69 | "aaaaa" 70 | 71 | include "string"; rtrim 72 | "aaaaa " 73 | "aaaaa" 74 | 75 | include "string"; trim 76 | " aaaaa " 77 | "aaaaa" 78 | 79 | include "string"; trim 80 | "a b c" 81 | "a b c" 82 | 83 | include "string"; ltrim 84 | " a b c" 85 | "a b c" 86 | 87 | include "string"; rtrim 88 | "a b c " 89 | "a b c" 90 | 91 | include "string"; trim 92 | " a b c " 93 | "a b c" 94 | 95 | # 96 | # char, ord 97 | # 98 | 99 | include "string"; ord("A") 100 | null 101 | 65 102 | 103 | include "string"; char(65) == "A" 104 | null 105 | true 106 | 107 | include "string"; ord(char(65)) == 65 and char(ord("A")) == "A" 108 | null 109 | true 110 | 111 | include "string"; [ord("€"), char(8364)] 112 | null 113 | [8364, "€"] 114 | 115 | # 116 | 117 | include "string"; join 118 | ["a","b"] 119 | "ab" 120 | 121 | include "string"; join(";") 122 | ["a","b"] 123 | "a;b" 124 | 125 | # vim:ai:sw=2:ts=2:et:syntax=jq 126 | -------------------------------------------------------------------------------- /tests/string_ascii.test: -------------------------------------------------------------------------------- 1 | ######################################################################### 2 | # ASCII encoding 3 | # 4 | # Tests are groups of three lines: program, input, expected output. 5 | # Blank lines and lines starting with # are ignored. 6 | ######################################################################### 7 | 8 | include "string/ascii"; isascii 9 | "AbCdE\t" 10 | true 11 | 12 | include "string/ascii"; iscntrl 13 | "\t\n\r" 14 | true 15 | 16 | include "string/ascii"; isspace 17 | " \t\r\n" 18 | true 19 | 20 | include "string/ascii"; isblank 21 | " \t" 22 | true 23 | 24 | include "string/ascii"; isupper 25 | "ABCDE" 26 | true 27 | 28 | include "string/ascii"; islower 29 | "abcde" 30 | true 31 | 32 | include "string/ascii"; isdigit 33 | "123" 34 | true 35 | 36 | include "string/ascii"; isxdigit 37 | "A123" 38 | true 39 | 40 | include "string/ascii"; ispunct 41 | "[],." 42 | true 43 | 44 | include "string/ascii"; isalpha 45 | "aA" 46 | true 47 | 48 | include "string/ascii"; isalnum 49 | "Aa12" 50 | true 51 | 52 | include "string/ascii"; isgraph 53 | "A[]" 54 | true 55 | 56 | include "string/ascii"; isprint 57 | "A []" 58 | true 59 | 60 | include "string/ascii"; isword 61 | "A_1" 62 | true 63 | 64 | include "string/ascii"; tolower 65 | "AbCdE" 66 | "abcde" 67 | 68 | include "string/ascii"; toupper 69 | "AbCdE" 70 | "ABCDE" 71 | 72 | # vim:ai:sw=2:ts=2:et:syntax=jq 73 | -------------------------------------------------------------------------------- /tests/string_latin1.test: -------------------------------------------------------------------------------- 1 | ######################################################################### 2 | # ISO-8859-1 encoding 3 | # 4 | # Tests are groups of three lines: program, input, expected output. 5 | # Blank lines and lines starting with # are ignored. 6 | ######################################################################### 7 | 8 | include "string/latin1"; isascii 9 | "AbCdE\t" 10 | true 11 | 12 | include "string/latin1"; islatin1 13 | "Ñç" 14 | true 15 | 16 | include "string/latin1"; isvacant 17 | "\u0080\u009F" 18 | true 19 | 20 | include "string/latin1"; iscntrl 21 | "\t\n\r" 22 | true 23 | 24 | include "string/latin1"; isspace 25 | " \t\r\n" 26 | true 27 | 28 | include "string/latin1"; isblank 29 | " \t " 30 | true 31 | 32 | include "string/latin1"; isupper 33 | "ÀBCÑÇDE" 34 | true 35 | 36 | include "string/latin1"; islower 37 | "áñèiço" 38 | true 39 | 40 | include "string/latin1"; isdigit 41 | "123" 42 | true 43 | 44 | include "string/latin1"; isxdigit 45 | "A123" 46 | true 47 | 48 | include "string/latin1"; ispunct 49 | "[],¶." 50 | true 51 | 52 | include "string/latin1"; isalpha 53 | "Niña" 54 | true 55 | 56 | include "string/latin1"; isalnum 57 | "Ço12" 58 | true 59 | 60 | include "string/latin1"; isgraph 61 | "A[]Ñ" 62 | true 63 | 64 | include "string/latin1"; isprint 65 | "A []Ç" 66 | true 67 | 68 | include "string/latin1"; isword 69 | "A_l·l1" 70 | true 71 | 72 | include "string/latin1"; tolower 73 | "NIÑO" 74 | "niño" 75 | 76 | include "string/latin1"; toupper 77 | "niño" 78 | "NIÑO" 79 | 80 | # vim:ai:sw=2:ts=2:et:syntax=jq 81 | -------------------------------------------------------------------------------- /tests/string_regexp.test: -------------------------------------------------------------------------------- 1 | ######################################################################### 2 | # Regex functions 3 | # 4 | # Tests are groups of three lines: program, input, expected output. 5 | # Blank lines and lines starting with # are ignored. 6 | ######################################################################### 7 | 8 | include "string/regexp"; include "string"; [split] | join(" ") 9 | " la le li " 10 | "la le li" 11 | 12 | # match 13 | include "string/regexp"; match("a")|tostr 14 | "----a---" 15 | "a" 16 | 17 | include "string/regexp"; match("a")|tostr 18 | "----a---" 19 | "a" 20 | 21 | include "string/regexp"; [match("a";"g")|tostr] 22 | "-a-a-a-a-" 23 | ["a","a","a","a"] 24 | 25 | include "string/regexp"; [match("a";"g")|tostr] 26 | "-a-a-a-a-" 27 | ["a","a","a","a"] 28 | 29 | include "string/regexp"; match("([abc])";"g")|tolist 30 | "abc" 31 | ["a","a"] 32 | ["b","b"] 33 | ["c","c"] 34 | 35 | include "string/regexp"; match("([abc])";"g")|tolist 36 | "abc" 37 | ["a","a"] 38 | ["b","b"] 39 | ["c","c"] 40 | 41 | include "string/regexp"; match("(?

[abc])";"g")|tomap 42 | "-a-" 43 | {"&":"a","`":"-","'":"-","p":"a"} 44 | 45 | include "string/regexp"; match("^(?[A-G])(?[#b])?(?[0-9]|10)?$";"")|tomap|{n,a,o} 46 | "C#3" 47 | {"n":"C","a":"#","o":"3"} 48 | 49 | include "string/regexp"; match("^(?[A-G])(?[#b])?(?[0-9]|10)$";"")|tomap|{n,a,o} 50 | "C3" 51 | {"n":"C","a":null,"o":"3"} 52 | 53 | # split 54 | include "string/regexp"; [split] 55 | "a b \tc" 56 | ["a","b","c"] 57 | 58 | include "string/regexp"; [split] 59 | " a b c " 60 | ["a","b","c"] 61 | 62 | include "string/regexp"; [split("b")] 63 | "abc" 64 | ["a","c"] 65 | 66 | include "string/regexp"; ./"" 67 | "abc" 68 | ["a","b","c"] 69 | 70 | include "string/regexp"; ./"," 71 | "a,b,c" 72 | ["a","b","c"] 73 | 74 | include "string/regexp"; ./"," 75 | ",a,b,c," 76 | ["","a","b","c",""] 77 | 78 | include "string/regexp"; [split(",")] 79 | ",,a,b,c," 80 | ["","","a","b","c",""] 81 | 82 | include "string/regexp"; [split(",")] 83 | "a,b,c" 84 | ["a","b","c"] 85 | 86 | include "string/regexp"; split(",";-1) 87 | "a,b,c" 88 | # empty 89 | 90 | include "string/regexp"; [split(",";0)] 91 | "a,b,c" 92 | ["a,b,c"] 93 | 94 | include "string/regexp"; [split(",";1)] 95 | "a,b,c" 96 | ["a","b,c"] 97 | 98 | include "string/regexp"; [split(",";2)] 99 | "a,b,c" 100 | ["a","b","c"] 101 | 102 | include "string/regexp"; [split(",";3)] 103 | "a,b,c" 104 | ["a","b","c"] 105 | 106 | include "string/regexp"; [split(",";4)] 107 | "a,b,c" 108 | ["a","b","c"] 109 | 110 | include "string/regexp"; [split(",")] 111 | "a,b,c" 112 | ["a","b","c"] 113 | 114 | include "string/regexp"; include "string"; [rstrip(",")|split(",")] 115 | "a,b,c,,," 116 | ["a","b","c"] 117 | 118 | include "string/regexp"; [split(",")] 119 | "a,b,c,,," 120 | ["a","b","c","","",""] 121 | 122 | include "string/regexp"; [split("")]!=(./"") 123 | "abc" 124 | true 125 | 126 | include "string/regexp"; [split("-|,"; 3)] 127 | "1-10,20" 128 | ["1","10","20"] 129 | 130 | include "string/regexp"; [split("(-|,)"; 3)] 131 | "1-10,20" 132 | ["1","-","10",",","20"] 133 | 134 | include "string/regexp"; [split("-|(,)")] 135 | "1-10,20" 136 | ["1",null,"10",",","20"] 137 | 138 | include "string/regexp"; [split("(-)|,"; 3)] 139 | "1-10,20" 140 | ["1","-","10",null,"20"] 141 | 142 | include "string/regexp"; [split("(-)|(,)")] 143 | "1-10,20" 144 | ["1","-",null,"10",null,",","20"] 145 | 146 | # sub 147 | 148 | include "string/regexp"; sub("a";"b") 149 | "aeiouae" 150 | "beiouae" 151 | 152 | include "string/regexp"; gsub("a";"b") 153 | "aeiouae" 154 | "beioube" 155 | 156 | include "string/regexp"; gsub("(?\\d+)";"\(.n)1") 157 | "12a34b56" 158 | "121a341b561" 159 | 160 | include "string/regexp"; sub("\\d+";"\((.["&"]|tonumber)*2)") 161 | "abc123xyz" 162 | "abc246xyz" 163 | 164 | include "string/regexp"; sub("[Ll][Oo][Nn][Dd][Oo][Nn]";"London") 165 | "londOn" 166 | "London" 167 | 168 | include "string/regexp"; gsub("(?[A-Z])";":\(.c):") 169 | "Lord Whopper of Fibbing" 170 | ":L:ord :W:hopper of :F:ibbing" 171 | 172 | include "string/regexp"; gsub("(?.)(?.*)(?.)";"\(.l)\(.m)\(.f)") 173 | "Aasdfas iao sdjladsj asjdZ" 174 | "Zasdfas iao sdjladsj asjdA" 175 | 176 | # BREAK(",") 177 | include "string/regexp"; [match("([^,]+)(?:,|)";"g")|tostr] 178 | "a,b,cde,f,gh,i" 179 | ["a","b","cde","f","gh","i"] 180 | 181 | include "string/regexp"; [match("([^,]+),?";"g")|tostr] 182 | "a,b,cde,f,gh,i," 183 | ["a","b","cde","f","gh","i"] 184 | 185 | include "string/regexp"; [match("([^,]+),|([^,]+)";"g")|tostr] 186 | "a,b,cde,f,gh,i," 187 | ["a","b","cde","f","gh","i"] 188 | 189 | include "string/regexp"; [match("([^,]+),|([^,]+)";"g")|tostr] 190 | "a,b,cde,f,gh,i" 191 | ["a","b","cde","f","gh","i"] 192 | 193 | # vim:ai:sw=2:ts=2:et:syntax=jq 194 | -------------------------------------------------------------------------------- /tests/string_roman.test: -------------------------------------------------------------------------------- 1 | ######################################################################### 2 | # Roman numerals 3 | # 4 | # Tests are groups of three lines: program, input, expected output. 5 | # Blank lines and lines starting with # are ignored. 6 | ######################################################################### 7 | 8 | include "string/roman"; encode 9 | 11 10 | "XI" 11 | 12 | include "string/roman"; encode 13 | 1961 14 | "MCMLXI" 15 | 16 | include "string/roman"; encode 17 | 3999 18 | "MMMCMXCIX" 19 | 20 | include "string/roman"; [ (1666, 1990, 2008, 2016, 2018) | encode] 21 | null 22 | ["MDCLXVI", "MCMXC", "MMVIII", "MMXVI", "MMXVIII"] 23 | 24 | include "string/roman"; [ ("MDCLXVI", "MCMXC", "MMVIII", "MMXVI", "MMXVIII") | decode] 25 | null 26 | [1666, 1990, 2008, 2016, 2018] 27 | 28 | # vim:ai:sw=2:ts=2:et:syntax=jq 29 | -------------------------------------------------------------------------------- /tests/string_table.test: -------------------------------------------------------------------------------- 1 | ######################################################################### 2 | # Conversion tables 3 | # 4 | # Tests are groups of three lines: program, input, expected output. 5 | # Blank lines and lines starting with # are ignored. 6 | ######################################################################### 7 | 8 | # 9 | # translate 10 | # 11 | 12 | 13 | include "string/table"; import "string/ascii" as ascii; ascii::toupper == translate(ascii::tt_toupper) 14 | "kaleidoscope" 15 | true 16 | 17 | include "string/table"; import "string/ascii" as ascii; translate(ascii::tt_toupper) 18 | "kaleidoscope" 19 | "KALEIDOSCOPE" 20 | 21 | include "string/table"; import "string/ascii" as ascii; ascii::tolower == translate(ascii::tt_tolower) 22 | "KALEIDOSCOPE" 23 | true 24 | 25 | include "string/table"; import "string/ascii" as ascii; translate(ascii::tt_tolower) 26 | "KALEIDOSCOPE" 27 | "kaleidoscope" 28 | 29 | # rot13 30 | include "string/table"; import "string/ascii" as ascii; def R: translate(rot13); . == (R|R) 31 | "Joan" 32 | true 33 | 34 | # flip 35 | include "string/table"; import "string/ascii" as ascii; translate(new(ascii::alpha; ascii::ALPHA)) 36 | "Una Casa" 37 | "uNA cASA" 38 | 39 | # remove 40 | include "string/table"; translate("01234567890"; "") 41 | "asdhaisd867asd876asiduhakjdshads8a08d60asdioahsdasd89a0sd" 42 | "asdhaisdasdasiduhakjdshadsadasdioahsdasdasd" 43 | 44 | include "string/table"; import "string/ascii" as ascii; translate(ascii::cntrl; "") 45 | "a\ne\rg\tl" 46 | "aegl" 47 | 48 | # preserve 49 | include "string/table"; import "string/ascii" as ascii; translate(preserve(.; ascii::lower)) 50 | "XXa\nUUe\rg\tlZZ" 51 | "aegl" 52 | 53 | include "string/table"; import "string/ascii" as ascii; translate(translate(ascii::lower; ""); "") 54 | "XXa\nUUe\rg\tlZZ" 55 | "aegl" 56 | 57 | # vim:ai:sw=2:ts=2:et:syntax=jq 58 | -------------------------------------------------------------------------------- /tests/string_url.test: -------------------------------------------------------------------------------- 1 | ######################################################################### 2 | # URL functions 3 | # 4 | # Tests are groups of three lines: program, input, expected output. 5 | # Blank lines and lines starting with # are ignored. 6 | ######################################################################### 7 | 8 | include "string/url"; decode 9 | "%21%23%24%26%27%28%29%2A%2B%2C%2F%3A%3B%3D%3F%40%5B%5D" 10 | "!#$&'()*+,/:;=?@[]" 11 | 12 | # vim:ai:sw=2:ts=2:et:syntax=jq 13 | -------------------------------------------------------------------------------- /tests/types.test: -------------------------------------------------------------------------------- 1 | ######################################################################### 2 | # Types 3 | # 4 | # Tests are groups of three lines: program, input, expected output. 5 | # Blank lines and lines starting with # are ignored. 6 | ######################################################################### 7 | 8 | include "types"; tobool(1,2,3) 9 | null 10 | true 11 | 12 | include "types"; tobool(empty) 13 | null 14 | false 15 | 16 | include "types"; map(tobool) 17 | [null,false,true,0,"",[],{}] 18 | [false,false,true,true,true,true,true] 19 | 20 | include "types"; isscalar 21 | null 22 | true 23 | 24 | ######################################################################## 25 | # null tests 26 | ######################################################################## 27 | 28 | length 29 | null 30 | 0 31 | 32 | . < -(infinite) 33 | null 34 | true 35 | 36 | .==null 37 | null 38 | true 39 | 40 | type=="null" 41 | null 42 | true 43 | 44 | .a=9 45 | null 46 | {"a":9} 47 | 48 | .[2]=9 49 | null 50 | [null,null,9] 51 | 52 | not|not 53 | null 54 | false 55 | 56 | .+33 57 | null 58 | 33 59 | 60 | .+"la" 61 | null 62 | "la" 63 | 64 | .+[1,2,3] 65 | null 66 | [1,2,3] 67 | 68 | .+{a:1} 69 | null 70 | {"a":1} 71 | 72 | 73 | "any string" * 0 74 | null 75 | null 76 | 77 | nan | tojson 78 | null 79 | "null" 80 | 81 | fromjson | isinfinite 82 | "Inf" 83 | true 84 | 85 | fromjson | isnan 86 | "NaN" 87 | true 88 | 89 | fromjson | tojson 90 | "NaN" 91 | "null" 92 | 93 | null[7] == null 94 | null 95 | true 96 | 97 | null.kk == null 98 | null 99 | true 100 | 101 | null["kk"] == null 102 | null 103 | true 104 | 105 | add 106 | [] 107 | null 108 | 109 | add 110 | {} 111 | null 112 | 113 | null//7 114 | null 115 | 7 116 | 117 | .[88]==null 118 | [] 119 | true 120 | 121 | path(null) 122 | null 123 | [] 124 | 125 | # vim:ai:sw=2:ts=2:et:syntax=jq 126 | -------------------------------------------------------------------------------- /tests/word.test: -------------------------------------------------------------------------------- 1 | ######################################################################### 2 | # Combinatorics on Words 3 | # 4 | # Tests are groups of three lines: program, input, expected output. 5 | # Blank lines and lines starting with # are ignored. 6 | ######################################################################### 7 | 8 | include "word"; rotate(1) 9 | [1,2,3,4,5] 10 | [2,3,4,5,1] 11 | 12 | include "word"; rotate(-1) 13 | [1,2,3,4,5] 14 | [5,1,2,3,4] 15 | 16 | include "word"; rotate(2) 17 | "kaleidoscope" 18 | "leidoscopeka" 19 | 20 | include "word"; rotate(-2) 21 | "kaleidoscope" 22 | "pekaleidosco" 23 | 24 | # Catenation 25 | . + [1,2,3] 26 | [3,4,5] 27 | [3,4,5,1,2,3] 28 | 29 | # Length of w 30 | length 31 | [3,4,5] 32 | 3 33 | 34 | # Number of a's in w 35 | indices(["a"]) | length 36 | [1,"a",3,4,"a",5] 37 | 2 38 | 39 | indices("a") | length 40 | "1a34a5" 41 | 2 42 | 43 | include "word"; count("as") 44 | "kjhkjhasiouoiuasuyt" 45 | 2 46 | 47 | include "word"; count("a") 48 | [1,"a",3,4,"a",5] 49 | 2 50 | 51 | include "word"; count("x") 52 | [1,"a",3,4,"a",5] 53 | 0 54 | 55 | # Alphabet of w 56 | unique 57 | [1,5,2,3,4,2,5] 58 | [1,2,3,4,5] 59 | 60 | # Reverse 61 | reverse 62 | [1,2,3,4,5] 63 | [5,4,3,2,1] 64 | 65 | include "word"; mirror 66 | [1,2,3,4,5] 67 | [5,4,3,2,1] 68 | 69 | include "word"; mirror 70 | "aeiou" 71 | "uoiea" 72 | 73 | 74 | ######################################################################## 75 | 76 | include "word"; power(0) 77 | "abcd" 78 | "" 79 | 80 | include "word"; power(1) 81 | "abcd" 82 | "abcd" 83 | 84 | include "word"; power(2) 85 | "abcd" 86 | "abcdabcd" 87 | 88 | include "word"; power(4) 89 | "abcd" 90 | "abcdabcdabcdabcd" 91 | 92 | include "word"; power(0) 93 | [2,3,4,2] 94 | [] 95 | 96 | include "word"; power(1) 97 | [2,3,4,2] 98 | [2,3,4,2] 99 | 100 | include "word"; power(2) 101 | [2,3,4,2] 102 | [2,3,4,2,2,3,4,2] 103 | 104 | include "word"; [[limit(13;star)][1:],[limit(12;plus)]] | .[0]==.[1] 105 | [1,2,3] 106 | true 107 | 108 | include "word"; [[limit(13;star)][1:],[limit(12;plus)]] | .[0]==.[1] 109 | "abc" 110 | true 111 | 112 | include "word"; power(0) 113 | "abcd" 114 | "" 115 | 116 | include "word"; power(1) 117 | "abcd" 118 | "abcd" 119 | 120 | include "word"; power(2) 121 | "abcd" 122 | "abcdabcd" 123 | 124 | include "word"; power(4) 125 | "abcd" 126 | "abcdabcdabcdabcd" 127 | 128 | include "word"; power(0) 129 | [2,3,4,2] 130 | [] 131 | 132 | include "word"; power(1) 133 | [2,3,4,2] 134 | [2,3,4,2] 135 | 136 | include "word"; power(2) 137 | [2,3,4,2] 138 | [2,3,4,2,2,3,4,2] 139 | 140 | include "word"; [[limit(13;star)][1:],[limit(12;plus)]] | .[0]==.[1] 141 | [1,2,3] 142 | true 143 | 144 | include "word"; [[limit(13;star)][1:],[limit(12;plus)]] | .[0]==.[1] 145 | "abc" 146 | true 147 | 148 | include "word"; splice(5;5;"xyz") 149 | "aeiou" 150 | "aeiouxyz" 151 | 152 | include "word"; splice(0;0;"xyz") 153 | "aeiou" 154 | "xyzaeiou" 155 | 156 | include "word"; splice(1;1;"xyz") 157 | "aeiou" 158 | "axyzeiou" 159 | 160 | include "word"; splice(1;2;"xyz") 161 | "aeiou" 162 | "axyziou" 163 | 164 | include "word"; splice(1;2;null) 165 | "aeiou" 166 | "aiou" 167 | 168 | include "word"; gsub("a";"x") 169 | "mi mama me mima" 170 | "mi mxmx me mimx" 171 | 172 | include "word"; gsub("a";"") 173 | "mi mama me mima" 174 | "mi mm me mim" 175 | 176 | include "word"; gsub("";"ignored") 177 | "mi mama me mima" 178 | "mi mama me mima" 179 | 180 | include "word"; gsub("ignored";"ignored") 181 | "" 182 | "" 183 | 184 | include "word"; sub("a";"x") 185 | "mi mama me mima" 186 | "mi mxma me mima" 187 | 188 | 189 | # vim:ai:sw=2:ts=2:et:syntax=jq 190 | -------------------------------------------------------------------------------- /tests/word_alphabet.test: -------------------------------------------------------------------------------- 1 | ######################################################################### 2 | # Alphabet operations and generated languages 3 | # 4 | # Tests are groups of three lines: program, input, expected output. 5 | # Blank lines and lines starting with # are ignored. 6 | ######################################################################### 7 | 8 | include "word/alphabet"; power(0) 9 | "123" 10 | "" 11 | 12 | include "word/alphabet"; limit(1;star) 13 | "123" 14 | "" 15 | 16 | include "word/alphabet"; power(0) 17 | [1,2,3] 18 | [] 19 | 20 | include "word/alphabet"; [power(1)] 21 | "123" 22 | ["1","2","3"] 23 | 24 | include "word/alphabet"; [power(1)] 25 | [1,2,3] 26 | [[1],[2],[3]] 27 | 28 | include "word/alphabet"; [power(2)] 29 | [1,2,3] 30 | [[1,1],[1,2],[1,3],[2,1],[2,2],[2,3],[3,1],[3,2],[3,3]] 31 | 32 | include "word/alphabet"; [power(2)] 33 | "123" 34 | ["11","12","13","21","22","23","31","32","33"] 35 | 36 | include "word/alphabet"; limit(2;star) 37 | "123" 38 | "" 39 | "1" 40 | 41 | include "word/alphabet"; limit(2;star) 42 | [1,2] 43 | [] 44 | [1] 45 | 46 | include "word/alphabet"; limit(1;star) 47 | "123" 48 | "" 49 | 50 | include "word/alphabet"; limit(1;star) 51 | [1,2] 52 | [] 53 | 54 | include "word/alphabet"; limit(2;plus) 55 | "123" 56 | "1" 57 | "2" 58 | 59 | include "word/alphabet"; limit(2;plus) 60 | [1,2] 61 | [1] 62 | [2] 63 | 64 | include "word/alphabet"; limit(1;plus) 65 | "123" 66 | "1" 67 | 68 | include "word/alphabet"; limit(1;plus) 69 | [1,2] 70 | [1] 71 | 72 | include "word/alphabet"; [[limit(13;star)],["",limit(12;plus)]] | .[0]==.[1] 73 | "123" 74 | true 75 | 76 | include "word/alphabet"; [[limit(13;star)],[[],limit(12;plus)]] | .[0]==.[1] 77 | [1,2,3] 78 | true 79 | 80 | # vim:ai:sw=2:ts=2:et:syntax=jq 81 | -------------------------------------------------------------------------------- /tests/word_factor.test: -------------------------------------------------------------------------------- 1 | ######################################################################### 2 | # Combinatorics on Words 3 | # 4 | # Tests are groups of three lines: program, input, expected output. 5 | # Blank lines and lines starting with # are ignored. 6 | ######################################################################### 7 | 8 | include "word/factor"; isfactor([1,2]) 9 | [2,3,1,2,3] 10 | true 11 | 12 | include "word/factor"; isfactor("a") 13 | "lae" 14 | true 15 | 16 | include "word/factor"; ispfactor([]) 17 | [1,2, 3,4,2, 5] 18 | false 19 | 20 | include "word/factor"; ispfactor([3,4,2]) 21 | [] 22 | false 23 | 24 | include "word/factor"; ispfactor([3,4,2]) 25 | [3,4,2] 26 | false 27 | 28 | include "word/factor"; ispfactor([3,4,2]) 29 | [1,2, 3,4,2, 5] 30 | true 31 | 32 | include "word/factor"; ispfactor("342") 33 | "123425" 34 | true 35 | 36 | ######################################################################## 37 | 38 | include "word/factor"; [prefixes] 39 | "abcd" 40 | ["a", "ab", "abc", "abcd"] 41 | 42 | include "word/factor"; [suffixes] 43 | "abcd" 44 | ["d", "cd", "bcd", "abcd"] 45 | 46 | include "word/factor"; [factors] 47 | "abcd" 48 | ["a","b","c","d","ab","bc","cd","abc","bcd","abcd"] 49 | 50 | 51 | include "word/factor"; [prefixes] 52 | "abcd" 53 | ["a", "ab", "abc", "abcd"] 54 | 55 | include "word/factor"; [suffixes] 56 | "abcd" 57 | ["d", "cd", "bcd", "abcd"] 58 | 59 | include "word/factor"; [factors] 60 | "abcd" 61 | ["a","b","c","d","ab","bc","cd","abc","bcd","abcd"] 62 | 63 | # vim:ai:sw=2:ts=2:et:syntax=jq 64 | -------------------------------------------------------------------------------- /tests/word_language.test: -------------------------------------------------------------------------------- 1 | ######################################################################### 2 | # Language operations 3 | 4 | # Tests are groups of three lines: program, input, expected output. 5 | # Blank lines and lines starting with # are ignored. 6 | ######################################################################### 7 | 8 | # fibstr 9 | include "word/language"; [limit(5; fibstr("a";"b"))] 10 | null 11 | ["a","b","ab","bab","abbab"] 12 | 13 | # fibstr 14 | include "word/language"; [limit(5; fibstr("a";"b"))] 15 | null 16 | ["a","b","ab","bab","abbab"] 17 | 18 | include "word/language"; power(0) 19 | [[1,2,3]] 20 | [] 21 | 22 | include "word/language"; power(0) 23 | ["123"] 24 | "" 25 | 26 | include "word/language"; power(1) 27 | ["123"] 28 | "123" 29 | 30 | include "word/language"; power(1) 31 | ["123","abc"] 32 | "123" 33 | "abc" 34 | 35 | include "word/language"; power(2) 36 | ["123"] 37 | "123123" 38 | 39 | include "word/language"; power(2) 40 | ["1","a"] 41 | "11" 42 | "1a" 43 | "a1" 44 | "aa" 45 | 46 | include "word/language"; power(1) 47 | [[1,2,3]] 48 | [1,2,3] 49 | 50 | include "word/language"; power(2) 51 | [[1,2]] 52 | [1,2,1,2] 53 | 54 | include "word/language"; power(2) 55 | [[1,2],[3,4]] 56 | [1,2,1,2] 57 | [1,2,3,4] 58 | [3,4,1,2] 59 | [3,4,3,4] 60 | 61 | include "word/language"; power(2) 62 | ["12","34"] 63 | "1212" 64 | "1234" 65 | "3412" 66 | "3434" 67 | 68 | include "word/language"; limit(1;star) 69 | ["123"] 70 | "" 71 | 72 | include "word/language"; limit(1;star) 73 | [[1,2,3]] 74 | [] 75 | 76 | include "word/language"; limit(5;star) 77 | ["a","b"] 78 | "" 79 | "a" 80 | "b" 81 | "aa" 82 | "ab" 83 | 84 | include "word/language"; limit(5;plus) 85 | ["a","b"] 86 | "a" 87 | "b" 88 | "aa" 89 | "ab" 90 | "ba" 91 | 92 | include "word/language"; limit(5;star) 93 | [[1],[2]] 94 | [] 95 | [1] 96 | [2] 97 | [1,1] 98 | [1,2] 99 | 100 | include "word/language"; limit(5;plus) 101 | [[1],[2]] 102 | [1] 103 | [2] 104 | [1,1] 105 | [1,2] 106 | [2,1] 107 | 108 | # vim:ai:sw=2:ts=2:et:syntax=jq 109 | -------------------------------------------------------------------------------- /tests/word_scanner.test: -------------------------------------------------------------------------------- 1 | ######################################################################### 2 | # Icon style scanner 3 | # 4 | # Tests are groups of three lines: program, input, expected output. 5 | # Blank lines and lines starting with # are ignored. 6 | ######################################################################### 7 | 8 | include "word/scanner"; meets([11,9,2,3]; .==[9]) 9 | 0 10 | # empty 11 | 12 | include "word/scanner"; meets([9,2,3]; .==[9]) 13 | 0 14 | 1 15 | 16 | include "word/scanner"; match([9,2,3]; [9]) 17 | 0 18 | 1 19 | 20 | include "word/scanner"; [upto("one motion is optional"; "on")] 21 | 0 22 | [0, 1, 5, 8, 9, 14, 18, 19] 23 | 24 | include "word/scanner"; [upto("kaleidoscope"; "aeiou")] 25 | 0 26 | [1, 3, 4, 6, 9, 11] 27 | 28 | 29 | include "word/scanner"; notany("Asperger"; "aeiou") 30 | 0 31 | 1 32 | 33 | include "word/scanner"; notany("Asperger"; "aeiou") 34 | 0 35 | 1 36 | 37 | include "word/scanner"; any("asperger"; "aeiou") 38 | 0 39 | 1 40 | 41 | include "word/scanner"; any("Asperger"; "aeiou") 42 | 0 43 | # empty 44 | 45 | include "word/scanner"; many_c("xxoops"; "aeiou") 46 | 0 47 | 2 48 | 49 | include "word/scanner"; many_c("axixo"; "aeiou") 50 | 0 51 | # empty 52 | 53 | include "word/scanner"; many("oops"; "aeiou") 54 | 0 55 | 2 56 | 57 | include "word/scanner"; many("xixo"; "aeiou") 58 | 0 59 | # empty 60 | 61 | ######################################################################### 62 | 63 | include "word/scanner"; [find("one motion is optional"; "on")] 64 | 0 65 | [0, 8, 18] 66 | 67 | include "word/scanner"; [find("mi mama me mima"; "ma")] 68 | 0 69 | [3, 5, 13] 70 | 71 | include "word/scanner"; [find("abaabbaaabbbaaaabbbb" ; "ab")] 72 | 0 73 | [0, 3, 8, 15] 74 | 75 | include "word/scanner"; [find("kaleidoscope"; "o")] 76 | 0 77 | [6, 9] 78 | 79 | # Factor, proper factor? 80 | 81 | include "word/scanner"; match("mi mama me mima"; "mi ma") 82 | 0 83 | 5 84 | 85 | include "word/scanner"; match("no se si mi mama me mima"; "mi ma") 86 | 9 87 | 14 88 | 89 | include "word/scanner"; match([1,2, 3,4,2, 5]; []) 90 | 0 91 | 0 92 | 93 | include "word/scanner"; match([1,2, 3,4,2, 5]; [1]) 94 | 0 95 | 1 96 | 97 | include "word/scanner"; match([]; [3,4,2]) 98 | 0 99 | # empty 100 | 101 | include "word/scanner"; find([1,2, 3,4,2, 5]; [3,4,2]) 102 | 0 103 | 2 104 | 105 | include "word/scanner"; match([3,4,2,1,2]; [3,4,2]) 106 | 0 107 | 3 108 | 109 | include "word/scanner"; find([2,1,3,3,4,2]; [3,4,2]) 110 | 0 111 | 3 112 | 113 | 114 | include "word/scanner"; . as $s | range(0;$s|length) | . as $i | bal($s; "("; ")") | . as $j | $s[$i:$j] 115 | "((A+(B*C))+D)" 116 | "((A+(B*C))+D)" 117 | "(A+(B*C))" 118 | "(A+(B*C))+" 119 | "(A+(B*C))+D" 120 | "A" 121 | "A+" 122 | "A+(B*C)" 123 | "+" 124 | "+(B*C)" 125 | "(B*C)" 126 | "B" 127 | "B*" 128 | "B*C" 129 | "*" 130 | "*C" 131 | "C" 132 | "+" 133 | "+D" 134 | "D" 135 | 136 | include "word/scanner"; [words_c("Hi there"; " \t")] 137 | 0 138 | ["Hi", "there"] 139 | 140 | include "word/scanner"; [words_c("Hi there "; " \t")] 141 | 0 142 | ["Hi", "there"] 143 | 144 | include "word/scanner"; [words_c(" Hi there"; " \t")] 145 | 0 146 | ["Hi", "there"] 147 | 148 | include "word/scanner"; [words_c("\t Hi \t there "; " \t")] 149 | 0 150 | ["Hi", "there"] 151 | 152 | include "word/scanner"; [words_c("a e i o u"; " \t")] 153 | 0 154 | ["a","e","i","o","u"] 155 | 156 | include "word/scanner"; [words_c(" \t "; " \t")] 157 | 0 158 | [] 159 | 160 | include "word/scanner"; [words("a e ae io aeiou o "; "aeiou")] 161 | 0 162 | ["a","e","ae","io","aeiou","o"] 163 | 164 | include "word/scanner"; [numbers("1000 lala 12 be 13.2 so 7. 7.77 lele10lolo-33.33")] 165 | 0 166 | [1e3, 12, 13.2, 7.0, 7.77, 10, -33.33] 167 | 168 | # vim:ai:sw=2:ts=2:et:syntax=jq 169 | --------------------------------------------------------------------------------