|
# https://github.com/stedolan/jq/wiki/Parsing-Expression-Grammars
|
|
|
|
<#
|
|
Sequence: e1 e2 e1 | e2
|
|
Ordered choice: e1 / e2 e1 // e2
|
|
Zero-or-more: e* star(E)
|
|
One-or-more: e+ plus(E)
|
|
Optional: e? optional(E)
|
|
And-predicate: &e amp(E)
|
|
Not-predicate: !e neg(E)
|
|
#>
|
|
|
|
# def star(E): (E | star(E)) // .;
|
|
|
|
function star {
|
|
param ($E)
|
|
|
|
process {
|
|
$x = $_ | & $E | star $E
|
|
if ($x) { $x } else { $_ }
|
|
}
|
|
}
|
|
|
|
# def plus(E): E | (plus(E) // . );
|
|
|
|
function plus {
|
|
param ($E)
|
|
|
|
process {
|
|
$x = $_ | & $E
|
|
if ($x) {
|
|
$y = $x | plus $E
|
|
if ($y) { $y } else { $x }
|
|
}
|
|
}
|
|
}
|
|
|
|
# def optional(E): E // .;
|
|
|
|
function optional {
|
|
param ($E)
|
|
|
|
process {
|
|
$x = $_ | & $E
|
|
if ($x) { $x } else { $_ }
|
|
}
|
|
}
|
|
|
|
# def amp(E): . as $in | E | $in;
|
|
|
|
function amp {
|
|
param ($E)
|
|
|
|
process {
|
|
$x = $_ | & $E
|
|
if ($x) { $_ }
|
|
}
|
|
}
|
|
|
|
# def neg(E): select( [E] == [] );
|
|
|
|
function neg {
|
|
param ($E)
|
|
|
|
process {
|
|
$x = $_ | & $E
|
|
if (-not $x) { $_ }
|
|
}
|
|
}
|
|
|
|
# def literal($s):
|
|
# select(.remainder | startswith($s))
|
|
# | .result += [$s]
|
|
# | .remainder |= .[$s | length :] ;
|
|
|
|
function literal {
|
|
param ([string]$s, [switch]$noresult)
|
|
|
|
process {
|
|
if ((($_.index + $s.Length) -le $_.input.Length) -and ($_.input.IndexOf($s, $_.index, $s.Length) -eq $_.index)) {
|
|
$r = $_.result
|
|
if (-not $noresult) { $r += $s }
|
|
@{ input = $_.input; index = $_.index + $s.Length; result = $r }
|
|
}
|
|
}
|
|
}
|
|
|
|
function match {
|
|
param([string]$re)
|
|
|
|
process {
|
|
if (($_.index -lt $_.input.Length) -and ($_.input.Substring($_.index) -match "^$re")) {
|
|
@{ input = $_.input; index = $_.index + $Matches[0].Length; result = $_.result + $Matches[0] }
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
# def nonempty: select( (.remainder | length) > 0 );
|
|
|
|
function nonempty {
|
|
process {
|
|
if ($_.input.Length -gt $_.index) {
|
|
$_
|
|
}
|
|
}
|
|
}
|
|
|
|
# def parse: {remainder: .} | S | .result;
|
|
|
|
function parse {
|
|
process {
|
|
@{ input = $_; index = 0; result = @() }
|
|
}
|
|
}
|
|
|
|
# def box(E):
|
|
# ((.result = null) | E) as $e
|
|
# | .remainder = $e.remainder
|
|
# | .result += [$e.result]; # the magic sauce
|
|
|
|
function box {
|
|
param ($E)
|
|
|
|
process {
|
|
$r = $_.result
|
|
$x = $_
|
|
$x.result = @()
|
|
$y = $x | & $E
|
|
if ($y) {
|
|
$y.result = $r + , $y.result
|
|
$y
|
|
}
|
|
}
|
|
}
|
|
|
|
<#
|
|
S ← &(A 'c') 'a'+ B !.
|
|
A ← 'a' A? 'b'
|
|
B ← 'b' B? 'c'
|
|
#>
|
|
|
|
# def A: literal("a") | optional(A) | literal("b");
|
|
function A {
|
|
process {
|
|
$_ | literal "a" | optional A | literal "b"
|
|
}
|
|
}
|
|
|
|
# def B: literal("b") | optional(B) | literal("c");
|
|
|
|
function B {
|
|
process {
|
|
$_ | literal "b" | optional B | literal "c"
|
|
}
|
|
}
|
|
|
|
# def S: amp(A | literal("c")) | plus(literal("a")) | B | neg(nonempty);
|
|
|
|
function S {
|
|
process {
|
|
$_ | amp { $_ | A | literal "c" } | plus { $_ | literal "a" } | B | neg { $_ | nonempty }
|
|
}
|
|
}
|
|
|
|
"aabbcc" | parse | S
|
|
"abc" | parse | S
|
|
"abbcc" | parse | S
|
|
|
|
# Expr ← Sum
|
|
# def Expr:
|
|
|
|
# Value ← [0-9]+ / '(' Expr ')'
|
|
# def Value: parseNumber( "[0-9]+" ) // (literal("(") | Expr | literal(")"));
|
|
# def Value: parseNumber( "[0-9]+" ) // (consume("[(]") | box(Expr) | consume("[)["));
|
|
|
|
function parseNumber {
|
|
param ([string]$re)
|
|
|
|
process {
|
|
$x = $_ | match $re
|
|
if ($x) {
|
|
$x.result[-1] = [int]$x.result[-1]
|
|
$x
|
|
}
|
|
}
|
|
}
|
|
|
|
function Value {
|
|
process {
|
|
$x = $_ | parseNumber "[0-9]+"
|
|
if ($x) { $x }
|
|
else {
|
|
$_ | literal "(" -noresult | box { $_ | Sum } | literal ")" -noresult
|
|
}
|
|
}
|
|
}
|
|
|
|
# Product ← Value (('*' / '/') Value)*
|
|
# def Product: (Value | star( (literal("*") // literal("/")) | Value));
|
|
|
|
function Product {
|
|
process {
|
|
$_ | Value | star { $_ | match "[*/]" | Value }
|
|
}
|
|
}
|
|
|
|
# Sum ← Product (('+' / '-') Product)*
|
|
# def Sum: (Product | star( (literal("+") // literal("-")) | Product));
|
|
|
|
function Sum {
|
|
process {
|
|
$_ | Product | star { $_ | match "[+-]" | Product }
|
|
}
|
|
}
|
|
|
|
# Sum ;
|
|
|
|
<#
|
|
def eval:
|
|
if type == "array" then
|
|
if length == 0 then null
|
|
else .[-1] |= eval
|
|
| if length == 1 then .[0]
|
|
else (.[:-2] | eval) as $v
|
|
| if .[-2] == "*" then $v * .[-1]
|
|
elif .[-2] == "/" then $v / .[-1]
|
|
elif .[-2] == "+" then $v + .[-1]
|
|
elif .[-2] == "-" then $v - .[-1]
|
|
else tostring|error
|
|
end
|
|
end
|
|
end
|
|
else .
|
|
end ;
|
|
#>
|
|
|
|
function eval {
|
|
param($r)
|
|
|
|
if ($r -is [array]) {
|
|
if ($r.Length -gt 0) {
|
|
$r[-1] = eval $r[-1]
|
|
}
|
|
|
|
if ($r.Length -eq 1) {
|
|
$r[0]
|
|
} else {
|
|
$v = eval $r[0..($r.Length - 3)]
|
|
switch ($r[-2]) {
|
|
"*" { $v * $r[-1] }
|
|
"/" { $v / $r[-1] }
|
|
"+" { $v + $r[-1] }
|
|
"-" { $v - $r[-1] }
|
|
}
|
|
}
|
|
} else {
|
|
$r
|
|
}
|
|
}
|
|
|
|
$p = "(1+2)*(3+4)+(1*1)" | parse | Sum
|
|
$p
|
|
eval $p.result
|
|
|
|
# def atoz: parse("[a-z]");
|
|
|
|
function atoz {
|
|
process {
|
|
$_ | match "[a-z]"
|
|
}
|
|
}
|
|
|
|
# def ing: literal("ing");
|
|
|
|
function ing {
|
|
process {
|
|
$_ | literal "ing"
|
|
}
|
|
}
|
|
|
|
# def Aplus_B(A; B): plus(neg(B) | A) | B;
|
|
|
|
function Aplus_B {
|
|
param ($A, $B)
|
|
|
|
process {
|
|
$_ | plus { $_ | neg $B | & $A } | & $B
|
|
}
|
|
}
|
|
|
|
# Aplus_B( atoz; ing )
|
|
|
|
"zxing" | parse | Aplus_B atoz ing
|