Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3622810ea7 |
No files matched your search
@@ -1,2 +0,0 @@
|
||||
.gitattributes export-ignore
|
||||
.gitignore export-ignore
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
*.o
|
||||
*~
|
||||
.*.sw[a-p]
|
||||
tags
|
||||
|
||||
jq
|
||||
jq.1
|
||||
|
||||
# Something delightfully recursive happens otherwise
|
||||
docs/content/2.download/source/*
|
||||
|
||||
# Autogenerated
|
||||
lexer.c
|
||||
lexer.h
|
||||
parser.c
|
||||
parser.h
|
||||
|
||||
# Autotools junk
|
||||
.deps
|
||||
*.log
|
||||
stamp-h1
|
||||
config.log
|
||||
config.status
|
||||
autom4te.cache
|
||||
config.h
|
||||
Makefile
|
||||
jq-*.tar.gz
|
||||
configure
|
||||
aclocal.m4
|
||||
Makefile.in
|
||||
version.h
|
||||
.remake-version-h
|
||||
config.cache
|
||||
@@ -1,6 +0,0 @@
|
||||
Created By:
|
||||
Stephen Dolan <mu@netsoc.tcd.ie>
|
||||
|
||||
Contributions by
|
||||
Anthony Shortland <anthony.shortland@me.com> - rpmbuild target
|
||||
Lee Thompson <stagr.lee@gmail.com> - autoconf stuff
|
||||
@@ -1,70 +0,0 @@
|
||||
jq is copyright (C) 2012 Stephen Dolan
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
|
||||
jq's documentation (everything found under the docs/ subdirectory in
|
||||
the source tree) is licensed under the Creative Commons CC BY 3.0
|
||||
license, which can be found at:
|
||||
|
||||
http://creativecommons.org/licenses/by/3.0/
|
||||
|
||||
The documentation website includes a copy of Twitter's Boostrap and
|
||||
relies on Bonsai, Liquid templates and various other projects, look
|
||||
them up for detailed licensing conditions.
|
||||
|
||||
|
||||
|
||||
jq incorporates David M. Gay's dtoa.c and g_fmt.c, which bear the
|
||||
following notices:
|
||||
|
||||
dtoa.c:
|
||||
The author of this software is David M. Gay.
|
||||
|
||||
Copyright (c) 1991, 2000, 2001 by Lucent Technologies.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose without fee is hereby granted, provided that this entire notice
|
||||
is included in all copies of any software which is or includes a copy
|
||||
or modification of this software and in all copies of the supporting
|
||||
documentation for such software.
|
||||
|
||||
THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
|
||||
WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY
|
||||
REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
|
||||
OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
|
||||
|
||||
g_fmt.c:
|
||||
The author of this software is David M. Gay.
|
||||
|
||||
Copyright (c) 1991, 1996 by Lucent Technologies.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose without fee is hereby granted, provided that this entire notice
|
||||
is included in all copies of any software which is or includes a copy
|
||||
or modification of this software and in all copies of the supporting
|
||||
documentation for such software.
|
||||
|
||||
THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
|
||||
WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY
|
||||
REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
|
||||
OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
|
||||
|
||||
@@ -0,0 +1,304 @@
|
||||
module JQ where
|
||||
import Text.JSON
|
||||
import Text.JSON.String
|
||||
import Data.Maybe
|
||||
import Data.Char
|
||||
import Data.List (sortBy,sort,groupBy,partition,intercalate)
|
||||
import Data.Function (on)
|
||||
import Data.Ord (comparing)
|
||||
import Control.Monad
|
||||
import Control.Monad.Writer
|
||||
import Control.Monad.List
|
||||
import Control.Monad.Reader
|
||||
import qualified Data.Map as M
|
||||
import qualified Data.Set as S
|
||||
import Data.Map ((!))
|
||||
import Debug.Trace
|
||||
|
||||
type Path = [Either Int String]
|
||||
|
||||
type Program = JSValue -> [(JSValue, Path)]
|
||||
|
||||
type Filter = JQ JSValue
|
||||
newtype Operator = Operator {runOperator:: [Filter] -> Filter}
|
||||
|
||||
type JQ = ReaderT (JSValue, M.Map String JSValue, M.Map (String, Int) Operator) (WriterT Path [])
|
||||
|
||||
runJQ :: JQ a -> JSValue -> [a]
|
||||
runJQ prog val = map fst $ runWriterT $ runReaderT prog (val,M.empty,primitives)
|
||||
|
||||
(>|) :: JQ JSValue -> JQ a -> JQ a
|
||||
a >| b = do
|
||||
val <- a
|
||||
local (\(v,s,d) -> (val,s,d)) b
|
||||
|
||||
setvar name val prog =
|
||||
local (\(v,s,d) -> (v, M.insert name val s, d)) prog
|
||||
getvar name = liftM (! name) $ asks (\(v,s,d) -> s)
|
||||
|
||||
input = asks (\(v,s,d) -> v)
|
||||
|
||||
collect :: JQ a -> JQ [a]
|
||||
collect prog = liftM (map fst) $ collectPaths prog
|
||||
|
||||
collectPaths :: JQ a -> JQ [(a,Path)]
|
||||
collectPaths prog = do
|
||||
rd <- ask
|
||||
return $ runWriterT $ runReaderT prog rd
|
||||
|
||||
withDefn :: (String, [String], Filter) -> Filter -> Filter
|
||||
withDefn (name, formals, body) subexp =
|
||||
local (\(v,s,d) -> (v,s,M.insert (name,length formals) (Operator func) d)) subexp
|
||||
where
|
||||
func args = local (\(v,s,d) -> (v,M.empty,M.fromList (zip (zip formals (repeat 0)) (map (Operator . const) args)) `M.union` d)) body
|
||||
|
||||
subexp :: JQ a -> JQ a
|
||||
subexp = censor $ const []
|
||||
|
||||
|
||||
yieldPaths :: [(a,Path)] -> JQ a
|
||||
yieldPaths ps = ReaderT $ const $ WriterT ps
|
||||
|
||||
|
||||
insert :: JQ JSValue -> JSValue -> Path -> JQ JSValue
|
||||
insert replace base [] = replace
|
||||
insert replace (JSArray values) ((Left n):rest) = do
|
||||
let array = take (max (n+1) (length values)) (values ++ repeat JSNull)
|
||||
replacement <- insert replace (array !! n) rest
|
||||
let (left, (_:right)) = splitAt n array
|
||||
return $ JSArray $ left ++ [replacement] ++ right
|
||||
insert replace (JSObject obj) ((Right k):rest) = do
|
||||
let oldval = maybe JSNull id (lookup k $ fromJSObject obj)
|
||||
replacement <- insert replace oldval rest
|
||||
let withoutK = filter ((/= k) . fst) $ fromJSObject obj
|
||||
return $ JSObject $ toJSObject $ (k, replacement):withoutK
|
||||
|
||||
insert replace JSNull p@((Right k):rest) = insert replace (JSObject $ toJSObject []) p
|
||||
insert replace JSNull p@((Left n):rest) = insert replace (JSArray []) p
|
||||
insert _ base p = error $ "Cannot insert into " ++ intercalate ", " (map (either show show) p) ++ " of " ++ encode base
|
||||
|
||||
|
||||
eqj a b = JSBool $ a == b
|
||||
|
||||
boolj (JSBool false) = False
|
||||
boolj (JSNull) = False
|
||||
boolj _ = True
|
||||
|
||||
andj a b = JSBool $ boolj a && boolj b
|
||||
orj a b = JSBool $ boolj a || boolj b
|
||||
|
||||
liftp :: (JSValue -> JSValue) -> JQ JSValue
|
||||
liftp f = liftM f input
|
||||
|
||||
idp = undefined
|
||||
failp t = []
|
||||
|
||||
constp :: JSValue -> Program
|
||||
constp t t' = idp t
|
||||
|
||||
anyj :: [JSValue] -> Bool
|
||||
anyj values = any isTrue values
|
||||
where
|
||||
isTrue (JSBool False) = False
|
||||
isTrue (JSNull) = False
|
||||
isTrue _ = True
|
||||
|
||||
selectp prog = do
|
||||
match <- collect prog
|
||||
guard $ anyj match
|
||||
input
|
||||
|
||||
constStr :: String -> JQ JSValue
|
||||
constStr = return . JSString . toJSString
|
||||
|
||||
constInt :: Int -> JQ JSValue
|
||||
constInt = return . JSRational False . toRational
|
||||
|
||||
tr x = trace (show x) x
|
||||
|
||||
|
||||
assignp sel replace = do
|
||||
paths <- collectPaths sel
|
||||
t <- input
|
||||
foldM (\base (val,path) -> insert (return val >| replace) base path) t paths
|
||||
|
||||
arrayp prog = liftM JSArray $ collect prog
|
||||
|
||||
|
||||
childp' :: JSValue -> JQ JSValue
|
||||
childp' (JSArray values) = msum [tell [Left i] >> return v | (v,i) <- zip values [0..]]
|
||||
childp' (JSObject obj) = msum [tell [Right k] >> return v | (k,v) <- fromJSObject obj]
|
||||
childp' _ = mzero
|
||||
|
||||
childp = input >>= childp'
|
||||
|
||||
--findp :: Program -> Program
|
||||
findp prog = do
|
||||
found <- collect prog
|
||||
if anyj found then input else childp >| findp prog
|
||||
|
||||
groupp prog = do
|
||||
list <- input
|
||||
case list of
|
||||
JSArray values -> do
|
||||
marked <- forM values $ \v -> do
|
||||
m <- collect (return v >| prog)
|
||||
return (m,v)
|
||||
msum $
|
||||
map (return . JSArray . map snd) $
|
||||
groupBy ((==) `on` fst) $
|
||||
sortBy (comparing fst) $
|
||||
marked
|
||||
_ -> return JSNull
|
||||
|
||||
recp prog = do
|
||||
found <- collectPaths prog
|
||||
let (roots,subs) = partition (null . snd) found
|
||||
msum $
|
||||
[tell p >> return x | (x,p) <- roots] ++
|
||||
[tell p >> (return x >| recp prog) | (x,p) <- subs]
|
||||
|
||||
elsep p1 p2 = do
|
||||
p1' <- collectPaths p1
|
||||
if null p1' then p2 else yieldPaths p1'
|
||||
|
||||
fullresultp prog = do
|
||||
res <- collectPaths prog
|
||||
msum [return $ JSObject $ toJSObject $ [("val",a),("path",JSArray $ map fromPath p)] | (a,p) <- res]
|
||||
where
|
||||
fromPath (Left n) = js n
|
||||
fromPath (Right s) = js s
|
||||
|
||||
|
||||
withArray f (JSArray values) = JSArray $ f values
|
||||
withArray f x = x
|
||||
|
||||
withString f (JSString str) = JSString $ toJSString $ f $ fromJSString str
|
||||
withString f x = x
|
||||
|
||||
{-
|
||||
callp "select" [p] = selectp p
|
||||
callp "find" [p] = findp p
|
||||
callp "sort" [] = liftp (withArray sort)
|
||||
callp "group" [p] = groupp p
|
||||
callp "rec" [p] = recp p
|
||||
callp "empty" [] = mzero
|
||||
callp "true" [] = return $ JSBool True
|
||||
callp "false" [] = return $ JSBool False
|
||||
callp "null" [] = return $ JSNull
|
||||
callp "count" [] = liftp countj
|
||||
callp "fullresult" [p] = fullresultp p
|
||||
callp "uppercase" [] = liftp $ withString $ map toUpper
|
||||
callp "lowercase" [] = liftp $ withString $ map toLower
|
||||
-}
|
||||
|
||||
primitives = M.fromList [((name,arglen),Operator func) |
|
||||
(name,arglen,func) <- prim]
|
||||
where
|
||||
prim = [("if",1,\[p] -> selectp p),
|
||||
("find", 1, \[p] -> findp p),
|
||||
("group", 1, \[p] -> groupp p),
|
||||
("rec", 1, \[p] -> recp p),
|
||||
("true", 0, const $ return $ JSBool True),
|
||||
("false", 0, const $ return $ JSBool False),
|
||||
("null", 0, const $ return $ JSNull),
|
||||
("count", 0, const $ liftp countj),
|
||||
("fullresult", 1, \[p] -> fullresultp p),
|
||||
("zip", 0, const $ liftp zipj),
|
||||
("keys", 0, const $ liftp keysj)
|
||||
]
|
||||
|
||||
callp :: String -> [Filter] -> Filter
|
||||
|
||||
callp name args = do
|
||||
(v,s,d) <- ask
|
||||
runOperator (d ! (name, length args)) args
|
||||
|
||||
countj (JSArray v) = js$ length v
|
||||
countj (JSObject o) = js$ length $ fromJSObject o
|
||||
countj _ = js$ (1::Int)
|
||||
|
||||
lookupj :: JSValue -> JSValue -> JQ JSValue
|
||||
lookupj (JSArray values) (JSRational _ n) = do
|
||||
let idx = round n
|
||||
tell [Left idx]
|
||||
if idx >= 0 && idx < length values
|
||||
then return $ values !! idx
|
||||
else return $ JSNull
|
||||
lookupj (JSObject obj) (JSString s) = do
|
||||
tell [Right (fromJSString s)]
|
||||
case (lookup (fromJSString s) (fromJSObject obj)) of
|
||||
Just x -> return x
|
||||
Nothing -> return JSNull
|
||||
lookupj JSNull (JSRational _ n) = do
|
||||
tell [Left $ round n]
|
||||
return JSNull
|
||||
lookupj JSNull (JSString s) = do
|
||||
tell [Right (fromJSString s)]
|
||||
return JSNull
|
||||
--lookupj v i = error $ "Cannot get element " ++ encode i ++ " of " ++ encode v
|
||||
lookupj _ _ = mzero
|
||||
|
||||
|
||||
plusj (JSRational f1 n1) (JSRational f2 n2) = JSRational (f1 || f2) (n1 + n2)
|
||||
plusj (JSString s1) (JSString s2) = JSString $ toJSString (fromJSString s1 ++ fromJSString s2)
|
||||
plusj (JSArray a1) (JSArray a2) = JSArray $ a1 ++ a2
|
||||
plusj (JSObject o1) (JSObject o2) = JSObject $ toJSObject $ o1' ++ fromJSObject o2
|
||||
where
|
||||
newkeys = map fst $ fromJSObject o2
|
||||
o1' = filter (not . (`elem` newkeys) . fst) $ fromJSObject o1
|
||||
|
||||
keysj (JSArray v) = js [0..length v - 1]
|
||||
keysj (JSObject obj) = js (map fst $ fromJSObject obj)
|
||||
keysj _ = JSArray []
|
||||
|
||||
zipj jsonValue = result $ tx values ctor
|
||||
where
|
||||
(result, values) = extract packed
|
||||
(packed, ctor) = case jsonValue of
|
||||
JSArray values -> (values, (\vs' -> JSArray $ [v' | Just v' <- vs']))
|
||||
JSObject jsObject ->
|
||||
let object = fromJSObject jsObject
|
||||
keys = map fst object
|
||||
values = map snd object
|
||||
build vs' = [(k,v') | (k,Just v') <- zip keys vs']
|
||||
in (values, JSObject . toJSObject . build)
|
||||
|
||||
_ -> error "only arrays and objects may be zipped"
|
||||
|
||||
extract values | all isArray values = (JSArray, [map Just arr | JSArray arr <- values])
|
||||
| all isObject values =
|
||||
let objects = [fromJSObject o | JSObject o <- values]
|
||||
keys = S.toList $ S.fromList [k | obj <- objects, (k,_) <- obj]
|
||||
values' :: [[Maybe JSValue]]
|
||||
values' = [[lookup k object | k <- keys] | object <- objects]
|
||||
result r = JSObject $ toJSObject $ zip keys r
|
||||
in (result, values')
|
||||
| otherwise = error "elements of zipped value must be all objects or all arrays"
|
||||
where
|
||||
isArray (JSArray a) = True
|
||||
isArray _ = False
|
||||
isObject (JSObject o) = True
|
||||
isObject _ = False
|
||||
|
||||
head' [] = Nothing
|
||||
head' (x:xs) = x
|
||||
tail' [] = []
|
||||
tail' (x:xs) = xs
|
||||
tx values ctor | all null values = []
|
||||
| otherwise = ctor (map head' values):tx (map tail' values) ctor
|
||||
|
||||
js :: JSON a => a -> JSValue
|
||||
js = showJSON
|
||||
|
||||
index s = do
|
||||
v <- input
|
||||
lookupj v (js s)
|
||||
|
||||
|
||||
dictp progs = do
|
||||
liftM (JSObject . toJSObject) $ forM progs $ \(k,v) -> do
|
||||
JSString k' <- subexp k
|
||||
v' <- subexp v
|
||||
return (fromJSString k', v')
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
{
|
||||
module Lexer where
|
||||
import Control.Monad.Error
|
||||
}
|
||||
|
||||
%wrapper "monadUserState"
|
||||
|
||||
$digit = 0-9
|
||||
$alpha = [a-zA-Z_]
|
||||
@reserved = "."|"["|"]"|","|":"|"("|")"|"{"|"}"|"|"|"=="|"+"|"="|"$"|"def"|";"|"else"|"and"|"or"|"as"
|
||||
@ident = $alpha [$alpha $digit]*
|
||||
@string = \" ($printable)* \"
|
||||
|
||||
|
||||
tokens :-
|
||||
|
||||
<0> "#" ($printable # [\n\r])* ;
|
||||
<0> $white+ ;
|
||||
<0> @reserved { tok TRes }
|
||||
<0> @ident { tok TIdent }
|
||||
<0> $digit+ { tok $ TInt . read }
|
||||
|
||||
|
||||
<0> \" { enterString }
|
||||
<string> \" { leaveString }
|
||||
<string> ($printable # [\"\\]) { pushString id }
|
||||
<string> \\ [\"\\\/] { pushString (drop 1) }
|
||||
<string> \\ [nrt] { pushString (escape . drop 1) }
|
||||
--<string> \\ 'u' [0-9a-fA-F]{4}
|
||||
-- { pushString (parseUnicode . drop 2) }
|
||||
|
||||
-- @string { \s -> TString $ init $ tail s}
|
||||
|
||||
|
||||
{
|
||||
|
||||
escape :: String -> String
|
||||
escape "r" = "\r"
|
||||
escape "n" = "\n"
|
||||
escape "t" = "\t"
|
||||
|
||||
getState :: Alex AlexState
|
||||
getState = Alex $ \s -> Right (s, s)
|
||||
|
||||
getUserState :: Alex AlexUserState
|
||||
getUserState = liftM alex_ust getState
|
||||
|
||||
setUserState :: AlexUserState -> Alex ()
|
||||
setUserState s' = Alex $ \s -> Right (s{alex_ust = s'}, ())
|
||||
|
||||
alexEOF = return $ Nothing
|
||||
|
||||
enterString input len = do
|
||||
setUserState []
|
||||
alexSetStartCode string
|
||||
skip input len
|
||||
|
||||
pushString f i@(p, _, s) len = do
|
||||
buf <- getUserState
|
||||
setUserState (buf ++ [f $ take len s])
|
||||
skip i len
|
||||
|
||||
leaveString input len = do
|
||||
s <- getUserState
|
||||
alexSetStartCode 0
|
||||
return $ Just $ TString $ concat s
|
||||
|
||||
|
||||
tok f (p,_,s) len = return $ Just $ f (take len s)
|
||||
data Token = TRes String | TString String | TIdent String | TInt Int
|
||||
|
||||
instance Show Token where
|
||||
show (TRes t) = "token " ++ t
|
||||
show (TString t) = "string " ++ t
|
||||
show (TIdent t) = "identifier " ++ t
|
||||
show (TInt t) = "integer " ++ show t
|
||||
|
||||
|
||||
type AlexUserState = [String]
|
||||
|
||||
alexInitUserState = undefined
|
||||
|
||||
wrapError (Alex scanner) = Alex $ \s -> case scanner s of
|
||||
Left message -> Left (message ++ " at " ++ showpos (alex_pos s))
|
||||
where
|
||||
showpos (AlexPn off line col) = "line " ++ show line ++ ", column " ++ show col
|
||||
x -> x
|
||||
|
||||
scanner = do
|
||||
tok <- wrapError alexMonadScan
|
||||
case tok of
|
||||
Nothing -> do
|
||||
s <- getState
|
||||
case alex_scd s of
|
||||
0 -> return []
|
||||
string -> alexError "Unterminated string literal"
|
||||
Just tok -> liftM (tok:) scanner
|
||||
|
||||
runLexer :: String -> Either String [Token]
|
||||
runLexer input = runAlex input scanner
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import Parser
|
||||
import Lexer
|
||||
import JQ
|
||||
import Text.JSON
|
||||
import Text.JSON.String
|
||||
import PrettyJSON
|
||||
import System.Environment
|
||||
import Control.Monad
|
||||
import System.IO
|
||||
|
||||
parseJS :: String -> JSValue
|
||||
parseJS s = case runGetJSON readJSValue s of
|
||||
Left err -> error err
|
||||
Right val -> val
|
||||
|
||||
|
||||
main = do
|
||||
[program] <- getArgs
|
||||
stdlib <- openFile "stdlib.jq" ReadMode >>= hGetContents
|
||||
json <- liftM parseJS $ hGetContents stdin
|
||||
case runLexer (stdlib ++ program) >>= runParser of
|
||||
Left err -> putStrLn err
|
||||
Right program -> mapM_ (putStrLn . show . renderJSON) (runJQ program json)
|
||||
@@ -0,0 +1,8 @@
|
||||
jq: *.hs Parser.hs Lexer.hs
|
||||
ghc *.hs -o jq
|
||||
|
||||
Parser.hs: Parser.y
|
||||
happy -i Parser.y
|
||||
|
||||
Lexer.hs: Lexer.x
|
||||
alex Lexer.x
|
||||
-134
@@ -1,134 +0,0 @@
|
||||
### C source files to be built and distributed.
|
||||
|
||||
JQ_INCS = jq_parser.h builtin.h bytecode.h compile.h execute.h \
|
||||
forkable_stack.h frame_layout.h jv.h jv_alloc.h jv_aux.h jv_dtoa.h \
|
||||
jv_file.h jv_parse.h jv_unicode.h locfile.h opcode_list.h parser.y \
|
||||
jv_utf8_tables.h lexer.l
|
||||
|
||||
JQ_SRC = locfile.c bytecode.c compile.c execute.c builtin.c jv.c \
|
||||
jv_parse.c jv_print.c jv_dtoa.c jv_unicode.c jv_aux.c jv_file.c \
|
||||
jv_alloc.c jq_test.c ${JQ_INCS}
|
||||
|
||||
|
||||
### C build options
|
||||
|
||||
AM_CFLAGS = -Wextra -Wall -Wno-missing-field-initializers \
|
||||
-Wno-unused-parameter -Wno-unused-function
|
||||
|
||||
|
||||
### Generating the lexer and parser
|
||||
|
||||
# While there is some autoconf macro support for lex/flex, it doesn't support
|
||||
# header file creation so we'll use good old make
|
||||
BUILT_SOURCES = lexer.h lexer.c parser.h parser.c version.h
|
||||
lexer.c: lexer.l
|
||||
$(AM_V_LEX) flex -o lexer.c --header-file=lexer.h $<
|
||||
lexer.h: lexer.c
|
||||
|
||||
# Tell YACC (bison) autoconf macros that you want a header file created.
|
||||
# If the --warnings=all fails, you probably have an old version of bison
|
||||
# OSX ships an old bison, so update with homebrew or macports
|
||||
AM_YFLAGS = --warnings=all -d
|
||||
|
||||
|
||||
### Building the jq binary
|
||||
|
||||
# Remake the version.h header file if, and only if, the git ID has changed
|
||||
.PHONY: .FORCE
|
||||
.FORCE:
|
||||
generate_ver = ver="`{ $(srcdir)/scripts/version || echo '$(VERSION)' ; } | sed 's/.*/\#define JQ_VERSION \"&\"/'`"
|
||||
.remake-version-h: .FORCE
|
||||
@ $(generate_ver); test "x`cat version.h 2>/dev/null`" = "x$$ver" || touch .remake-version-h
|
||||
version.h: .remake-version-h
|
||||
$(AM_V_GEN) $(generate_ver); echo "$$ver" > $@
|
||||
|
||||
bin_PROGRAMS = jq
|
||||
jq_SOURCES = ${JQ_SRC} main.c
|
||||
|
||||
### Tests (make check)
|
||||
|
||||
TESTS = tests/all.test
|
||||
TEST_LOG_COMPILER = ${srcdir}/tests/run
|
||||
|
||||
|
||||
### Building the manpage
|
||||
|
||||
# If ENABLE_DOCS is not set, just copy jq.1.default to jq.1
|
||||
# The real_docs target succeeds (possibly after building jq.1) only if ENABLE_DOCS is set
|
||||
# Thus, making "dist" depend on "real_docs" ensures that we never build a tarball with
|
||||
# a stub manpage.
|
||||
|
||||
man_MANS = jq.1
|
||||
.PHONY: real_docs
|
||||
if ENABLE_DOCS
|
||||
jq.1: $(srcdir)/docs/content/3.manual/manual.yml
|
||||
$(AM_V_GEN) ( cd ${abs_srcdir}/docs; '$(BUNDLER)' exec rake manpage ) > $@ || { rm -f $@; false; }
|
||||
jq.1.default: $(srcdir)/docs/default_manpage.md
|
||||
$(AM_V_GEN) ( cd ${abs_srcdir}/docs; '$(BUNDLER)' exec rake manpage_default ) > $@ || { rm -f $@; false; }
|
||||
real_docs: jq.1
|
||||
if cmp jq.1 $(srcdir)/jq.1.default > /dev/null; then\
|
||||
rm -f jq.1; $(MAKE) $(AM_MAKEFLAGS) jq.1;\
|
||||
fi
|
||||
else
|
||||
jq.1: $(srcdir)/jq.1.default
|
||||
$(AM_V_GEN) cp $^ $@
|
||||
real_docs:
|
||||
@echo "Ruby dependencies not found, cannot build manpage." > /dev/stderr
|
||||
@echo "Follow the instructions in docs/README.md to install them" > /dev/stderr
|
||||
@echo "and then rerun ./configure" > /dev/stderr
|
||||
false
|
||||
endif
|
||||
|
||||
|
||||
### Packaging
|
||||
|
||||
docs/site.yml: configure.ac
|
||||
sed 's/^jq_version: .*/jq_version: "$(VERSION)"/' $@ > $@.new
|
||||
mv $@.new $@
|
||||
|
||||
install-binaries: $(BUILT_SOURCES)
|
||||
$(MAKE) $(AM_MAKEFLAGS) install-exec
|
||||
|
||||
# Ensure "make dist" fails when we can't build the real manpage
|
||||
dist-hook: real_docs
|
||||
|
||||
DOC_FILES = docs/content docs/public docs/templates docs/site.yml \
|
||||
docs/Gemfile docs/Gemfile.lock docs/Rakefile docs/README.md \
|
||||
docs/default_manpage.md jq.1.default
|
||||
|
||||
# setup is only used by distribution developers, not package developers.
|
||||
# Still, as a matter of allowing patching, its not a bad idea to distribute
|
||||
# the developer setup script in the tarball.
|
||||
EXTRA_DIST = config.h.in $(man_MANS) $(TESTS) \
|
||||
$(TEST_LOG_COMPILER) gen_utf8_tables.py jq.spec \
|
||||
$(DOC_FILES) scripts/version \
|
||||
parser.h parser.c lexer.h lexer.c
|
||||
|
||||
# README.md is expected in Github projects, good stuff in it, so we'll
|
||||
# distribute it and install it with the package in the doc directory.
|
||||
docdir = ${datadir}/doc/${PACKAGE}
|
||||
dist_doc_DATA = README.md COPYING AUTHORS README
|
||||
|
||||
RELEASE ?= 1
|
||||
rpm: jq
|
||||
@echo "Packaging jq as an RPM ..."
|
||||
mkdir -p rpm/SOURCES rpm/BUILD rpm/BUILDROOT rpm/RPMS rpm/SPECS
|
||||
cp jq-$$(cat VERSION).tar.gz rpm/SOURCES/
|
||||
rpmbuild -tb --define "_topdir ${PWD}/rpm" --define "_prefix /usr" --define "myver $$(cat VERSION)" --define "myrel ${RELEASE}" rpm/SOURCES/jq-$$(cat VERSION).tar.gz
|
||||
find rpm/RPMS/ -name "*.rpm" -exec mv {} ./ \;
|
||||
rm -rf rpm
|
||||
|
||||
dist-clean-local:
|
||||
rm -f ${BUILT_SOURCES}
|
||||
|
||||
# Not sure why this doesn't get cleaned up automatically, guess
|
||||
# automake used to man pages which are hand coded?
|
||||
# 'make clean' doesn't delete the manpage if it can't be rebuilt
|
||||
.PHONY: clean-local-docs
|
||||
clean-local-docs:
|
||||
if ENABLE_DOCS
|
||||
rm -f jq.1
|
||||
endif
|
||||
|
||||
clean-local: clean-local-docs
|
||||
rm -f version.h .remake-version-h
|
||||
@@ -0,0 +1,121 @@
|
||||
{
|
||||
module Parser where
|
||||
import Lexer
|
||||
import JQ
|
||||
import Text.JSON
|
||||
import Debug.Trace
|
||||
import Data.List
|
||||
import Control.Monad.Error
|
||||
import Control.Monad.Reader
|
||||
|
||||
instance Error (Maybe a) where
|
||||
noMsg = Nothing
|
||||
strMsg = const Nothing
|
||||
|
||||
instance (Error a, Error b) => Error (a, b) where
|
||||
noMsg = (noMsg, noMsg)
|
||||
strMsg s = (strMsg s, strMsg s)
|
||||
}
|
||||
|
||||
%name runParser TopLevel
|
||||
%tokentype { Token }
|
||||
|
||||
%monad { Either String }
|
||||
%error { \t -> throwError $ "parse error: unexpected " ++ if null t then [] else (show $ t) }
|
||||
|
||||
%token
|
||||
'|' { TRes "|" }
|
||||
'.' { TRes "." }
|
||||
'[' { TRes "[" }
|
||||
']' { TRes "]" }
|
||||
'{' { TRes "{" }
|
||||
'}' { TRes "}" }
|
||||
'(' { TRes "(" }
|
||||
')' { TRes ")" }
|
||||
'$' { TRes "$" }
|
||||
'as' { TRes "as" }
|
||||
',' { TRes "," }
|
||||
':' { TRes ":" }
|
||||
'==' { TRes "==" }
|
||||
'=' { TRes "=" }
|
||||
'+' { TRes "+" }
|
||||
'def' { TRes "def" }
|
||||
';' { TRes ";" }
|
||||
'else' { TRes "else" }
|
||||
'and' { TRes "and" }
|
||||
'or' { TRes "or" }
|
||||
Ident { TIdent $$ }
|
||||
String { TString $$ }
|
||||
Int { TInt $$ }
|
||||
|
||||
|
||||
%left 'else'
|
||||
%right '|'
|
||||
%left '='
|
||||
%left ','
|
||||
%left 'and' 'or'
|
||||
%nonassoc '=='
|
||||
%left '+'
|
||||
|
||||
%%
|
||||
|
||||
TopLevel
|
||||
: Defn TopLevel { withDefn $1 $2 }
|
||||
| Exp { $1 }
|
||||
|
||||
Exp
|
||||
: Exp 'else' Exp { $1 `elsep` $3 }
|
||||
| Assign '|' Exp { do { v <- snd $1; setvar (fst $1) v $3 } }
|
||||
| Exp '|' Exp { $1 >| $3 }
|
||||
| Exp ',' Exp { $1 `mplus` $3 }
|
||||
| Exp 'and' Exp { liftM2 andj $1 $3 }
|
||||
| Exp 'or' Exp { liftM2 orj $1 $3 }
|
||||
| Exp '=' Exp { assignp $1 $3 }
|
||||
| Exp '==' Exp { liftM2 eqj (subexp $1) (subexp $3) }
|
||||
| Exp '+' Exp { liftM2 plusj (subexp $1) (subexp $3) }
|
||||
| Term { $1 }
|
||||
|
||||
Assign
|
||||
: Term 'as' '$' Ident { ($4, $1) }
|
||||
|
||||
Defn
|
||||
: 'def' Ident '=' Exp ';' { ($2, [], $4) }
|
||||
| 'def' Ident '(' ParamList ')' '=' Exp ';' { ($2, $4, $7) }
|
||||
|
||||
ParamList
|
||||
: { [] }
|
||||
| Ident { [$1] }
|
||||
| Ident ';' ParamList { $1:$3 }
|
||||
|
||||
ExpD
|
||||
: ExpD '|' ExpD { $1 >| $3 }
|
||||
| ExpD '==' ExpD { liftM2 eqj $1 $3 }
|
||||
| Term { $1 }
|
||||
|
||||
|
||||
Term
|
||||
: '.' { input }
|
||||
| Term '.' Ident { $1 >| index $3 }
|
||||
| '.' Ident { index $2 }
|
||||
| String { constStr $1 }
|
||||
| Term '[' Exp ']' { do {t <- $1; i <- subexp $3; lookupj t i} }
|
||||
| Term '[' ']' { $1 >| childp }
|
||||
| '(' Exp ')' { $2 }
|
||||
| '[' Exp ']' { arrayp $2 }
|
||||
| '[' ']' { arrayp (callp "empty" []) }
|
||||
| Int { constInt $1 }
|
||||
| '{' MkDict '}' { dictp $2 }
|
||||
| Ident '(' Exp ')' { callp $1 [$3] }
|
||||
| Ident { callp $1 [] }
|
||||
| '$' Ident { getvar $2 }
|
||||
|
||||
MkDict
|
||||
: { [] }
|
||||
| MkDictPair { [$1] }
|
||||
| MkDictPair ',' MkDict { $1:$3 }
|
||||
|
||||
MkDictPair
|
||||
: Ident ':' ExpD { (constStr $1, $3) }
|
||||
| Ident { (constStr $1, index $1) }
|
||||
| String ':' ExpD { (constStr $1, $3) }
|
||||
| '(' Exp ')' ':' ExpD{ ($2, $5) }
|
||||
@@ -0,0 +1,12 @@
|
||||
module PrettyJSON where
|
||||
import Text.JSON
|
||||
import Text.PrettyPrint
|
||||
|
||||
renderJSON (JSArray vals) = brackets $ fsep $ punctuate comma $ map renderJSON vals
|
||||
|
||||
renderJSON (JSObject jsObject) =
|
||||
let object = fromJSObject jsObject
|
||||
in braces $ fsep $ punctuate comma $
|
||||
[hang (renderJSON (JSString $ toJSString $ k) <> colon) 2 (renderJSON v)
|
||||
| (k,v) <- object]
|
||||
renderJSON x = text $ encode x
|
||||
@@ -1,28 +0,0 @@
|
||||
jq
|
||||
==
|
||||
|
||||
jq is a command-line JSON processor.
|
||||
|
||||
If you want to learn to use jq, read the documentation at
|
||||
[http://stedolan.github.io/jq](http://stedolan.github.io/jq). This
|
||||
documentation is generated from the docs/ folder of this repository.
|
||||
|
||||
If you want to hack on jq, feel free, but be warned that its internals
|
||||
are not well-documented at the moment. Bring a hard hat and a
|
||||
shovel. Also, read the wiki: http://github.com/stedolan/jq/wiki
|
||||
|
||||
If you're building directly from the latest git, you'll need flex and
|
||||
bison installed. To build, run:
|
||||
|
||||
autoreconf
|
||||
./configure
|
||||
make
|
||||
|
||||
After make finishes, you'll be able to use `./jq`. You can also
|
||||
install it using:
|
||||
|
||||
sudo make install
|
||||
|
||||
If you're not using the latest git version but instead building a
|
||||
released tarball (available on the website), then you won't need to
|
||||
run `autoreconf` (and shouldn't), and you won't need flex or bison.
|
||||
@@ -1 +0,0 @@
|
||||
*
|
||||
@@ -1,593 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "builtin.h"
|
||||
#include "compile.h"
|
||||
#include "jq_parser.h"
|
||||
#include "bytecode.h"
|
||||
#include "locfile.h"
|
||||
#include "jv_aux.h"
|
||||
#include "jv_file.h"
|
||||
#include "jv_unicode.h"
|
||||
|
||||
|
||||
static jv type_error(jv bad, const char* msg) {
|
||||
jv err = jv_invalid_with_msg(jv_string_fmt("%s %s",
|
||||
jv_kind_name(jv_get_kind(bad)),
|
||||
msg));
|
||||
jv_free(bad);
|
||||
return err;
|
||||
}
|
||||
|
||||
static jv type_error2(jv bad1, jv bad2, const char* msg) {
|
||||
jv err = jv_invalid_with_msg(jv_string_fmt("%s and %s %s",
|
||||
jv_kind_name(jv_get_kind(bad1)),
|
||||
jv_kind_name(jv_get_kind(bad2)),
|
||||
msg));
|
||||
jv_free(bad1);
|
||||
jv_free(bad2);
|
||||
return err;
|
||||
}
|
||||
|
||||
static jv f_plus(jv input, jv a, jv b) {
|
||||
jv_free(input);
|
||||
if (jv_get_kind(a) == JV_KIND_NULL) {
|
||||
jv_free(a);
|
||||
return b;
|
||||
} else if (jv_get_kind(b) == JV_KIND_NULL) {
|
||||
jv_free(b);
|
||||
return a;
|
||||
} else if (jv_get_kind(a) == JV_KIND_NUMBER && jv_get_kind(b) == JV_KIND_NUMBER) {
|
||||
return jv_number(jv_number_value(a) +
|
||||
jv_number_value(b));
|
||||
} else if (jv_get_kind(a) == JV_KIND_STRING && jv_get_kind(b) == JV_KIND_STRING) {
|
||||
return jv_string_concat(a, b);
|
||||
} else if (jv_get_kind(a) == JV_KIND_ARRAY && jv_get_kind(b) == JV_KIND_ARRAY) {
|
||||
return jv_array_concat(a, b);
|
||||
} else if (jv_get_kind(a) == JV_KIND_OBJECT && jv_get_kind(b) == JV_KIND_OBJECT) {
|
||||
return jv_object_merge(a, b);
|
||||
} else {
|
||||
return type_error2(a, b, "cannot be added");
|
||||
}
|
||||
}
|
||||
|
||||
static jv f_negate(jv input) {
|
||||
if (jv_get_kind(input) != JV_KIND_NUMBER) {
|
||||
return type_error(input, "cannot be negated");
|
||||
}
|
||||
jv ret = jv_number(-jv_number_value(input));
|
||||
jv_free(input);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static jv f_minus(jv input, jv a, jv b) {
|
||||
jv_free(input);
|
||||
if (jv_get_kind(a) == JV_KIND_NUMBER && jv_get_kind(b) == JV_KIND_NUMBER) {
|
||||
return jv_number(jv_number_value(a) - jv_number_value(b));
|
||||
} else if (jv_get_kind(a) == JV_KIND_ARRAY && jv_get_kind(b) == JV_KIND_ARRAY) {
|
||||
jv out = jv_array();
|
||||
jv_array_foreach(a, i, x) {
|
||||
int include = 1;
|
||||
jv_array_foreach(b, j, y) {
|
||||
if (jv_equal(jv_copy(x), y)) {
|
||||
include = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (include)
|
||||
out = jv_array_append(out, jv_copy(x));
|
||||
jv_free(x);
|
||||
}
|
||||
jv_free(a);
|
||||
jv_free(b);
|
||||
return out;
|
||||
} else {
|
||||
return type_error2(a, b, "cannot be subtracted");
|
||||
}
|
||||
}
|
||||
|
||||
static jv f_multiply(jv input, jv a, jv b) {
|
||||
jv_free(input);
|
||||
if (jv_get_kind(a) == JV_KIND_NUMBER && jv_get_kind(b) == JV_KIND_NUMBER) {
|
||||
return jv_number(jv_number_value(a) * jv_number_value(b));
|
||||
} else {
|
||||
return type_error2(a, b, "cannot be multiplied");
|
||||
}
|
||||
}
|
||||
|
||||
static jv f_divide(jv input, jv a, jv b) {
|
||||
jv_free(input);
|
||||
if (jv_get_kind(a) == JV_KIND_NUMBER && jv_get_kind(b) == JV_KIND_NUMBER) {
|
||||
return jv_number(jv_number_value(a) / jv_number_value(b));
|
||||
} else {
|
||||
return type_error2(a, b, "cannot be divided");
|
||||
}
|
||||
}
|
||||
|
||||
static jv f_equal(jv input, jv a, jv b) {
|
||||
jv_free(input);
|
||||
return jv_bool(jv_equal(a, b));
|
||||
}
|
||||
|
||||
static jv f_notequal(jv input, jv a, jv b) {
|
||||
jv_free(input);
|
||||
return jv_bool(!jv_equal(a, b));
|
||||
}
|
||||
|
||||
enum cmp_op {
|
||||
CMP_OP_LESS,
|
||||
CMP_OP_GREATER,
|
||||
CMP_OP_LESSEQ,
|
||||
CMP_OP_GREATEREQ
|
||||
};
|
||||
|
||||
static jv order_cmp(jv input, jv a, jv b, enum cmp_op op) {
|
||||
jv_free(input);
|
||||
int r = jv_cmp(a, b);
|
||||
return jv_bool((op == CMP_OP_LESS && r < 0) ||
|
||||
(op == CMP_OP_LESSEQ && r <= 0) ||
|
||||
(op == CMP_OP_GREATEREQ && r >= 0) ||
|
||||
(op == CMP_OP_GREATER && r > 0));
|
||||
}
|
||||
|
||||
static jv f_less(jv input, jv a, jv b) {
|
||||
return order_cmp(input, a, b, CMP_OP_LESS);
|
||||
}
|
||||
|
||||
static jv f_greater(jv input, jv a, jv b) {
|
||||
return order_cmp(input, a, b, CMP_OP_GREATER);
|
||||
}
|
||||
|
||||
static jv f_lesseq(jv input, jv a, jv b) {
|
||||
return order_cmp(input, a, b, CMP_OP_LESSEQ);
|
||||
}
|
||||
|
||||
static jv f_greatereq(jv input, jv a, jv b) {
|
||||
return order_cmp(input, a, b, CMP_OP_GREATEREQ);
|
||||
}
|
||||
|
||||
static jv f_contains(jv a, jv b) {
|
||||
if (jv_get_kind(a) == jv_get_kind(b)) {
|
||||
return jv_bool(jv_contains(a, b));
|
||||
} else {
|
||||
return type_error2(a, b, "cannot have their containment checked");
|
||||
}
|
||||
}
|
||||
|
||||
static jv f_tonumber(jv input) {
|
||||
if (jv_get_kind(input) == JV_KIND_NUMBER) {
|
||||
return input;
|
||||
}
|
||||
if (jv_get_kind(input) == JV_KIND_STRING) {
|
||||
jv parsed = jv_parse(jv_string_value(input));
|
||||
if (!jv_is_valid(parsed) || jv_get_kind(parsed) == JV_KIND_NUMBER) {
|
||||
jv_free(input);
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
return type_error(input, "cannot be parsed as a number");
|
||||
}
|
||||
|
||||
static jv f_length(jv input) {
|
||||
if (jv_get_kind(input) == JV_KIND_ARRAY) {
|
||||
return jv_number(jv_array_length(input));
|
||||
} else if (jv_get_kind(input) == JV_KIND_OBJECT) {
|
||||
return jv_number(jv_object_length(input));
|
||||
} else if (jv_get_kind(input) == JV_KIND_STRING) {
|
||||
return jv_number(jv_string_length_codepoints(input));
|
||||
} else if (jv_get_kind(input) == JV_KIND_NULL) {
|
||||
jv_free(input);
|
||||
return jv_number(0);
|
||||
} else {
|
||||
return type_error(input, "has no length");
|
||||
}
|
||||
}
|
||||
|
||||
static jv f_tostring(jv input) {
|
||||
if (jv_get_kind(input) == JV_KIND_STRING) {
|
||||
return input;
|
||||
} else {
|
||||
return jv_dump_string(input, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#define CHARS_ALPHANUM "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||
|
||||
static jv escape_string(jv input, const char* escapings) {
|
||||
|
||||
assert(jv_get_kind(input) == JV_KIND_STRING);
|
||||
const char* lookup[128] = {0};
|
||||
const char* p = escapings;
|
||||
while (*p) {
|
||||
lookup[(int)*p] = p+1;
|
||||
p++;
|
||||
p += strlen(p);
|
||||
p++;
|
||||
}
|
||||
|
||||
jv ret = jv_string("");
|
||||
const char* i = jv_string_value(input);
|
||||
const char* end = i + jv_string_length_bytes(jv_copy(input));
|
||||
const char* cstart;
|
||||
int c = 0;
|
||||
while ((i = jvp_utf8_next((cstart = i), end, &c))) {
|
||||
assert(c != -1);
|
||||
if (c < 128 && lookup[c]) {
|
||||
ret = jv_string_append_str(ret, lookup[c]);
|
||||
} else {
|
||||
ret = jv_string_append_buf(ret, cstart, i - cstart);
|
||||
}
|
||||
}
|
||||
jv_free(input);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static jv f_format(jv input, jv fmt) {
|
||||
if (jv_get_kind(fmt) != JV_KIND_STRING) {
|
||||
jv_free(input);
|
||||
return type_error(fmt, "is not a valid format");
|
||||
}
|
||||
const char* fmt_s = jv_string_value(fmt);
|
||||
if (!strcmp(fmt_s, "json")) {
|
||||
jv_free(fmt);
|
||||
return jv_dump_string(input, 0);
|
||||
} else if (!strcmp(fmt_s, "text")) {
|
||||
jv_free(fmt);
|
||||
return f_tostring(input);
|
||||
} else if (!strcmp(fmt_s, "csv")) {
|
||||
jv_free(fmt);
|
||||
if (jv_get_kind(input) != JV_KIND_ARRAY)
|
||||
return type_error(input, "cannot be csv-formatted, only array");
|
||||
jv line = jv_string("");
|
||||
jv_array_foreach(input, i, x) {
|
||||
if (i) line = jv_string_append_str(line, ",");
|
||||
switch (jv_get_kind(x)) {
|
||||
case JV_KIND_NULL:
|
||||
/* null rendered as empty string */
|
||||
jv_free(x);
|
||||
break;
|
||||
case JV_KIND_TRUE:
|
||||
case JV_KIND_FALSE:
|
||||
line = jv_string_concat(line, jv_dump_string(x, 0));
|
||||
break;
|
||||
case JV_KIND_NUMBER:
|
||||
if (jv_number_value(x) != jv_number_value(x)) {
|
||||
/* NaN, render as empty string */
|
||||
jv_free(x);
|
||||
} else {
|
||||
line = jv_string_concat(line, jv_dump_string(x, 0));
|
||||
}
|
||||
break;
|
||||
case JV_KIND_STRING: {
|
||||
line = jv_string_append_str(line, "\"");
|
||||
line = jv_string_concat(line, escape_string(x, "\"\"\"\0"));
|
||||
line = jv_string_append_str(line, "\"");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
jv_free(input);
|
||||
jv_free(line);
|
||||
return type_error(x, "is not valid in a csv row");
|
||||
}
|
||||
}
|
||||
jv_free(input);
|
||||
return line;
|
||||
} else if (!strcmp(fmt_s, "html")) {
|
||||
jv_free(fmt);
|
||||
return escape_string(f_tostring(input), "&&\0<<\0>>\0''\0\""\0");
|
||||
} else if (!strcmp(fmt_s, "uri")) {
|
||||
jv_free(fmt);
|
||||
input = f_tostring(input);
|
||||
|
||||
int unreserved[128] = {0};
|
||||
const char* p = CHARS_ALPHANUM "-_.!~*'()";
|
||||
while (*p) unreserved[(int)*p++] = 1;
|
||||
|
||||
jv line = jv_string("");
|
||||
const char* s = jv_string_value(input);
|
||||
for (int i=0; i<jv_string_length_bytes(jv_copy(input)); i++) {
|
||||
unsigned ch = (unsigned)(unsigned char)*s;
|
||||
if (ch < 128 && unreserved[ch]) {
|
||||
line = jv_string_append_buf(line, s, 1);
|
||||
} else {
|
||||
line = jv_string_concat(line, jv_string_fmt("%%%02x", ch));
|
||||
}
|
||||
s++;
|
||||
}
|
||||
jv_free(input);
|
||||
return line;
|
||||
} else if (!strcmp(fmt_s, "sh")) {
|
||||
jv_free(fmt);
|
||||
if (jv_get_kind(input) != JV_KIND_ARRAY)
|
||||
input = jv_array_set(jv_array(), 0, input);
|
||||
jv line = jv_string("");
|
||||
jv_array_foreach(input, i, x) {
|
||||
if (i) line = jv_string_append_str(line, " ");
|
||||
switch (jv_get_kind(x)) {
|
||||
case JV_KIND_NULL:
|
||||
case JV_KIND_TRUE:
|
||||
case JV_KIND_FALSE:
|
||||
case JV_KIND_NUMBER:
|
||||
line = jv_string_concat(line, jv_dump_string(x, 0));
|
||||
break;
|
||||
|
||||
case JV_KIND_STRING: {
|
||||
line = jv_string_append_str(line, "'");
|
||||
line = jv_string_concat(line, escape_string(x, "''\\''\0"));
|
||||
line = jv_string_append_str(line, "'");
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
jv_free(input);
|
||||
jv_free(line);
|
||||
return type_error(x, "can not be escaped for shell");
|
||||
}
|
||||
}
|
||||
jv_free(input);
|
||||
return line;
|
||||
} else if (!strcmp(fmt_s, "base64")) {
|
||||
jv_free(fmt);
|
||||
input = f_tostring(input);
|
||||
jv line = jv_string("");
|
||||
const char b64[64 + 1] = CHARS_ALPHANUM "+/";
|
||||
const char* data = jv_string_value(input);
|
||||
int len = jv_string_length_bytes(jv_copy(input));
|
||||
for (int i=0; i<len; i+=3) {
|
||||
uint32_t code = 0;
|
||||
int n = len - i >= 3 ? 3 : len-i;
|
||||
for (int j=0; j<3; j++) {
|
||||
code <<= 8;
|
||||
code |= j < n ? (unsigned)data[i+j] : 0;
|
||||
}
|
||||
char buf[4];
|
||||
for (int j=0; j<4; j++) {
|
||||
buf[j] = b64[(code >> (18 - j*6)) & 0x3f];
|
||||
}
|
||||
if (n < 3) buf[3] = '=';
|
||||
if (n < 2) buf[2] = '=';
|
||||
line = jv_string_append_buf(line, buf, sizeof(buf));
|
||||
}
|
||||
jv_free(input);
|
||||
return line;
|
||||
} else {
|
||||
jv_free(input);
|
||||
return jv_invalid_with_msg(jv_string_concat(fmt, jv_string(" is not a valid format")));
|
||||
}
|
||||
}
|
||||
|
||||
static jv f_keys(jv input) {
|
||||
if (jv_get_kind(input) == JV_KIND_OBJECT || jv_get_kind(input) == JV_KIND_ARRAY) {
|
||||
return jv_keys(input);
|
||||
} else {
|
||||
return type_error(input, "has no keys");
|
||||
}
|
||||
}
|
||||
|
||||
static jv f_sort(jv input){
|
||||
if (jv_get_kind(input) == JV_KIND_ARRAY) {
|
||||
return jv_sort(input, jv_copy(input));
|
||||
} else {
|
||||
return type_error(input, "cannot be sorted, as it is not an array");
|
||||
}
|
||||
}
|
||||
|
||||
static jv f_sort_by_impl(jv input, jv keys) {
|
||||
if (jv_get_kind(input) == JV_KIND_ARRAY &&
|
||||
jv_get_kind(keys) == JV_KIND_ARRAY &&
|
||||
jv_array_length(jv_copy(input)) == jv_array_length(jv_copy(keys))) {
|
||||
return jv_sort(input, keys);
|
||||
} else {
|
||||
return type_error2(input, keys, "cannot be sorted, as they are not both arrays");
|
||||
}
|
||||
}
|
||||
|
||||
static jv f_group_by_impl(jv input, jv keys) {
|
||||
if (jv_get_kind(input) == JV_KIND_ARRAY &&
|
||||
jv_get_kind(keys) == JV_KIND_ARRAY &&
|
||||
jv_array_length(jv_copy(input)) == jv_array_length(jv_copy(keys))) {
|
||||
return jv_group(input, keys);
|
||||
} else {
|
||||
return type_error2(input, keys, "cannot be sorted, as they are not both arrays");
|
||||
}
|
||||
}
|
||||
|
||||
static jv minmax_by(jv values, jv keys, int is_min) {
|
||||
if (jv_get_kind(values) != JV_KIND_ARRAY)
|
||||
return type_error2(values, keys, "cannot be iterated over");
|
||||
if (jv_get_kind(keys) != JV_KIND_ARRAY)
|
||||
return type_error2(values, keys, "cannot be iterated over");
|
||||
if (jv_array_length(jv_copy(values)) != jv_array_length(jv_copy(keys)))
|
||||
return type_error2(values, keys, "have wrong length");
|
||||
|
||||
if (jv_array_length(jv_copy(values)) == 0) {
|
||||
jv_free(values);
|
||||
jv_free(keys);
|
||||
return jv_null();
|
||||
}
|
||||
jv ret = jv_array_get(jv_copy(values), 0);
|
||||
jv retkey = jv_array_get(jv_copy(keys), 0);
|
||||
for (int i=1; i<jv_array_length(jv_copy(values)); i++) {
|
||||
jv item = jv_array_get(jv_copy(keys), i);
|
||||
int cmp = jv_cmp(jv_copy(item), jv_copy(retkey));
|
||||
if ((cmp < 0) == (is_min == 1)) {
|
||||
jv_free(retkey);
|
||||
retkey = item;
|
||||
jv_free(ret);
|
||||
ret = jv_array_get(jv_copy(values), i);
|
||||
} else {
|
||||
jv_free(item);
|
||||
}
|
||||
}
|
||||
jv_free(values);
|
||||
jv_free(keys);
|
||||
jv_free(retkey);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static jv f_min(jv x) {
|
||||
return minmax_by(x, jv_copy(x), 1);
|
||||
}
|
||||
|
||||
static jv f_max(jv x) {
|
||||
return minmax_by(x, jv_copy(x), 0);
|
||||
}
|
||||
|
||||
static jv f_min_by_impl(jv x, jv y) {
|
||||
return minmax_by(x, y, 1);
|
||||
}
|
||||
|
||||
static jv f_max_by_impl(jv x, jv y) {
|
||||
return minmax_by(x, y, 0);
|
||||
}
|
||||
|
||||
|
||||
static jv f_type(jv input) {
|
||||
jv out = jv_string(jv_kind_name(jv_get_kind(input)));
|
||||
jv_free(input);
|
||||
return out;
|
||||
}
|
||||
|
||||
static jv f_error(jv input, jv msg) {
|
||||
jv_free(input);
|
||||
msg = f_tostring(msg);
|
||||
return jv_invalid_with_msg(msg);
|
||||
}
|
||||
|
||||
static const struct cfunction function_list[] = {
|
||||
{(cfunction_ptr)f_plus, "_plus", 3},
|
||||
{(cfunction_ptr)f_negate, "_negate", 1},
|
||||
{(cfunction_ptr)f_minus, "_minus", 3},
|
||||
{(cfunction_ptr)f_multiply, "_multiply", 3},
|
||||
{(cfunction_ptr)f_divide, "_divide", 3},
|
||||
{(cfunction_ptr)f_tonumber, "tonumber", 1},
|
||||
{(cfunction_ptr)f_tostring, "tostring", 1},
|
||||
{(cfunction_ptr)f_keys, "keys", 1},
|
||||
{(cfunction_ptr)jv_setpath, "setpath", 3}, // FIXME typechecking
|
||||
{(cfunction_ptr)jv_getpath, "getpath", 2},
|
||||
{(cfunction_ptr)jv_delpaths, "delpaths", 2},
|
||||
{(cfunction_ptr)jv_has, "has", 2},
|
||||
{(cfunction_ptr)f_equal, "_equal", 3},
|
||||
{(cfunction_ptr)f_notequal, "_notequal", 3},
|
||||
{(cfunction_ptr)f_less, "_less", 3},
|
||||
{(cfunction_ptr)f_greater, "_greater", 3},
|
||||
{(cfunction_ptr)f_lesseq, "_lesseq", 3},
|
||||
{(cfunction_ptr)f_greatereq, "_greatereq", 3},
|
||||
{(cfunction_ptr)f_contains, "contains", 2},
|
||||
{(cfunction_ptr)f_length, "length", 1},
|
||||
{(cfunction_ptr)f_type, "type", 1},
|
||||
{(cfunction_ptr)f_sort, "sort", 1},
|
||||
{(cfunction_ptr)f_sort_by_impl, "_sort_by_impl", 2},
|
||||
{(cfunction_ptr)f_group_by_impl, "_group_by_impl", 2},
|
||||
{(cfunction_ptr)f_min, "min", 1},
|
||||
{(cfunction_ptr)f_max, "max", 1},
|
||||
{(cfunction_ptr)f_min_by_impl, "_min_by_impl", 2},
|
||||
{(cfunction_ptr)f_max_by_impl, "_max_by_impl", 2},
|
||||
{(cfunction_ptr)f_error, "error", 2},
|
||||
{(cfunction_ptr)f_format, "format", 2},
|
||||
};
|
||||
|
||||
struct bytecoded_builtin { const char* name; block code; };
|
||||
static block bind_bytecoded_builtins(block b) {
|
||||
block builtins = gen_noop();
|
||||
{
|
||||
struct bytecoded_builtin builtin_defs[] = {
|
||||
{"empty", gen_op_simple(BACKTRACK)},
|
||||
{"false", gen_const(jv_false())},
|
||||
{"true", gen_const(jv_true())},
|
||||
{"null", gen_const(jv_null())},
|
||||
{"not", gen_condbranch(gen_const(jv_false()),
|
||||
gen_const(jv_true()))}
|
||||
};
|
||||
for (unsigned i=0; i<sizeof(builtin_defs)/sizeof(builtin_defs[0]); i++) {
|
||||
builtins = BLOCK(builtins, gen_function(builtin_defs[i].name, gen_noop(),
|
||||
builtin_defs[i].code));
|
||||
}
|
||||
}
|
||||
{
|
||||
struct bytecoded_builtin builtin_def_1arg[] = {
|
||||
{"path", BLOCK(gen_op_simple(PATH_BEGIN),
|
||||
gen_call("arg", gen_noop()),
|
||||
gen_op_simple(PATH_END))},
|
||||
};
|
||||
for (unsigned i=0; i<sizeof(builtin_def_1arg)/sizeof(builtin_def_1arg[0]); i++) {
|
||||
builtins = BLOCK(builtins, gen_function(builtin_def_1arg[i].name,
|
||||
gen_param("arg"),
|
||||
builtin_def_1arg[i].code));
|
||||
}
|
||||
}
|
||||
{
|
||||
block rangevar = gen_op_var_fresh(STOREV, "rangevar");
|
||||
block init = BLOCK(gen_op_simple(DUP), gen_call("start", gen_noop()), rangevar);
|
||||
block range = BLOCK(init,
|
||||
gen_call("end", gen_noop()),
|
||||
gen_op_bound(RANGE, rangevar));
|
||||
builtins = BLOCK(builtins, gen_function("range",
|
||||
BLOCK(gen_param("start"), gen_param("end")),
|
||||
range));
|
||||
}
|
||||
|
||||
return block_bind_referenced(builtins, b, OP_IS_CALL_PSEUDO);
|
||||
}
|
||||
|
||||
static const char* const jq_builtins[] = {
|
||||
"def map(f): [.[] | f];",
|
||||
"def select(f): if f then . else empty end;",
|
||||
"def sort_by(f): _sort_by_impl(map([f]));",
|
||||
"def group_by(f): _group_by_impl(map([f]));",
|
||||
"def unique: group_by(.) | map(.[0]);",
|
||||
"def max_by(f): _max_by_impl(map([f]));",
|
||||
"def min_by(f): _min_by_impl(map([f]));",
|
||||
"def add: reduce .[] as $x (null; . + $x);",
|
||||
"def del(f): delpaths([path(f)]);",
|
||||
"def _assign(paths; value): value as $v | reduce path(paths) as $p (.; setpath($p; $v));",
|
||||
"def _modify(paths; update): reduce path(paths) as $p (.; setpath($p; getpath($p) | update));",
|
||||
"def recurse(f): ., (f | select(. != null) | recurse(f));",
|
||||
"def to_entries: [keys[] as $k | {key: $k, value: .[$k]}];",
|
||||
"def from_entries: map({(.key): .value}) | add;",
|
||||
"def with_entries(f): to_entries | map(f) | from_entries;",
|
||||
"def reverse: [.[length - 1 - range(0;length)]];",
|
||||
};
|
||||
|
||||
|
||||
int builtins_bind_one(block* bb, const char* code) {
|
||||
struct locfile src;
|
||||
locfile_init(&src, code, strlen(code));
|
||||
block funcs;
|
||||
int nerrors = jq_parse_library(&src, &funcs);
|
||||
if (nerrors == 0) {
|
||||
*bb = block_bind_referenced(funcs, *bb, OP_IS_CALL_PSEUDO);
|
||||
}
|
||||
locfile_free(&src);
|
||||
return nerrors;
|
||||
}
|
||||
|
||||
int slurp_lib(block* bb) {
|
||||
int nerrors = 0;
|
||||
char* home = getenv("HOME");
|
||||
if (home) { // silently ignore no $HOME
|
||||
jv filename = jv_string_append_str(jv_string(home), "/.jq");
|
||||
jv data = jv_load_file(jv_string_value(filename), 1);
|
||||
if (jv_is_valid(data)) {
|
||||
nerrors = builtins_bind_one(bb, jv_string_value(data) );
|
||||
}
|
||||
jv_free(filename);
|
||||
jv_free(data);
|
||||
}
|
||||
return nerrors;
|
||||
}
|
||||
|
||||
int builtins_bind(block* bb) {
|
||||
int nerrors = slurp_lib(bb);
|
||||
if (nerrors) {
|
||||
block_free(*bb);
|
||||
return nerrors;
|
||||
}
|
||||
for (int i=(int)(sizeof(jq_builtins)/sizeof(jq_builtins[0]))-1; i>=0; i--) {
|
||||
nerrors = builtins_bind_one(bb, jq_builtins[i]);
|
||||
assert(!nerrors);
|
||||
}
|
||||
*bb = bind_bytecoded_builtins(*bb);
|
||||
*bb = gen_cbinding(function_list, sizeof(function_list)/sizeof(function_list[0]), *bb);
|
||||
return nerrors;
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
#ifndef BUILTIN_H
|
||||
#define BUILTIN_H
|
||||
|
||||
#include "bytecode.h"
|
||||
#include "compile.h"
|
||||
|
||||
int builtins_bind(block*);
|
||||
|
||||
#endif
|
||||
-160
@@ -1,160 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "bytecode.h"
|
||||
#include "jv_alloc.h"
|
||||
|
||||
// flags, length
|
||||
#define NONE 0, 1
|
||||
#define CONSTANT OP_HAS_CONSTANT, 2
|
||||
#define VARIABLE (OP_HAS_VARIABLE | OP_HAS_BINDING), 3
|
||||
#define BRANCH OP_HAS_BRANCH, 2
|
||||
#define CFUNC (OP_HAS_CFUNC | OP_HAS_BINDING), 3
|
||||
#define UFUNC (OP_HAS_UFUNC | OP_HAS_BINDING | OP_IS_CALL_PSEUDO), 4
|
||||
#define DEFINITION (OP_IS_CALL_PSEUDO | OP_HAS_BINDING), 0
|
||||
#define CLOSURE_REF_IMM (OP_IS_CALL_PSEUDO | OP_HAS_BINDING), 2
|
||||
|
||||
#define OP(name, imm, in, out) \
|
||||
{name, #name, imm, in, out},
|
||||
|
||||
static const struct opcode_description opcode_descriptions[] = {
|
||||
#include "opcode_list.h"
|
||||
};
|
||||
|
||||
static const struct opcode_description invalid_opcode_description = {
|
||||
-1, "#INVALID", 0, 0, 0, 0
|
||||
};
|
||||
|
||||
|
||||
const struct opcode_description* opcode_describe(opcode op) {
|
||||
if ((int)op >= 0 && (int)op < NUM_OPCODES) {
|
||||
return &opcode_descriptions[op];
|
||||
} else {
|
||||
return &invalid_opcode_description;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int bytecode_operation_length(uint16_t* codeptr) {
|
||||
int length = opcode_describe(*codeptr)->length;
|
||||
if (*codeptr == CALL_JQ) {
|
||||
length += codeptr[1] * 2;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
static void dump_code(int indent, struct bytecode* bc) {
|
||||
int pc = 0;
|
||||
while (pc < bc->codelen) {
|
||||
printf("%*s", indent, "");
|
||||
dump_operation(bc, bc->code + pc);
|
||||
printf("\n");
|
||||
pc += bytecode_operation_length(bc->code + pc);
|
||||
}
|
||||
}
|
||||
|
||||
static void symbol_table_free(struct symbol_table* syms) {
|
||||
jv_mem_free(syms->cfunctions);
|
||||
jv_free(syms->cfunc_names);
|
||||
jv_mem_free(syms);
|
||||
}
|
||||
|
||||
void dump_disassembly(int indent, struct bytecode* bc) {
|
||||
if (bc->nclosures > 0) {
|
||||
printf("%*s[params: ", indent, "");
|
||||
jv params = jv_object_get(jv_copy(bc->debuginfo), jv_string("params"));
|
||||
for (int i=0; i<bc->nclosures; i++) {
|
||||
if (i) printf(", ");
|
||||
jv name = jv_array_get(jv_copy(params), i);
|
||||
printf("%s", jv_string_value(name));
|
||||
jv_free(name);
|
||||
}
|
||||
jv_free(params);
|
||||
printf("]\n");
|
||||
}
|
||||
dump_code(indent, bc);
|
||||
for (int i=0; i<bc->nsubfunctions; i++) {
|
||||
struct bytecode* subfn = bc->subfunctions[i];
|
||||
jv name = jv_object_get(jv_copy(subfn->debuginfo), jv_string("name"));
|
||||
printf("%*s%s:%d:\n", indent, "", jv_string_value(name), i);
|
||||
jv_free(name);
|
||||
dump_disassembly(indent+2, subfn);
|
||||
}
|
||||
}
|
||||
|
||||
static struct bytecode* getlevel(struct bytecode* bc, int level) {
|
||||
while (level > 0) {
|
||||
bc = bc->parent;
|
||||
level--;
|
||||
}
|
||||
return bc;
|
||||
}
|
||||
|
||||
void dump_operation(struct bytecode* bc, uint16_t* codeptr) {
|
||||
int pc = codeptr - bc->code;
|
||||
printf("%04d ", pc);
|
||||
const struct opcode_description* op = opcode_describe(bc->code[pc++]);
|
||||
printf("%s", op->name);
|
||||
if (op->length > 1) {
|
||||
uint16_t imm = bc->code[pc++];
|
||||
if (op->op == CALL_JQ) {
|
||||
for (int i=0; i<imm+1; i++) {
|
||||
uint16_t level = bc->code[pc++];
|
||||
uint16_t idx = bc->code[pc++];
|
||||
jv name;
|
||||
if (idx & ARG_NEWCLOSURE) {
|
||||
idx &= ~ARG_NEWCLOSURE;
|
||||
name = jv_object_get(jv_copy(getlevel(bc,level)->subfunctions[idx]->debuginfo),
|
||||
jv_string("name"));
|
||||
} else {
|
||||
name = jv_array_get(jv_object_get(jv_copy(getlevel(bc,level)->debuginfo),
|
||||
jv_string("params")), idx);
|
||||
}
|
||||
printf(" %s:%d",
|
||||
jv_string_value(name),
|
||||
idx);
|
||||
jv_free(name);
|
||||
if (level) {
|
||||
printf("^%d", level);
|
||||
}
|
||||
}
|
||||
} else if (op->op == CALL_BUILTIN) {
|
||||
int func = bc->code[pc++];
|
||||
jv name = jv_array_get(jv_copy(bc->globals->cfunc_names), func);
|
||||
printf(" %s", jv_string_value(name));
|
||||
jv_free(name);
|
||||
} else if (op->flags & OP_HAS_BRANCH) {
|
||||
printf(" %04d", pc + imm);
|
||||
} else if (op->flags & OP_HAS_CONSTANT) {
|
||||
printf(" ");
|
||||
jv_dump(jv_array_get(jv_copy(bc->constants), imm), 0);
|
||||
} else if (op->flags & OP_HAS_VARIABLE) {
|
||||
uint16_t v = bc->code[pc++];
|
||||
jv name = jv_array_get(jv_object_get(jv_copy(getlevel(bc,imm)->debuginfo), jv_string("locals")), v);
|
||||
printf(" $%s:%d",
|
||||
jv_string_value(name),
|
||||
v);
|
||||
jv_free(name);
|
||||
if (imm) {
|
||||
printf("^%d", imm);
|
||||
}
|
||||
} else {
|
||||
printf(" %d", imm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bytecode_free(struct bytecode* bc) {
|
||||
if (!bc)
|
||||
return;
|
||||
jv_mem_free(bc->code);
|
||||
jv_free(bc->constants);
|
||||
for (int i=0; i<bc->nsubfunctions; i++)
|
||||
bytecode_free(bc->subfunctions[i]);
|
||||
if (!bc->parent)
|
||||
symbol_table_free(bc->globals);
|
||||
jv_mem_free(bc->subfunctions);
|
||||
jv_free(bc->debuginfo);
|
||||
jv_mem_free(bc);
|
||||
}
|
||||
-88
@@ -1,88 +0,0 @@
|
||||
#ifndef BYTECODE_H
|
||||
#define BYTECODE_H
|
||||
#include <stdint.h>
|
||||
|
||||
#include "jv.h"
|
||||
|
||||
typedef enum {
|
||||
#define OP(name, imm, in, out) name,
|
||||
#include "opcode_list.h"
|
||||
#undef OP
|
||||
} opcode;
|
||||
|
||||
enum {
|
||||
NUM_OPCODES =
|
||||
#define OP(name, imm, in, out) +1
|
||||
#include "opcode_list.h"
|
||||
#undef OP
|
||||
};
|
||||
|
||||
enum {
|
||||
OP_HAS_CONSTANT = 2,
|
||||
OP_HAS_VARIABLE = 4,
|
||||
OP_HAS_BRANCH = 8,
|
||||
OP_HAS_CFUNC = 32,
|
||||
OP_HAS_UFUNC = 64,
|
||||
OP_IS_CALL_PSEUDO = 128,
|
||||
OP_HAS_BINDING = 1024,
|
||||
};
|
||||
struct opcode_description {
|
||||
opcode op;
|
||||
const char* name;
|
||||
|
||||
int flags;
|
||||
|
||||
// length in 16-bit units
|
||||
int length;
|
||||
|
||||
int stack_in, stack_out;
|
||||
};
|
||||
|
||||
const struct opcode_description* opcode_describe(opcode op);
|
||||
|
||||
|
||||
#define MAX_CFUNCTION_ARGS 10
|
||||
typedef void (*cfunction_ptr)();
|
||||
struct cfunction {
|
||||
cfunction_ptr fptr;
|
||||
const char* name;
|
||||
int nargs;
|
||||
};
|
||||
|
||||
struct symbol_table {
|
||||
struct cfunction* cfunctions;
|
||||
int ncfunctions;
|
||||
jv cfunc_names;
|
||||
};
|
||||
|
||||
// The bytecode format matters in:
|
||||
// execute.c - interpreter
|
||||
// compile.c - compiler
|
||||
// bytecode.c - disassembler
|
||||
|
||||
#define ARG_NEWCLOSURE 0x1000
|
||||
|
||||
struct bytecode {
|
||||
uint16_t* code;
|
||||
int codelen;
|
||||
|
||||
int nlocals;
|
||||
int nclosures;
|
||||
|
||||
jv constants; // JSON array of constants
|
||||
struct symbol_table* globals;
|
||||
|
||||
struct bytecode** subfunctions;
|
||||
int nsubfunctions;
|
||||
|
||||
struct bytecode* parent;
|
||||
|
||||
jv debuginfo;
|
||||
};
|
||||
|
||||
void dump_disassembly(int, struct bytecode* code);
|
||||
void dump_operation(struct bytecode* bc, uint16_t* op);
|
||||
|
||||
void bytecode_free(struct bytecode* bc);
|
||||
|
||||
#endif
|
||||
@@ -1,670 +0,0 @@
|
||||
#define _GNU_SOURCE // for strdup
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "compile.h"
|
||||
#include "bytecode.h"
|
||||
#include "locfile.h"
|
||||
#include "jv_alloc.h"
|
||||
|
||||
/*
|
||||
The intermediate representation for jq filters is as a sequence of
|
||||
struct inst, which form a doubly-linked list via the next and prev
|
||||
pointers.
|
||||
|
||||
A "block" represents a sequence of "struct inst", which may be
|
||||
empty.
|
||||
|
||||
Blocks are generated by the parser bottom-up, so may have free
|
||||
variables (refer to things not defined). See inst.bound_by and
|
||||
inst.symbol.
|
||||
*/
|
||||
struct inst {
|
||||
struct inst* next;
|
||||
struct inst* prev;
|
||||
|
||||
opcode op;
|
||||
|
||||
struct {
|
||||
uint16_t intval;
|
||||
struct inst* target;
|
||||
jv constant;
|
||||
const struct cfunction* cfunc;
|
||||
} imm;
|
||||
|
||||
location source;
|
||||
|
||||
// Binding
|
||||
// An instruction requiring binding (for parameters/variables/functions)
|
||||
// is in one of three states:
|
||||
// inst->bound_by = NULL - Unbound free variable
|
||||
// inst->bound_by = inst - This instruction binds a variable
|
||||
// inst->bound_by = other - Uses variable bound by other instruction
|
||||
// Unbound instructions (references to other things that may or may not
|
||||
// exist) are created by "gen_foo_unbound", and bindings are created by
|
||||
// block_bind(definition, body), which binds all instructions in
|
||||
// body which are unboudn and refer to "definition" by name.
|
||||
struct inst* bound_by;
|
||||
char* symbol;
|
||||
|
||||
block subfn; // used by CLOSURE_CREATE (body of function)
|
||||
block arglist; // used by CLOSURE_CREATE (formals) and CALL_JQ (arguments)
|
||||
|
||||
// This instruction is compiled as part of which function?
|
||||
// (only used during block_compile)
|
||||
struct bytecode* compiled;
|
||||
|
||||
int bytecode_pos; // position just after this insn
|
||||
};
|
||||
|
||||
static inst* inst_new(opcode op) {
|
||||
inst* i = jv_mem_alloc(sizeof(inst));
|
||||
i->next = i->prev = 0;
|
||||
i->op = op;
|
||||
i->bytecode_pos = -1;
|
||||
i->bound_by = 0;
|
||||
i->symbol = 0;
|
||||
i->subfn = gen_noop();
|
||||
i->arglist = gen_noop();
|
||||
i->source = UNKNOWN_LOCATION;
|
||||
return i;
|
||||
}
|
||||
|
||||
static void inst_free(struct inst* i) {
|
||||
jv_mem_free(i->symbol);
|
||||
block_free(i->subfn);
|
||||
block_free(i->arglist);
|
||||
if (opcode_describe(i->op)->flags & OP_HAS_CONSTANT) {
|
||||
jv_free(i->imm.constant);
|
||||
}
|
||||
jv_mem_free(i);
|
||||
}
|
||||
|
||||
static block inst_block(inst* i) {
|
||||
block b = {i,i};
|
||||
return b;
|
||||
}
|
||||
|
||||
static int block_is_single(block b) {
|
||||
return b.first && b.first == b.last;
|
||||
}
|
||||
|
||||
static inst* block_take(block* b) {
|
||||
if (b->first == 0) return 0;
|
||||
inst* i = b->first;
|
||||
if (i->next) {
|
||||
i->next->prev = 0;
|
||||
b->first = i->next;
|
||||
i->next = 0;
|
||||
} else {
|
||||
b->first = 0;
|
||||
b->last = 0;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
block gen_location(location loc, block b) {
|
||||
for (inst* i = b.first; i; i = i->next) {
|
||||
if (i->source.start == UNKNOWN_LOCATION.start &&
|
||||
i->source.end == UNKNOWN_LOCATION.end) {
|
||||
i->source = loc;
|
||||
}
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
block gen_noop() {
|
||||
block b = {0,0};
|
||||
return b;
|
||||
}
|
||||
|
||||
block gen_op_simple(opcode op) {
|
||||
assert(opcode_describe(op)->length == 1);
|
||||
return inst_block(inst_new(op));
|
||||
}
|
||||
|
||||
|
||||
block gen_const(jv constant) {
|
||||
assert(opcode_describe(LOADK)->flags & OP_HAS_CONSTANT);
|
||||
inst* i = inst_new(LOADK);
|
||||
i->imm.constant = constant;
|
||||
return inst_block(i);
|
||||
}
|
||||
|
||||
block gen_op_target(opcode op, block target) {
|
||||
assert(opcode_describe(op)->flags & OP_HAS_BRANCH);
|
||||
assert(target.last);
|
||||
inst* i = inst_new(op);
|
||||
i->imm.target = target.last;
|
||||
return inst_block(i);
|
||||
}
|
||||
|
||||
block gen_op_targetlater(opcode op) {
|
||||
assert(opcode_describe(op)->flags & OP_HAS_BRANCH);
|
||||
inst* i = inst_new(op);
|
||||
i->imm.target = 0;
|
||||
return inst_block(i);
|
||||
}
|
||||
void inst_set_target(block b, block target) {
|
||||
assert(block_is_single(b));
|
||||
assert(opcode_describe(b.first->op)->flags & OP_HAS_BRANCH);
|
||||
assert(target.last);
|
||||
b.first->imm.target = target.last;
|
||||
}
|
||||
|
||||
block gen_op_unbound(opcode op, const char* name) {
|
||||
assert(opcode_describe(op)->flags & OP_HAS_BINDING);
|
||||
inst* i = inst_new(op);
|
||||
i->symbol = strdup(name);
|
||||
return inst_block(i);
|
||||
}
|
||||
|
||||
block gen_op_var_fresh(opcode op, const char* name) {
|
||||
assert(opcode_describe(op)->flags & OP_HAS_VARIABLE);
|
||||
return block_bind(gen_op_unbound(op, name),
|
||||
gen_noop(), OP_HAS_VARIABLE);
|
||||
}
|
||||
|
||||
block gen_op_bound(opcode op, block binder) {
|
||||
assert(block_is_single(binder));
|
||||
block b = gen_op_unbound(op, binder.first->symbol);
|
||||
b.first->bound_by = binder.first;
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
static void inst_join(inst* a, inst* b) {
|
||||
assert(a && b);
|
||||
assert(!a->next);
|
||||
assert(!b->prev);
|
||||
a->next = b;
|
||||
b->prev = a;
|
||||
}
|
||||
|
||||
void block_append(block* b, block b2) {
|
||||
if (b2.first) {
|
||||
if (b->last) {
|
||||
inst_join(b->last, b2.first);
|
||||
} else {
|
||||
b->first = b2.first;
|
||||
}
|
||||
b->last = b2.last;
|
||||
}
|
||||
}
|
||||
|
||||
block block_join(block a, block b) {
|
||||
block c = a;
|
||||
block_append(&c, b);
|
||||
return c;
|
||||
}
|
||||
|
||||
int block_has_only_binders(block binders, int bindflags) {
|
||||
bindflags |= OP_HAS_BINDING;
|
||||
for (inst* curr = binders.first; curr; curr = curr->next) {
|
||||
if ((opcode_describe(curr->op)->flags & bindflags) != bindflags) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int block_bind_subblock(block binder, block body, int bindflags) {
|
||||
assert(block_is_single(binder));
|
||||
assert((opcode_describe(binder.first->op)->flags & bindflags) == bindflags);
|
||||
assert(binder.first->symbol);
|
||||
assert(binder.first->bound_by == 0 || binder.first->bound_by == binder.first);
|
||||
|
||||
binder.first->bound_by = binder.first;
|
||||
int nrefs = 0;
|
||||
for (inst* i = body.first; i; i = i->next) {
|
||||
int flags = opcode_describe(i->op)->flags;
|
||||
if ((flags & bindflags) == bindflags &&
|
||||
i->bound_by == 0 &&
|
||||
!strcmp(i->symbol, binder.first->symbol)) {
|
||||
// bind this instruction
|
||||
i->bound_by = binder.first;
|
||||
nrefs++;
|
||||
}
|
||||
// binding recurses into closures
|
||||
nrefs += block_bind_subblock(binder, i->subfn, bindflags);
|
||||
// binding recurses into argument list
|
||||
nrefs += block_bind_subblock(binder, i->arglist, bindflags);
|
||||
}
|
||||
return nrefs;
|
||||
}
|
||||
|
||||
static void block_bind_each(block binder, block body, int bindflags) {
|
||||
assert(block_has_only_binders(binder, bindflags));
|
||||
bindflags |= OP_HAS_BINDING;
|
||||
for (inst* curr = binder.first; curr; curr = curr->next) {
|
||||
block_bind_subblock(inst_block(curr), body, bindflags);
|
||||
}
|
||||
}
|
||||
|
||||
block block_bind(block binder, block body, int bindflags) {
|
||||
block_bind_each(binder, body, bindflags);
|
||||
return block_join(binder, body);
|
||||
}
|
||||
|
||||
block block_bind_referenced(block binder, block body, int bindflags) {
|
||||
assert(block_has_only_binders(binder, bindflags));
|
||||
bindflags |= OP_HAS_BINDING;
|
||||
block refd = gen_noop();
|
||||
for (inst* curr; (curr = block_take(&binder));) {
|
||||
block b = inst_block(curr);
|
||||
if (block_bind_subblock(b, body, bindflags)) {
|
||||
refd = BLOCK(refd, b);
|
||||
} else {
|
||||
block_free(b);
|
||||
}
|
||||
}
|
||||
return block_join(refd, body);
|
||||
}
|
||||
|
||||
block gen_function(const char* name, block formals, block body) {
|
||||
block_bind_each(formals, body, OP_IS_CALL_PSEUDO);
|
||||
inst* i = inst_new(CLOSURE_CREATE);
|
||||
i->subfn = body;
|
||||
i->symbol = strdup(name);
|
||||
i->arglist = formals;
|
||||
block b = inst_block(i);
|
||||
block_bind_subblock(b, b, OP_IS_CALL_PSEUDO | OP_HAS_BINDING);
|
||||
return b;
|
||||
}
|
||||
|
||||
block gen_param(const char* name) {
|
||||
return gen_op_unbound(CLOSURE_PARAM, name);
|
||||
}
|
||||
|
||||
block gen_lambda(block body) {
|
||||
return gen_function("@lambda", gen_noop(), body);
|
||||
}
|
||||
|
||||
block gen_call(const char* name, block args) {
|
||||
block b = gen_op_unbound(CALL_JQ, name);
|
||||
b.first->arglist = args;
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
|
||||
block gen_subexp(block a) {
|
||||
return BLOCK(gen_op_simple(SUBEXP_BEGIN), a, gen_op_simple(SUBEXP_END));
|
||||
}
|
||||
|
||||
block gen_both(block a, block b) {
|
||||
block jump = gen_op_targetlater(JUMP);
|
||||
block fork = gen_op_target(FORK, jump);
|
||||
block c = BLOCK(fork, a, jump, b);
|
||||
inst_set_target(jump, c);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
block gen_collect(block expr) {
|
||||
block array_var = gen_op_var_fresh(STOREV, "collect");
|
||||
block c = BLOCK(gen_op_simple(DUP), gen_const(jv_array()), array_var);
|
||||
|
||||
block tail = BLOCK(gen_op_bound(APPEND, array_var),
|
||||
gen_op_simple(BACKTRACK));
|
||||
|
||||
return BLOCK(c,
|
||||
gen_op_target(FORK, tail),
|
||||
expr,
|
||||
tail,
|
||||
gen_op_bound(LOADVN, array_var));
|
||||
}
|
||||
|
||||
block gen_reduce(const char* varname, block source, block init, block body) {
|
||||
block res_var = gen_op_var_fresh(STOREV, "reduce");
|
||||
block loop = BLOCK(gen_op_simple(DUP),
|
||||
source,
|
||||
block_bind(gen_op_unbound(STOREV, varname),
|
||||
BLOCK(gen_op_bound(LOADVN, res_var),
|
||||
body,
|
||||
gen_op_bound(STOREV, res_var)),
|
||||
OP_HAS_VARIABLE),
|
||||
gen_op_simple(BACKTRACK));
|
||||
return BLOCK(gen_op_simple(DUP),
|
||||
init,
|
||||
res_var,
|
||||
gen_op_target(FORK, loop),
|
||||
loop,
|
||||
gen_op_bound(LOADVN, res_var));
|
||||
}
|
||||
|
||||
block gen_definedor(block a, block b) {
|
||||
// var found := false
|
||||
block found_var = gen_op_var_fresh(STOREV, "found");
|
||||
block init = BLOCK(gen_op_simple(DUP), gen_const(jv_false()), found_var);
|
||||
|
||||
// if found, backtrack. Otherwise execute b
|
||||
block backtrack = gen_op_simple(BACKTRACK);
|
||||
block tail = BLOCK(gen_op_simple(DUP),
|
||||
gen_op_bound(LOADV, found_var),
|
||||
gen_op_target(JUMP_F, backtrack),
|
||||
backtrack,
|
||||
gen_op_simple(POP),
|
||||
b);
|
||||
|
||||
// try again
|
||||
block if_notfound = gen_op_simple(BACKTRACK);
|
||||
|
||||
// found := true, produce result
|
||||
block if_found = BLOCK(gen_op_simple(DUP),
|
||||
gen_const(jv_true()),
|
||||
gen_op_bound(STOREV, found_var),
|
||||
gen_op_target(JUMP, tail));
|
||||
|
||||
return BLOCK(init,
|
||||
gen_op_target(FORK, if_notfound),
|
||||
a,
|
||||
gen_op_target(JUMP_F, if_found),
|
||||
if_found,
|
||||
if_notfound,
|
||||
tail);
|
||||
}
|
||||
|
||||
block gen_condbranch(block iftrue, block iffalse) {
|
||||
iftrue = BLOCK(iftrue, gen_op_target(JUMP, iffalse));
|
||||
return BLOCK(gen_op_target(JUMP_F, iftrue), iftrue, iffalse);
|
||||
}
|
||||
|
||||
block gen_and(block a, block b) {
|
||||
// a and b = if a then (if b then true else false) else false
|
||||
return BLOCK(gen_op_simple(DUP), a,
|
||||
gen_condbranch(BLOCK(gen_op_simple(POP),
|
||||
b,
|
||||
gen_condbranch(gen_const(jv_true()),
|
||||
gen_const(jv_false()))),
|
||||
BLOCK(gen_op_simple(POP), gen_const(jv_false()))));
|
||||
}
|
||||
|
||||
block gen_or(block a, block b) {
|
||||
// a or b = if a then true else (if b then true else false)
|
||||
return BLOCK(gen_op_simple(DUP), a,
|
||||
gen_condbranch(BLOCK(gen_op_simple(POP), gen_const(jv_true())),
|
||||
BLOCK(gen_op_simple(POP),
|
||||
b,
|
||||
gen_condbranch(gen_const(jv_true()),
|
||||
gen_const(jv_false())))));
|
||||
}
|
||||
|
||||
block gen_var_binding(block var, const char* name, block body) {
|
||||
return BLOCK(gen_op_simple(DUP), var,
|
||||
block_bind(gen_op_unbound(STOREV, name),
|
||||
body, OP_HAS_VARIABLE));
|
||||
}
|
||||
|
||||
block gen_cond(block cond, block iftrue, block iffalse) {
|
||||
return BLOCK(gen_op_simple(DUP), cond,
|
||||
gen_condbranch(BLOCK(gen_op_simple(POP), iftrue),
|
||||
BLOCK(gen_op_simple(POP), iffalse)));
|
||||
}
|
||||
|
||||
block gen_cbinding(const struct cfunction* cfunctions, int ncfunctions, block code) {
|
||||
for (int cfunc=0; cfunc<ncfunctions; cfunc++) {
|
||||
inst* i = inst_new(CLOSURE_CREATE_C);
|
||||
i->imm.cfunc = &cfunctions[cfunc];
|
||||
i->symbol = strdup(i->imm.cfunc->name);
|
||||
code = block_bind(inst_block(i), code, OP_IS_CALL_PSEUDO);
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
static uint16_t nesting_level(struct bytecode* bc, inst* target) {
|
||||
uint16_t level = 0;
|
||||
assert(bc && target->compiled);
|
||||
while (bc && target->compiled != bc) {
|
||||
level++;
|
||||
bc = bc->parent;
|
||||
}
|
||||
assert(bc && bc == target->compiled);
|
||||
return level;
|
||||
}
|
||||
|
||||
static int count_cfunctions(block b) {
|
||||
int n = 0;
|
||||
for (inst* i = b.first; i; i = i->next) {
|
||||
if (i->op == CLOSURE_CREATE_C) n++;
|
||||
n += count_cfunctions(i->subfn);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
// Expands call instructions into a calling sequence
|
||||
static int expand_call_arglist(struct locfile* locations, block* b) {
|
||||
int errors = 0;
|
||||
block ret = gen_noop();
|
||||
for (inst* curr; (curr = block_take(b));) {
|
||||
if (opcode_describe(curr->op)->flags & OP_HAS_BINDING) {
|
||||
if (!curr->bound_by) {
|
||||
locfile_locate(locations, curr->source, "error: %s is not defined", curr->symbol);
|
||||
errors++;
|
||||
// don't process this instruction if it's not well-defined
|
||||
ret = BLOCK(ret, inst_block(curr));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
block prelude = gen_noop();
|
||||
if (curr->op == CALL_JQ) {
|
||||
int actual_args = 0, desired_args = 0;
|
||||
// We expand the argument list as a series of instructions
|
||||
switch (curr->bound_by->op) {
|
||||
default: assert(0 && "Unknown function type"); break;
|
||||
case CLOSURE_CREATE:
|
||||
case CLOSURE_PARAM: {
|
||||
block callargs = gen_noop();
|
||||
for (inst* i; (i = block_take(&curr->arglist));) {
|
||||
assert(opcode_describe(i->op)->flags & OP_IS_CALL_PSEUDO);
|
||||
block b = inst_block(i);
|
||||
switch (i->op) {
|
||||
default: assert(0 && "Unknown type of parameter"); break;
|
||||
case CLOSURE_REF:
|
||||
block_append(&callargs, b);
|
||||
break;
|
||||
case CLOSURE_CREATE:
|
||||
block_append(&prelude, b);
|
||||
block_append(&callargs, gen_op_bound(CLOSURE_REF, b));
|
||||
break;
|
||||
}
|
||||
actual_args++;
|
||||
}
|
||||
curr->imm.intval = actual_args;
|
||||
curr->arglist = callargs;
|
||||
|
||||
if (curr->bound_by->op == CLOSURE_CREATE) {
|
||||
for (inst* i = curr->bound_by->arglist.first; i; i = i->next) {
|
||||
assert(i->op == CLOSURE_PARAM);
|
||||
desired_args++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case CLOSURE_CREATE_C: {
|
||||
for (inst* i; (i = block_take(&curr->arglist)); ) {
|
||||
assert(i->op == CLOSURE_CREATE); // FIXME
|
||||
block body = i->subfn;
|
||||
i->subfn = gen_noop();
|
||||
inst_free(i);
|
||||
// arguments should be pushed in reverse order, prepend them to prelude
|
||||
errors += expand_call_arglist(locations, &body);
|
||||
prelude = BLOCK(gen_subexp(body), prelude);
|
||||
actual_args++;
|
||||
}
|
||||
assert(curr->op == CALL_JQ);
|
||||
curr->op = CALL_BUILTIN;
|
||||
curr->imm.intval = actual_args + 1 /* include the implicit input in arg count */;
|
||||
assert(curr->bound_by->op == CLOSURE_CREATE_C);
|
||||
desired_args = curr->bound_by->imm.cfunc->nargs - 1;
|
||||
assert(!curr->arglist.first);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (actual_args != desired_args) {
|
||||
locfile_locate(locations, curr->source,
|
||||
"error: %s arguments to %s (expected %d but got %d)",
|
||||
actual_args > desired_args ? "too many" : "too few",
|
||||
curr->symbol, desired_args, actual_args);
|
||||
errors++;
|
||||
}
|
||||
|
||||
}
|
||||
ret = BLOCK(ret, prelude, inst_block(curr));
|
||||
}
|
||||
*b = ret;
|
||||
return errors;
|
||||
}
|
||||
|
||||
static int compile(struct locfile* locations, struct bytecode* bc, block b) {
|
||||
int errors = 0;
|
||||
int pos = 0;
|
||||
int var_frame_idx = 0;
|
||||
bc->nsubfunctions = 0;
|
||||
errors += expand_call_arglist(locations, &b);
|
||||
b = BLOCK(b, gen_op_simple(RET));
|
||||
jv localnames = jv_array();
|
||||
for (inst* curr = b.first; curr; curr = curr->next) {
|
||||
if (!curr->next) assert(curr == b.last);
|
||||
int length = opcode_describe(curr->op)->length;
|
||||
if (curr->op == CALL_JQ) {
|
||||
for (inst* arg = curr->arglist.first; arg; arg = arg->next) {
|
||||
length += 2;
|
||||
}
|
||||
}
|
||||
pos += length;
|
||||
curr->bytecode_pos = pos;
|
||||
curr->compiled = bc;
|
||||
|
||||
assert(curr->op != CLOSURE_REF && curr->op != CLOSURE_PARAM);
|
||||
|
||||
if ((opcode_describe(curr->op)->flags & OP_HAS_VARIABLE) &&
|
||||
curr->bound_by == curr) {
|
||||
curr->imm.intval = var_frame_idx++;
|
||||
localnames = jv_array_append(localnames, jv_string(curr->symbol));
|
||||
}
|
||||
|
||||
if (curr->op == CLOSURE_CREATE) {
|
||||
assert(curr->bound_by == curr);
|
||||
curr->imm.intval = bc->nsubfunctions++;
|
||||
}
|
||||
if (curr->op == CLOSURE_CREATE_C) {
|
||||
assert(curr->bound_by == curr);
|
||||
int idx = bc->globals->ncfunctions++;
|
||||
bc->globals->cfunc_names = jv_array_append(bc->globals->cfunc_names,
|
||||
jv_string(curr->symbol));
|
||||
bc->globals->cfunctions[idx] = *curr->imm.cfunc;
|
||||
curr->imm.intval = idx;
|
||||
}
|
||||
}
|
||||
bc->debuginfo = jv_object_set(bc->debuginfo, jv_string("locals"), localnames);
|
||||
if (bc->nsubfunctions) {
|
||||
bc->subfunctions = jv_mem_alloc(sizeof(struct bytecode*) * bc->nsubfunctions);
|
||||
for (inst* curr = b.first; curr; curr = curr->next) {
|
||||
if (curr->op == CLOSURE_CREATE) {
|
||||
struct bytecode* subfn = jv_mem_alloc(sizeof(struct bytecode));
|
||||
bc->subfunctions[curr->imm.intval] = subfn;
|
||||
subfn->globals = bc->globals;
|
||||
subfn->parent = bc;
|
||||
subfn->nclosures = 0;
|
||||
subfn->debuginfo = jv_object_set(jv_object(), jv_string("name"), jv_string(curr->symbol));
|
||||
jv params = jv_array();
|
||||
for (inst* param = curr->arglist.first; param; param = param->next) {
|
||||
assert(param->op == CLOSURE_PARAM);
|
||||
assert(param->bound_by == param);
|
||||
param->imm.intval = subfn->nclosures++;
|
||||
param->compiled = subfn;
|
||||
params = jv_array_append(params, jv_string(param->symbol));
|
||||
}
|
||||
subfn->debuginfo = jv_object_set(subfn->debuginfo, jv_string("params"), params);
|
||||
errors += compile(locations, subfn, curr->subfn);
|
||||
curr->subfn = gen_noop();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bc->subfunctions = 0;
|
||||
}
|
||||
bc->codelen = pos;
|
||||
uint16_t* code = jv_mem_alloc(sizeof(uint16_t) * bc->codelen);
|
||||
bc->code = code;
|
||||
pos = 0;
|
||||
jv constant_pool = jv_array();
|
||||
int maxvar = -1;
|
||||
if (!errors) for (inst* curr = b.first; curr; curr = curr->next) {
|
||||
const struct opcode_description* op = opcode_describe(curr->op);
|
||||
if (op->length == 0)
|
||||
continue;
|
||||
code[pos++] = curr->op;
|
||||
assert(curr->op != CLOSURE_REF && curr->op != CLOSURE_PARAM);
|
||||
if (curr->op == CALL_BUILTIN) {
|
||||
assert(curr->bound_by->op == CLOSURE_CREATE_C);
|
||||
assert(!curr->arglist.first);
|
||||
code[pos++] = (uint16_t)curr->imm.intval;
|
||||
code[pos++] = curr->bound_by->imm.intval;
|
||||
} else if (curr->op == CALL_JQ) {
|
||||
assert(curr->bound_by->op == CLOSURE_CREATE ||
|
||||
curr->bound_by->op == CLOSURE_PARAM);
|
||||
code[pos++] = (uint16_t)curr->imm.intval;
|
||||
code[pos++] = nesting_level(bc, curr->bound_by);
|
||||
code[pos++] = curr->bound_by->imm.intval |
|
||||
(curr->bound_by->op == CLOSURE_CREATE ? ARG_NEWCLOSURE : 0);
|
||||
for (inst* arg = curr->arglist.first; arg; arg = arg->next) {
|
||||
assert(arg->op == CLOSURE_REF && arg->bound_by->op == CLOSURE_CREATE);
|
||||
code[pos++] = nesting_level(bc, arg->bound_by);
|
||||
code[pos++] = arg->bound_by->imm.intval | ARG_NEWCLOSURE;
|
||||
}
|
||||
} else if (op->flags & OP_HAS_CONSTANT) {
|
||||
code[pos++] = jv_array_length(jv_copy(constant_pool));
|
||||
constant_pool = jv_array_append(constant_pool, jv_copy(curr->imm.constant));
|
||||
} else if (op->flags & OP_HAS_VARIABLE) {
|
||||
code[pos++] = nesting_level(bc, curr->bound_by);
|
||||
uint16_t var = (uint16_t)curr->bound_by->imm.intval;
|
||||
code[pos++] = var;
|
||||
if (var > maxvar) maxvar = var;
|
||||
} else if (op->flags & OP_HAS_BRANCH) {
|
||||
assert(curr->imm.target->bytecode_pos != -1);
|
||||
assert(curr->imm.target->bytecode_pos > pos); // only forward branches
|
||||
code[pos] = curr->imm.target->bytecode_pos - (pos + 1);
|
||||
pos++;
|
||||
} else if (op->length > 1) {
|
||||
assert(0 && "codegen not implemented for this operation");
|
||||
}
|
||||
}
|
||||
bc->constants = constant_pool;
|
||||
bc->nlocals = maxvar + 2; // FIXME: frames of size zero?
|
||||
block_free(b);
|
||||
return errors;
|
||||
}
|
||||
|
||||
int block_compile(block b, struct locfile* locations, struct bytecode** out) {
|
||||
struct bytecode* bc = jv_mem_alloc(sizeof(struct bytecode));
|
||||
bc->parent = 0;
|
||||
bc->nclosures = 0;
|
||||
bc->globals = jv_mem_alloc(sizeof(struct symbol_table));
|
||||
int ncfunc = count_cfunctions(b);
|
||||
bc->globals->ncfunctions = 0;
|
||||
bc->globals->cfunctions = jv_mem_alloc(sizeof(struct cfunction) * ncfunc);
|
||||
bc->globals->cfunc_names = jv_array();
|
||||
bc->debuginfo = jv_object_set(jv_object(), jv_string("name"), jv_null());
|
||||
int nerrors = compile(locations, bc, b);
|
||||
assert(bc->globals->ncfunctions == ncfunc);
|
||||
if (nerrors > 0) {
|
||||
bytecode_free(bc);
|
||||
*out = 0;
|
||||
} else {
|
||||
*out = bc;
|
||||
}
|
||||
return nerrors;
|
||||
}
|
||||
|
||||
void block_free(block b) {
|
||||
struct inst* next;
|
||||
for (struct inst* curr = b.first; curr; curr = next) {
|
||||
next = curr->next;
|
||||
inst_free(curr);
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
#ifndef COMPILE_H
|
||||
#define COMPILE_H
|
||||
#include <stdint.h>
|
||||
#include "jv.h"
|
||||
#include "bytecode.h"
|
||||
#include "locfile.h"
|
||||
|
||||
struct inst;
|
||||
typedef struct inst inst;
|
||||
|
||||
|
||||
typedef struct block {
|
||||
inst* first;
|
||||
inst* last;
|
||||
} block;
|
||||
|
||||
block gen_location(location, block);
|
||||
|
||||
block gen_noop();
|
||||
block gen_op_simple(opcode op);
|
||||
block gen_const(jv constant);
|
||||
block gen_op_target(opcode op, block target);
|
||||
block gen_op_unbound(opcode op, const char* name);
|
||||
block gen_op_bound(opcode op, block binder);
|
||||
block gen_op_var_fresh(opcode op, const char* name);
|
||||
|
||||
block gen_function(const char* name, block formals, block body);
|
||||
block gen_param(const char* name);
|
||||
block gen_lambda(block body);
|
||||
block gen_call(const char* name, block body);
|
||||
block gen_subexp(block a);
|
||||
block gen_both(block a, block b);
|
||||
block gen_collect(block expr);
|
||||
block gen_reduce(const char* varname, block source, block init, block body);
|
||||
block gen_definedor(block a, block b);
|
||||
block gen_condbranch(block iftrue, block iffalse);
|
||||
block gen_and(block a, block b);
|
||||
block gen_or(block a, block b);
|
||||
|
||||
block gen_var_binding(block var, const char* name, block body);
|
||||
|
||||
block gen_cond(block cond, block iftrue, block iffalse);
|
||||
|
||||
block gen_cbinding(const struct cfunction* functions, int nfunctions, block b);
|
||||
|
||||
void block_append(block* b, block b2);
|
||||
block block_join(block a, block b);
|
||||
int block_has_only_binders(block, int bindflags);
|
||||
block block_bind(block binder, block body, int bindflags);
|
||||
block block_bind_referenced(block binder, block body, int bindflags);
|
||||
|
||||
int block_compile(block, struct locfile*, struct bytecode**);
|
||||
|
||||
void block_free(block);
|
||||
|
||||
|
||||
|
||||
// Here's some horrible preprocessor gunk so that code
|
||||
// sequences can be contructed as BLOCK(block1, block2, block3)
|
||||
|
||||
#define BLOCK_1(b1) (b1)
|
||||
#define BLOCK_2(b1,b2) (block_join((b1),(b2)))
|
||||
#define BLOCK_3(b1,b2,b3) (block_join(BLOCK_2(b1,b2),(b3)))
|
||||
#define BLOCK_4(b1,b2,b3,b4) (block_join(BLOCK_3(b1,b2,b3),(b4)))
|
||||
#define BLOCK_5(b1,b2,b3,b4,b5) (block_join(BLOCK_4(b1,b2,b3,b4),(b5)))
|
||||
#define BLOCK_6(b1,b2,b3,b4,b5,b6) (block_join(BLOCK_5(b1,b2,b3,b4,b5),(b6)))
|
||||
#define BLOCK_7(b1,b2,b3,b4,b5,b6,b7) (block_join(BLOCK_6(b1,b2,b3,b4,b5,b6),(b7)))
|
||||
|
||||
#define BLOCK_IDX(_1,_2,_3,_4,_5,_6,_7,NAME,...) NAME
|
||||
#define BLOCK(...) \
|
||||
BLOCK_IDX(__VA_ARGS__, BLOCK_7, BLOCK_6, BLOCK_5, BLOCK_4, BLOCK_3, BLOCK_2, BLOCK_1)(__VA_ARGS__)
|
||||
|
||||
|
||||
#endif
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
/* config.h.in. Generated from configure.ac by autoheader. */
|
||||
|
||||
/* Define to 1 if your C compiler doesn't accept -c and -o together. */
|
||||
#undef NO_MINUS_C_MINUS_O
|
||||
|
||||
/* Name of package */
|
||||
#undef PACKAGE
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#undef PACKAGE_BUGREPORT
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#undef PACKAGE_NAME
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#undef PACKAGE_STRING
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#undef PACKAGE_TARNAME
|
||||
|
||||
/* Define to the home page for this package. */
|
||||
#undef PACKAGE_URL
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#undef PACKAGE_VERSION
|
||||
|
||||
/* Version number of package */
|
||||
#undef VERSION
|
||||
|
||||
/* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a
|
||||
`char[]'. */
|
||||
#undef YYTEXT_POINTER
|
||||
-343
@@ -1,343 +0,0 @@
|
||||
#! /bin/sh
|
||||
# Wrapper for compilers which do not understand '-c -o'.
|
||||
|
||||
scriptversion=2012-03-05.13; # UTC
|
||||
|
||||
# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2009, 2010, 2012 Free
|
||||
# Software Foundation, Inc.
|
||||
# Written by Tom Tromey <tromey@cygnus.com>.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
# This file is maintained in Automake, please report
|
||||
# bugs to <bug-automake@gnu.org> or send patches to
|
||||
# <automake-patches@gnu.org>.
|
||||
|
||||
nl='
|
||||
'
|
||||
|
||||
# We need space, tab and new line, in precisely that order. Quoting is
|
||||
# there to prevent tools from complaining about whitespace usage.
|
||||
IFS=" "" $nl"
|
||||
|
||||
file_conv=
|
||||
|
||||
# func_file_conv build_file lazy
|
||||
# Convert a $build file to $host form and store it in $file
|
||||
# Currently only supports Windows hosts. If the determined conversion
|
||||
# type is listed in (the comma separated) LAZY, no conversion will
|
||||
# take place.
|
||||
func_file_conv ()
|
||||
{
|
||||
file=$1
|
||||
case $file in
|
||||
/ | /[!/]*) # absolute file, and not a UNC file
|
||||
if test -z "$file_conv"; then
|
||||
# lazily determine how to convert abs files
|
||||
case `uname -s` in
|
||||
MINGW*)
|
||||
file_conv=mingw
|
||||
;;
|
||||
CYGWIN*)
|
||||
file_conv=cygwin
|
||||
;;
|
||||
*)
|
||||
file_conv=wine
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
case $file_conv/,$2, in
|
||||
*,$file_conv,*)
|
||||
;;
|
||||
mingw/*)
|
||||
file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
|
||||
;;
|
||||
cygwin/*)
|
||||
file=`cygpath -m "$file" || echo "$file"`
|
||||
;;
|
||||
wine/*)
|
||||
file=`winepath -w "$file" || echo "$file"`
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# func_cl_dashL linkdir
|
||||
# Make cl look for libraries in LINKDIR
|
||||
func_cl_dashL ()
|
||||
{
|
||||
func_file_conv "$1"
|
||||
if test -z "$lib_path"; then
|
||||
lib_path=$file
|
||||
else
|
||||
lib_path="$lib_path;$file"
|
||||
fi
|
||||
linker_opts="$linker_opts -LIBPATH:$file"
|
||||
}
|
||||
|
||||
# func_cl_dashl library
|
||||
# Do a library search-path lookup for cl
|
||||
func_cl_dashl ()
|
||||
{
|
||||
lib=$1
|
||||
found=no
|
||||
save_IFS=$IFS
|
||||
IFS=';'
|
||||
for dir in $lib_path $LIB
|
||||
do
|
||||
IFS=$save_IFS
|
||||
if $shared && test -f "$dir/$lib.dll.lib"; then
|
||||
found=yes
|
||||
lib=$dir/$lib.dll.lib
|
||||
break
|
||||
fi
|
||||
if test -f "$dir/$lib.lib"; then
|
||||
found=yes
|
||||
lib=$dir/$lib.lib
|
||||
break
|
||||
fi
|
||||
done
|
||||
IFS=$save_IFS
|
||||
|
||||
if test "$found" != yes; then
|
||||
lib=$lib.lib
|
||||
fi
|
||||
}
|
||||
|
||||
# func_cl_wrapper cl arg...
|
||||
# Adjust compile command to suit cl
|
||||
func_cl_wrapper ()
|
||||
{
|
||||
# Assume a capable shell
|
||||
lib_path=
|
||||
shared=:
|
||||
linker_opts=
|
||||
for arg
|
||||
do
|
||||
if test -n "$eat"; then
|
||||
eat=
|
||||
else
|
||||
case $1 in
|
||||
-o)
|
||||
# configure might choose to run compile as 'compile cc -o foo foo.c'.
|
||||
eat=1
|
||||
case $2 in
|
||||
*.o | *.[oO][bB][jJ])
|
||||
func_file_conv "$2"
|
||||
set x "$@" -Fo"$file"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
func_file_conv "$2"
|
||||
set x "$@" -Fe"$file"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
-I)
|
||||
eat=1
|
||||
func_file_conv "$2" mingw
|
||||
set x "$@" -I"$file"
|
||||
shift
|
||||
;;
|
||||
-I*)
|
||||
func_file_conv "${1#-I}" mingw
|
||||
set x "$@" -I"$file"
|
||||
shift
|
||||
;;
|
||||
-l)
|
||||
eat=1
|
||||
func_cl_dashl "$2"
|
||||
set x "$@" "$lib"
|
||||
shift
|
||||
;;
|
||||
-l*)
|
||||
func_cl_dashl "${1#-l}"
|
||||
set x "$@" "$lib"
|
||||
shift
|
||||
;;
|
||||
-L)
|
||||
eat=1
|
||||
func_cl_dashL "$2"
|
||||
;;
|
||||
-L*)
|
||||
func_cl_dashL "${1#-L}"
|
||||
;;
|
||||
-static)
|
||||
shared=false
|
||||
;;
|
||||
-Wl,*)
|
||||
arg=${1#-Wl,}
|
||||
save_ifs="$IFS"; IFS=','
|
||||
for flag in $arg; do
|
||||
IFS="$save_ifs"
|
||||
linker_opts="$linker_opts $flag"
|
||||
done
|
||||
IFS="$save_ifs"
|
||||
;;
|
||||
-Xlinker)
|
||||
eat=1
|
||||
linker_opts="$linker_opts $2"
|
||||
;;
|
||||
-*)
|
||||
set x "$@" "$1"
|
||||
shift
|
||||
;;
|
||||
*.cc | *.CC | *.cxx | *.CXX | *.[cC]++)
|
||||
func_file_conv "$1"
|
||||
set x "$@" -Tp"$file"
|
||||
shift
|
||||
;;
|
||||
*.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO])
|
||||
func_file_conv "$1" mingw
|
||||
set x "$@" "$file"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set x "$@" "$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
shift
|
||||
done
|
||||
if test -n "$linker_opts"; then
|
||||
linker_opts="-link$linker_opts"
|
||||
fi
|
||||
exec "$@" $linker_opts
|
||||
exit 1
|
||||
}
|
||||
|
||||
eat=
|
||||
|
||||
case $1 in
|
||||
'')
|
||||
echo "$0: No command. Try '$0 --help' for more information." 1>&2
|
||||
exit 1;
|
||||
;;
|
||||
-h | --h*)
|
||||
cat <<\EOF
|
||||
Usage: compile [--help] [--version] PROGRAM [ARGS]
|
||||
|
||||
Wrapper for compilers which do not understand '-c -o'.
|
||||
Remove '-o dest.o' from ARGS, run PROGRAM with the remaining
|
||||
arguments, and rename the output as expected.
|
||||
|
||||
If you are trying to build a whole package this is not the
|
||||
right script to run: please start by reading the file 'INSTALL'.
|
||||
|
||||
Report bugs to <bug-automake@gnu.org>.
|
||||
EOF
|
||||
exit $?
|
||||
;;
|
||||
-v | --v*)
|
||||
echo "compile $scriptversion"
|
||||
exit $?
|
||||
;;
|
||||
cl | *[/\\]cl | cl.exe | *[/\\]cl.exe )
|
||||
func_cl_wrapper "$@" # Doesn't return...
|
||||
;;
|
||||
esac
|
||||
|
||||
ofile=
|
||||
cfile=
|
||||
|
||||
for arg
|
||||
do
|
||||
if test -n "$eat"; then
|
||||
eat=
|
||||
else
|
||||
case $1 in
|
||||
-o)
|
||||
# configure might choose to run compile as 'compile cc -o foo foo.c'.
|
||||
# So we strip '-o arg' only if arg is an object.
|
||||
eat=1
|
||||
case $2 in
|
||||
*.o | *.obj)
|
||||
ofile=$2
|
||||
;;
|
||||
*)
|
||||
set x "$@" -o "$2"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*.c)
|
||||
cfile=$1
|
||||
set x "$@" "$1"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set x "$@" "$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
shift
|
||||
done
|
||||
|
||||
if test -z "$ofile" || test -z "$cfile"; then
|
||||
# If no '-o' option was seen then we might have been invoked from a
|
||||
# pattern rule where we don't need one. That is ok -- this is a
|
||||
# normal compilation that the losing compiler can handle. If no
|
||||
# '.c' file was seen then we are probably linking. That is also
|
||||
# ok.
|
||||
exec "$@"
|
||||
fi
|
||||
|
||||
# Name of file we expect compiler to create.
|
||||
cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'`
|
||||
|
||||
# Create the lock directory.
|
||||
# Note: use '[/\\:.-]' here to ensure that we don't use the same name
|
||||
# that we are using for the .o file. Also, base the name on the expected
|
||||
# object file name, since that is what matters with a parallel build.
|
||||
lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d
|
||||
while true; do
|
||||
if mkdir "$lockdir" >/dev/null 2>&1; then
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
# FIXME: race condition here if user kills between mkdir and trap.
|
||||
trap "rmdir '$lockdir'; exit 1" 1 2 15
|
||||
|
||||
# Run the compile.
|
||||
"$@"
|
||||
ret=$?
|
||||
|
||||
if test -f "$cofile"; then
|
||||
test "$cofile" = "$ofile" || mv "$cofile" "$ofile"
|
||||
elif test -f "${cofile}bj"; then
|
||||
test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile"
|
||||
fi
|
||||
|
||||
rmdir "$lockdir"
|
||||
exit $ret
|
||||
|
||||
# Local Variables:
|
||||
# mode: shell-script
|
||||
# sh-indentation: 2
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
||||
-708
@@ -1,708 +0,0 @@
|
||||
#! /bin/sh
|
||||
# depcomp - compile a program generating dependencies as side-effects
|
||||
|
||||
scriptversion=2012-03-27.16; # UTC
|
||||
|
||||
# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2006, 2007, 2009, 2010,
|
||||
# 2011, 2012 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
|
||||
|
||||
case $1 in
|
||||
'')
|
||||
echo "$0: No command. Try '$0 --help' for more information." 1>&2
|
||||
exit 1;
|
||||
;;
|
||||
-h | --h*)
|
||||
cat <<\EOF
|
||||
Usage: depcomp [--help] [--version] PROGRAM [ARGS]
|
||||
|
||||
Run PROGRAMS ARGS to compile a file, generating dependencies
|
||||
as side-effects.
|
||||
|
||||
Environment variables:
|
||||
depmode Dependency tracking mode.
|
||||
source Source file read by 'PROGRAMS ARGS'.
|
||||
object Object file output by 'PROGRAMS ARGS'.
|
||||
DEPDIR directory where to store dependencies.
|
||||
depfile Dependency file to output.
|
||||
tmpdepfile Temporary file to use when outputting dependencies.
|
||||
libtool Whether libtool is used (yes/no).
|
||||
|
||||
Report bugs to <bug-automake@gnu.org>.
|
||||
EOF
|
||||
exit $?
|
||||
;;
|
||||
-v | --v*)
|
||||
echo "depcomp $scriptversion"
|
||||
exit $?
|
||||
;;
|
||||
esac
|
||||
|
||||
# A tabulation character.
|
||||
tab=' '
|
||||
# A newline character.
|
||||
nl='
|
||||
'
|
||||
|
||||
if test -z "$depmode" || test -z "$source" || test -z "$object"; then
|
||||
echo "depcomp: Variables source, object and depmode must be set" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
|
||||
depfile=${depfile-`echo "$object" |
|
||||
sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
|
||||
tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
|
||||
|
||||
rm -f "$tmpdepfile"
|
||||
|
||||
# Some modes work just like other modes, but use different flags. We
|
||||
# parameterize here, but still list the modes in the big case below,
|
||||
# to make depend.m4 easier to write. Note that we *cannot* use a case
|
||||
# here, because this file can only contain one case statement.
|
||||
if test "$depmode" = hp; then
|
||||
# HP compiler uses -M and no extra arg.
|
||||
gccflag=-M
|
||||
depmode=gcc
|
||||
fi
|
||||
|
||||
if test "$depmode" = dashXmstdout; then
|
||||
# This is just like dashmstdout with a different argument.
|
||||
dashmflag=-xM
|
||||
depmode=dashmstdout
|
||||
fi
|
||||
|
||||
cygpath_u="cygpath -u -f -"
|
||||
if test "$depmode" = msvcmsys; then
|
||||
# This is just like msvisualcpp but w/o cygpath translation.
|
||||
# Just convert the backslash-escaped backslashes to single forward
|
||||
# slashes to satisfy depend.m4
|
||||
cygpath_u='sed s,\\\\,/,g'
|
||||
depmode=msvisualcpp
|
||||
fi
|
||||
|
||||
if test "$depmode" = msvc7msys; then
|
||||
# This is just like msvc7 but w/o cygpath translation.
|
||||
# Just convert the backslash-escaped backslashes to single forward
|
||||
# slashes to satisfy depend.m4
|
||||
cygpath_u='sed s,\\\\,/,g'
|
||||
depmode=msvc7
|
||||
fi
|
||||
|
||||
if test "$depmode" = xlc; then
|
||||
# IBM C/C++ Compilers xlc/xlC can output gcc-like dependency informations.
|
||||
gccflag=-qmakedep=gcc,-MF
|
||||
depmode=gcc
|
||||
fi
|
||||
|
||||
case "$depmode" in
|
||||
gcc3)
|
||||
## gcc 3 implements dependency tracking that does exactly what
|
||||
## we want. Yay! Note: for some reason libtool 1.4 doesn't like
|
||||
## it if -MD -MP comes after the -MF stuff. Hmm.
|
||||
## Unfortunately, FreeBSD c89 acceptance of flags depends upon
|
||||
## the command line argument order; so add the flags where they
|
||||
## appear in depend2.am. Note that the slowdown incurred here
|
||||
## affects only configure: in makefiles, %FASTDEP% shortcuts this.
|
||||
for arg
|
||||
do
|
||||
case $arg in
|
||||
-c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;;
|
||||
*) set fnord "$@" "$arg" ;;
|
||||
esac
|
||||
shift # fnord
|
||||
shift # $arg
|
||||
done
|
||||
"$@"
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
mv "$tmpdepfile" "$depfile"
|
||||
;;
|
||||
|
||||
gcc)
|
||||
## There are various ways to get dependency output from gcc. Here's
|
||||
## why we pick this rather obscure method:
|
||||
## - Don't want to use -MD because we'd like the dependencies to end
|
||||
## up in a subdir. Having to rename by hand is ugly.
|
||||
## (We might end up doing this anyway to support other compilers.)
|
||||
## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
|
||||
## -MM, not -M (despite what the docs say).
|
||||
## - Using -M directly means running the compiler twice (even worse
|
||||
## than renaming).
|
||||
if test -z "$gccflag"; then
|
||||
gccflag=-MD,
|
||||
fi
|
||||
"$@" -Wp,"$gccflag$tmpdepfile"
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
rm -f "$depfile"
|
||||
echo "$object : \\" > "$depfile"
|
||||
alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
|
||||
## The second -e expression handles DOS-style file names with drive letters.
|
||||
sed -e 's/^[^:]*: / /' \
|
||||
-e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
|
||||
## This next piece of magic avoids the "deleted header file" problem.
|
||||
## The problem is that when a header file which appears in a .P file
|
||||
## is deleted, the dependency causes make to die (because there is
|
||||
## typically no way to rebuild the header). We avoid this by adding
|
||||
## dummy dependencies for each header file. Too bad gcc doesn't do
|
||||
## this for us directly.
|
||||
tr ' ' "$nl" < "$tmpdepfile" |
|
||||
## Some versions of gcc put a space before the ':'. On the theory
|
||||
## that the space means something, we add a space to the output as
|
||||
## well. hp depmode also adds that space, but also prefixes the VPATH
|
||||
## to the object. Take care to not repeat it in the output.
|
||||
## Some versions of the HPUX 10.20 sed can't process this invocation
|
||||
## correctly. Breaking it into two sed invocations is a workaround.
|
||||
sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \
|
||||
| sed -e 's/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
hp)
|
||||
# This case exists only to let depend.m4 do its work. It works by
|
||||
# looking at the text of this script. This case will never be run,
|
||||
# since it is checked for above.
|
||||
exit 1
|
||||
;;
|
||||
|
||||
sgi)
|
||||
if test "$libtool" = yes; then
|
||||
"$@" "-Wp,-MDupdate,$tmpdepfile"
|
||||
else
|
||||
"$@" -MDupdate "$tmpdepfile"
|
||||
fi
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
rm -f "$depfile"
|
||||
|
||||
if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files
|
||||
echo "$object : \\" > "$depfile"
|
||||
|
||||
# Clip off the initial element (the dependent). Don't try to be
|
||||
# clever and replace this with sed code, as IRIX sed won't handle
|
||||
# lines with more than a fixed number of characters (4096 in
|
||||
# IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines;
|
||||
# the IRIX cc adds comments like '#:fec' to the end of the
|
||||
# dependency line.
|
||||
tr ' ' "$nl" < "$tmpdepfile" \
|
||||
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \
|
||||
tr "$nl" ' ' >> "$depfile"
|
||||
echo >> "$depfile"
|
||||
|
||||
# The second pass generates a dummy entry for each header file.
|
||||
tr ' ' "$nl" < "$tmpdepfile" \
|
||||
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
|
||||
>> "$depfile"
|
||||
else
|
||||
# The sourcefile does not contain any dependencies, so just
|
||||
# store a dummy comment line, to avoid errors with the Makefile
|
||||
# "include basename.Plo" scheme.
|
||||
echo "#dummy" > "$depfile"
|
||||
fi
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
xlc)
|
||||
# This case exists only to let depend.m4 do its work. It works by
|
||||
# looking at the text of this script. This case will never be run,
|
||||
# since it is checked for above.
|
||||
exit 1
|
||||
;;
|
||||
|
||||
aix)
|
||||
# The C for AIX Compiler uses -M and outputs the dependencies
|
||||
# in a .u file. In older versions, this file always lives in the
|
||||
# current directory. Also, the AIX compiler puts '$object:' at the
|
||||
# start of each line; $object doesn't have directory information.
|
||||
# Version 6 uses the directory in both cases.
|
||||
dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
|
||||
test "x$dir" = "x$object" && dir=
|
||||
base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
|
||||
if test "$libtool" = yes; then
|
||||
tmpdepfile1=$dir$base.u
|
||||
tmpdepfile2=$base.u
|
||||
tmpdepfile3=$dir.libs/$base.u
|
||||
"$@" -Wc,-M
|
||||
else
|
||||
tmpdepfile1=$dir$base.u
|
||||
tmpdepfile2=$dir$base.u
|
||||
tmpdepfile3=$dir$base.u
|
||||
"$@" -M
|
||||
fi
|
||||
stat=$?
|
||||
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
|
||||
exit $stat
|
||||
fi
|
||||
|
||||
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
|
||||
do
|
||||
test -f "$tmpdepfile" && break
|
||||
done
|
||||
if test -f "$tmpdepfile"; then
|
||||
# Each line is of the form 'foo.o: dependent.h'.
|
||||
# Do two passes, one to just change these to
|
||||
# '$object: dependent.h' and one to simply 'dependent.h:'.
|
||||
sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
|
||||
sed -e 's,^.*\.[a-z]*:['"$tab"' ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
|
||||
else
|
||||
# The sourcefile does not contain any dependencies, so just
|
||||
# store a dummy comment line, to avoid errors with the Makefile
|
||||
# "include basename.Plo" scheme.
|
||||
echo "#dummy" > "$depfile"
|
||||
fi
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
icc)
|
||||
# Intel's C compiler anf tcc (Tiny C Compiler) understand '-MD -MF file'.
|
||||
# However on
|
||||
# $CC -MD -MF foo.d -c -o sub/foo.o sub/foo.c
|
||||
# ICC 7.0 will fill foo.d with something like
|
||||
# foo.o: sub/foo.c
|
||||
# foo.o: sub/foo.h
|
||||
# which is wrong. We want
|
||||
# sub/foo.o: sub/foo.c
|
||||
# sub/foo.o: sub/foo.h
|
||||
# sub/foo.c:
|
||||
# sub/foo.h:
|
||||
# ICC 7.1 will output
|
||||
# foo.o: sub/foo.c sub/foo.h
|
||||
# and will wrap long lines using '\':
|
||||
# foo.o: sub/foo.c ... \
|
||||
# sub/foo.h ... \
|
||||
# ...
|
||||
# tcc 0.9.26 (FIXME still under development at the moment of writing)
|
||||
# will emit a similar output, but also prepend the continuation lines
|
||||
# with horizontal tabulation characters.
|
||||
"$@" -MD -MF "$tmpdepfile"
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
rm -f "$depfile"
|
||||
# Each line is of the form 'foo.o: dependent.h',
|
||||
# or 'foo.o: dep1.h dep2.h \', or ' dep3.h dep4.h \'.
|
||||
# Do two passes, one to just change these to
|
||||
# '$object: dependent.h' and one to simply 'dependent.h:'.
|
||||
sed -e "s/^[ $tab][ $tab]*/ /" -e "s,^[^:]*:,$object :," \
|
||||
< "$tmpdepfile" > "$depfile"
|
||||
sed '
|
||||
s/[ '"$tab"'][ '"$tab"']*/ /g
|
||||
s/^ *//
|
||||
s/ *\\*$//
|
||||
s/^[^:]*: *//
|
||||
/^$/d
|
||||
/:$/d
|
||||
s/$/ :/
|
||||
' < "$tmpdepfile" >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
hp2)
|
||||
# The "hp" stanza above does not work with aCC (C++) and HP's ia64
|
||||
# compilers, which have integrated preprocessors. The correct option
|
||||
# to use with these is +Maked; it writes dependencies to a file named
|
||||
# 'foo.d', which lands next to the object file, wherever that
|
||||
# happens to be.
|
||||
# Much of this is similar to the tru64 case; see comments there.
|
||||
dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
|
||||
test "x$dir" = "x$object" && dir=
|
||||
base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
|
||||
if test "$libtool" = yes; then
|
||||
tmpdepfile1=$dir$base.d
|
||||
tmpdepfile2=$dir.libs/$base.d
|
||||
"$@" -Wc,+Maked
|
||||
else
|
||||
tmpdepfile1=$dir$base.d
|
||||
tmpdepfile2=$dir$base.d
|
||||
"$@" +Maked
|
||||
fi
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile1" "$tmpdepfile2"
|
||||
exit $stat
|
||||
fi
|
||||
|
||||
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2"
|
||||
do
|
||||
test -f "$tmpdepfile" && break
|
||||
done
|
||||
if test -f "$tmpdepfile"; then
|
||||
sed -e "s,^.*\.[a-z]*:,$object:," "$tmpdepfile" > "$depfile"
|
||||
# Add 'dependent.h:' lines.
|
||||
sed -ne '2,${
|
||||
s/^ *//
|
||||
s/ \\*$//
|
||||
s/$/:/
|
||||
p
|
||||
}' "$tmpdepfile" >> "$depfile"
|
||||
else
|
||||
echo "#dummy" > "$depfile"
|
||||
fi
|
||||
rm -f "$tmpdepfile" "$tmpdepfile2"
|
||||
;;
|
||||
|
||||
tru64)
|
||||
# The Tru64 compiler uses -MD to generate dependencies as a side
|
||||
# effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'.
|
||||
# At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
|
||||
# dependencies in 'foo.d' instead, so we check for that too.
|
||||
# Subdirectories are respected.
|
||||
dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
|
||||
test "x$dir" = "x$object" && dir=
|
||||
base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
|
||||
|
||||
if test "$libtool" = yes; then
|
||||
# With Tru64 cc, shared objects can also be used to make a
|
||||
# static library. This mechanism is used in libtool 1.4 series to
|
||||
# handle both shared and static libraries in a single compilation.
|
||||
# With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d.
|
||||
#
|
||||
# With libtool 1.5 this exception was removed, and libtool now
|
||||
# generates 2 separate objects for the 2 libraries. These two
|
||||
# compilations output dependencies in $dir.libs/$base.o.d and
|
||||
# in $dir$base.o.d. We have to check for both files, because
|
||||
# one of the two compilations can be disabled. We should prefer
|
||||
# $dir$base.o.d over $dir.libs/$base.o.d because the latter is
|
||||
# automatically cleaned when .libs/ is deleted, while ignoring
|
||||
# the former would cause a distcleancheck panic.
|
||||
tmpdepfile1=$dir.libs/$base.lo.d # libtool 1.4
|
||||
tmpdepfile2=$dir$base.o.d # libtool 1.5
|
||||
tmpdepfile3=$dir.libs/$base.o.d # libtool 1.5
|
||||
tmpdepfile4=$dir.libs/$base.d # Compaq CCC V6.2-504
|
||||
"$@" -Wc,-MD
|
||||
else
|
||||
tmpdepfile1=$dir$base.o.d
|
||||
tmpdepfile2=$dir$base.d
|
||||
tmpdepfile3=$dir$base.d
|
||||
tmpdepfile4=$dir$base.d
|
||||
"$@" -MD
|
||||
fi
|
||||
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
|
||||
exit $stat
|
||||
fi
|
||||
|
||||
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
|
||||
do
|
||||
test -f "$tmpdepfile" && break
|
||||
done
|
||||
if test -f "$tmpdepfile"; then
|
||||
sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
|
||||
sed -e 's,^.*\.[a-z]*:['"$tab"' ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
|
||||
else
|
||||
echo "#dummy" > "$depfile"
|
||||
fi
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
msvc7)
|
||||
if test "$libtool" = yes; then
|
||||
showIncludes=-Wc,-showIncludes
|
||||
else
|
||||
showIncludes=-showIncludes
|
||||
fi
|
||||
"$@" $showIncludes > "$tmpdepfile"
|
||||
stat=$?
|
||||
grep -v '^Note: including file: ' "$tmpdepfile"
|
||||
if test "$stat" = 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
rm -f "$depfile"
|
||||
echo "$object : \\" > "$depfile"
|
||||
# The first sed program below extracts the file names and escapes
|
||||
# backslashes for cygpath. The second sed program outputs the file
|
||||
# name when reading, but also accumulates all include files in the
|
||||
# hold buffer in order to output them again at the end. This only
|
||||
# works with sed implementations that can handle large buffers.
|
||||
sed < "$tmpdepfile" -n '
|
||||
/^Note: including file: *\(.*\)/ {
|
||||
s//\1/
|
||||
s/\\/\\\\/g
|
||||
p
|
||||
}' | $cygpath_u | sort -u | sed -n '
|
||||
s/ /\\ /g
|
||||
s/\(.*\)/'"$tab"'\1 \\/p
|
||||
s/.\(.*\) \\/\1:/
|
||||
H
|
||||
$ {
|
||||
s/.*/'"$tab"'/
|
||||
G
|
||||
p
|
||||
}' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
msvc7msys)
|
||||
# This case exists only to let depend.m4 do its work. It works by
|
||||
# looking at the text of this script. This case will never be run,
|
||||
# since it is checked for above.
|
||||
exit 1
|
||||
;;
|
||||
|
||||
#nosideeffect)
|
||||
# This comment above is used by automake to tell side-effect
|
||||
# dependency tracking mechanisms from slower ones.
|
||||
|
||||
dashmstdout)
|
||||
# Important note: in order to support this mode, a compiler *must*
|
||||
# always write the preprocessed file to stdout, regardless of -o.
|
||||
"$@" || exit $?
|
||||
|
||||
# Remove the call to Libtool.
|
||||
if test "$libtool" = yes; then
|
||||
while test "X$1" != 'X--mode=compile'; do
|
||||
shift
|
||||
done
|
||||
shift
|
||||
fi
|
||||
|
||||
# Remove '-o $object'.
|
||||
IFS=" "
|
||||
for arg
|
||||
do
|
||||
case $arg in
|
||||
-o)
|
||||
shift
|
||||
;;
|
||||
$object)
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set fnord "$@" "$arg"
|
||||
shift # fnord
|
||||
shift # $arg
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
test -z "$dashmflag" && dashmflag=-M
|
||||
# Require at least two characters before searching for ':'
|
||||
# in the target name. This is to cope with DOS-style filenames:
|
||||
# a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise.
|
||||
"$@" $dashmflag |
|
||||
sed 's:^['"$tab"' ]*[^:'"$tab"' ][^:][^:]*\:['"$tab"' ]*:'"$object"'\: :' > "$tmpdepfile"
|
||||
rm -f "$depfile"
|
||||
cat < "$tmpdepfile" > "$depfile"
|
||||
tr ' ' "$nl" < "$tmpdepfile" | \
|
||||
## Some versions of the HPUX 10.20 sed can't process this invocation
|
||||
## correctly. Breaking it into two sed invocations is a workaround.
|
||||
sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
dashXmstdout)
|
||||
# This case only exists to satisfy depend.m4. It is never actually
|
||||
# run, as this mode is specially recognized in the preamble.
|
||||
exit 1
|
||||
;;
|
||||
|
||||
makedepend)
|
||||
"$@" || exit $?
|
||||
# Remove any Libtool call
|
||||
if test "$libtool" = yes; then
|
||||
while test "X$1" != 'X--mode=compile'; do
|
||||
shift
|
||||
done
|
||||
shift
|
||||
fi
|
||||
# X makedepend
|
||||
shift
|
||||
cleared=no eat=no
|
||||
for arg
|
||||
do
|
||||
case $cleared in
|
||||
no)
|
||||
set ""; shift
|
||||
cleared=yes ;;
|
||||
esac
|
||||
if test $eat = yes; then
|
||||
eat=no
|
||||
continue
|
||||
fi
|
||||
case "$arg" in
|
||||
-D*|-I*)
|
||||
set fnord "$@" "$arg"; shift ;;
|
||||
# Strip any option that makedepend may not understand. Remove
|
||||
# the object too, otherwise makedepend will parse it as a source file.
|
||||
-arch)
|
||||
eat=yes ;;
|
||||
-*|$object)
|
||||
;;
|
||||
*)
|
||||
set fnord "$@" "$arg"; shift ;;
|
||||
esac
|
||||
done
|
||||
obj_suffix=`echo "$object" | sed 's/^.*\././'`
|
||||
touch "$tmpdepfile"
|
||||
${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
|
||||
rm -f "$depfile"
|
||||
# makedepend may prepend the VPATH from the source file name to the object.
|
||||
# No need to regex-escape $object, excess matching of '.' is harmless.
|
||||
sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile"
|
||||
sed '1,2d' "$tmpdepfile" | tr ' ' "$nl" | \
|
||||
## Some versions of the HPUX 10.20 sed can't process this invocation
|
||||
## correctly. Breaking it into two sed invocations is a workaround.
|
||||
sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile" "$tmpdepfile".bak
|
||||
;;
|
||||
|
||||
cpp)
|
||||
# Important note: in order to support this mode, a compiler *must*
|
||||
# always write the preprocessed file to stdout.
|
||||
"$@" || exit $?
|
||||
|
||||
# Remove the call to Libtool.
|
||||
if test "$libtool" = yes; then
|
||||
while test "X$1" != 'X--mode=compile'; do
|
||||
shift
|
||||
done
|
||||
shift
|
||||
fi
|
||||
|
||||
# Remove '-o $object'.
|
||||
IFS=" "
|
||||
for arg
|
||||
do
|
||||
case $arg in
|
||||
-o)
|
||||
shift
|
||||
;;
|
||||
$object)
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set fnord "$@" "$arg"
|
||||
shift # fnord
|
||||
shift # $arg
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
"$@" -E |
|
||||
sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
|
||||
-e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' |
|
||||
sed '$ s: \\$::' > "$tmpdepfile"
|
||||
rm -f "$depfile"
|
||||
echo "$object : \\" > "$depfile"
|
||||
cat < "$tmpdepfile" >> "$depfile"
|
||||
sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
msvisualcpp)
|
||||
# Important note: in order to support this mode, a compiler *must*
|
||||
# always write the preprocessed file to stdout.
|
||||
"$@" || exit $?
|
||||
|
||||
# Remove the call to Libtool.
|
||||
if test "$libtool" = yes; then
|
||||
while test "X$1" != 'X--mode=compile'; do
|
||||
shift
|
||||
done
|
||||
shift
|
||||
fi
|
||||
|
||||
IFS=" "
|
||||
for arg
|
||||
do
|
||||
case "$arg" in
|
||||
-o)
|
||||
shift
|
||||
;;
|
||||
$object)
|
||||
shift
|
||||
;;
|
||||
"-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
|
||||
set fnord "$@"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set fnord "$@" "$arg"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
"$@" -E 2>/dev/null |
|
||||
sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile"
|
||||
rm -f "$depfile"
|
||||
echo "$object : \\" > "$depfile"
|
||||
sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile"
|
||||
echo "$tab" >> "$depfile"
|
||||
sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
msvcmsys)
|
||||
# This case exists only to let depend.m4 do its work. It works by
|
||||
# looking at the text of this script. This case will never be run,
|
||||
# since it is checked for above.
|
||||
exit 1
|
||||
;;
|
||||
|
||||
none)
|
||||
exec "$@"
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Unknown depmode $depmode" 1>&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
|
||||
# Local Variables:
|
||||
# mode: shell-script
|
||||
# sh-indentation: 2
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
||||
@@ -1,527 +0,0 @@
|
||||
#!/bin/sh
|
||||
# install - install a program, script, or datafile
|
||||
|
||||
scriptversion=2011-01-19.21; # UTC
|
||||
|
||||
# This originates from X11R5 (mit/util/scripts/install.sh), which was
|
||||
# later released in X11R6 (xc/config/util/install.sh) with the
|
||||
# following copyright and license.
|
||||
#
|
||||
# Copyright (C) 1994 X Consortium
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
# deal in the Software without restriction, including without limitation the
|
||||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
# sell copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
|
||||
# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# Except as contained in this notice, the name of the X Consortium shall not
|
||||
# be used in advertising or otherwise to promote the sale, use or other deal-
|
||||
# ings in this Software without prior written authorization from the X Consor-
|
||||
# tium.
|
||||
#
|
||||
#
|
||||
# FSF changes to this file are in the public domain.
|
||||
#
|
||||
# Calling this script install-sh is preferred over install.sh, to prevent
|
||||
# `make' implicit rules from creating a file called install from it
|
||||
# when there is no Makefile.
|
||||
#
|
||||
# This script is compatible with the BSD install script, but was written
|
||||
# from scratch.
|
||||
|
||||
nl='
|
||||
'
|
||||
IFS=" "" $nl"
|
||||
|
||||
# set DOITPROG to echo to test this script
|
||||
|
||||
# Don't use :- since 4.3BSD and earlier shells don't like it.
|
||||
doit=${DOITPROG-}
|
||||
if test -z "$doit"; then
|
||||
doit_exec=exec
|
||||
else
|
||||
doit_exec=$doit
|
||||
fi
|
||||
|
||||
# Put in absolute file names if you don't have them in your path;
|
||||
# or use environment vars.
|
||||
|
||||
chgrpprog=${CHGRPPROG-chgrp}
|
||||
chmodprog=${CHMODPROG-chmod}
|
||||
chownprog=${CHOWNPROG-chown}
|
||||
cmpprog=${CMPPROG-cmp}
|
||||
cpprog=${CPPROG-cp}
|
||||
mkdirprog=${MKDIRPROG-mkdir}
|
||||
mvprog=${MVPROG-mv}
|
||||
rmprog=${RMPROG-rm}
|
||||
stripprog=${STRIPPROG-strip}
|
||||
|
||||
posix_glob='?'
|
||||
initialize_posix_glob='
|
||||
test "$posix_glob" != "?" || {
|
||||
if (set -f) 2>/dev/null; then
|
||||
posix_glob=
|
||||
else
|
||||
posix_glob=:
|
||||
fi
|
||||
}
|
||||
'
|
||||
|
||||
posix_mkdir=
|
||||
|
||||
# Desired mode of installed file.
|
||||
mode=0755
|
||||
|
||||
chgrpcmd=
|
||||
chmodcmd=$chmodprog
|
||||
chowncmd=
|
||||
mvcmd=$mvprog
|
||||
rmcmd="$rmprog -f"
|
||||
stripcmd=
|
||||
|
||||
src=
|
||||
dst=
|
||||
dir_arg=
|
||||
dst_arg=
|
||||
|
||||
copy_on_change=false
|
||||
no_target_directory=
|
||||
|
||||
usage="\
|
||||
Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
|
||||
or: $0 [OPTION]... SRCFILES... DIRECTORY
|
||||
or: $0 [OPTION]... -t DIRECTORY SRCFILES...
|
||||
or: $0 [OPTION]... -d DIRECTORIES...
|
||||
|
||||
In the 1st form, copy SRCFILE to DSTFILE.
|
||||
In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
|
||||
In the 4th, create DIRECTORIES.
|
||||
|
||||
Options:
|
||||
--help display this help and exit.
|
||||
--version display version info and exit.
|
||||
|
||||
-c (ignored)
|
||||
-C install only if different (preserve the last data modification time)
|
||||
-d create directories instead of installing files.
|
||||
-g GROUP $chgrpprog installed files to GROUP.
|
||||
-m MODE $chmodprog installed files to MODE.
|
||||
-o USER $chownprog installed files to USER.
|
||||
-s $stripprog installed files.
|
||||
-t DIRECTORY install into DIRECTORY.
|
||||
-T report an error if DSTFILE is a directory.
|
||||
|
||||
Environment variables override the default commands:
|
||||
CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
|
||||
RMPROG STRIPPROG
|
||||
"
|
||||
|
||||
while test $# -ne 0; do
|
||||
case $1 in
|
||||
-c) ;;
|
||||
|
||||
-C) copy_on_change=true;;
|
||||
|
||||
-d) dir_arg=true;;
|
||||
|
||||
-g) chgrpcmd="$chgrpprog $2"
|
||||
shift;;
|
||||
|
||||
--help) echo "$usage"; exit $?;;
|
||||
|
||||
-m) mode=$2
|
||||
case $mode in
|
||||
*' '* | *' '* | *'
|
||||
'* | *'*'* | *'?'* | *'['*)
|
||||
echo "$0: invalid mode: $mode" >&2
|
||||
exit 1;;
|
||||
esac
|
||||
shift;;
|
||||
|
||||
-o) chowncmd="$chownprog $2"
|
||||
shift;;
|
||||
|
||||
-s) stripcmd=$stripprog;;
|
||||
|
||||
-t) dst_arg=$2
|
||||
# Protect names problematic for `test' and other utilities.
|
||||
case $dst_arg in
|
||||
-* | [=\(\)!]) dst_arg=./$dst_arg;;
|
||||
esac
|
||||
shift;;
|
||||
|
||||
-T) no_target_directory=true;;
|
||||
|
||||
--version) echo "$0 $scriptversion"; exit $?;;
|
||||
|
||||
--) shift
|
||||
break;;
|
||||
|
||||
-*) echo "$0: invalid option: $1" >&2
|
||||
exit 1;;
|
||||
|
||||
*) break;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
|
||||
# When -d is used, all remaining arguments are directories to create.
|
||||
# When -t is used, the destination is already specified.
|
||||
# Otherwise, the last argument is the destination. Remove it from $@.
|
||||
for arg
|
||||
do
|
||||
if test -n "$dst_arg"; then
|
||||
# $@ is not empty: it contains at least $arg.
|
||||
set fnord "$@" "$dst_arg"
|
||||
shift # fnord
|
||||
fi
|
||||
shift # arg
|
||||
dst_arg=$arg
|
||||
# Protect names problematic for `test' and other utilities.
|
||||
case $dst_arg in
|
||||
-* | [=\(\)!]) dst_arg=./$dst_arg;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
|
||||
if test $# -eq 0; then
|
||||
if test -z "$dir_arg"; then
|
||||
echo "$0: no input file specified." >&2
|
||||
exit 1
|
||||
fi
|
||||
# It's OK to call `install-sh -d' without argument.
|
||||
# This can happen when creating conditional directories.
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if test -z "$dir_arg"; then
|
||||
do_exit='(exit $ret); exit $ret'
|
||||
trap "ret=129; $do_exit" 1
|
||||
trap "ret=130; $do_exit" 2
|
||||
trap "ret=141; $do_exit" 13
|
||||
trap "ret=143; $do_exit" 15
|
||||
|
||||
# Set umask so as not to create temps with too-generous modes.
|
||||
# However, 'strip' requires both read and write access to temps.
|
||||
case $mode in
|
||||
# Optimize common cases.
|
||||
*644) cp_umask=133;;
|
||||
*755) cp_umask=22;;
|
||||
|
||||
*[0-7])
|
||||
if test -z "$stripcmd"; then
|
||||
u_plus_rw=
|
||||
else
|
||||
u_plus_rw='% 200'
|
||||
fi
|
||||
cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
|
||||
*)
|
||||
if test -z "$stripcmd"; then
|
||||
u_plus_rw=
|
||||
else
|
||||
u_plus_rw=,u+rw
|
||||
fi
|
||||
cp_umask=$mode$u_plus_rw;;
|
||||
esac
|
||||
fi
|
||||
|
||||
for src
|
||||
do
|
||||
# Protect names problematic for `test' and other utilities.
|
||||
case $src in
|
||||
-* | [=\(\)!]) src=./$src;;
|
||||
esac
|
||||
|
||||
if test -n "$dir_arg"; then
|
||||
dst=$src
|
||||
dstdir=$dst
|
||||
test -d "$dstdir"
|
||||
dstdir_status=$?
|
||||
else
|
||||
|
||||
# Waiting for this to be detected by the "$cpprog $src $dsttmp" command
|
||||
# might cause directories to be created, which would be especially bad
|
||||
# if $src (and thus $dsttmp) contains '*'.
|
||||
if test ! -f "$src" && test ! -d "$src"; then
|
||||
echo "$0: $src does not exist." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test -z "$dst_arg"; then
|
||||
echo "$0: no destination specified." >&2
|
||||
exit 1
|
||||
fi
|
||||
dst=$dst_arg
|
||||
|
||||
# If destination is a directory, append the input filename; won't work
|
||||
# if double slashes aren't ignored.
|
||||
if test -d "$dst"; then
|
||||
if test -n "$no_target_directory"; then
|
||||
echo "$0: $dst_arg: Is a directory" >&2
|
||||
exit 1
|
||||
fi
|
||||
dstdir=$dst
|
||||
dst=$dstdir/`basename "$src"`
|
||||
dstdir_status=0
|
||||
else
|
||||
# Prefer dirname, but fall back on a substitute if dirname fails.
|
||||
dstdir=`
|
||||
(dirname "$dst") 2>/dev/null ||
|
||||
expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
|
||||
X"$dst" : 'X\(//\)[^/]' \| \
|
||||
X"$dst" : 'X\(//\)$' \| \
|
||||
X"$dst" : 'X\(/\)' \| . 2>/dev/null ||
|
||||
echo X"$dst" |
|
||||
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
|
||||
s//\1/
|
||||
q
|
||||
}
|
||||
/^X\(\/\/\)[^/].*/{
|
||||
s//\1/
|
||||
q
|
||||
}
|
||||
/^X\(\/\/\)$/{
|
||||
s//\1/
|
||||
q
|
||||
}
|
||||
/^X\(\/\).*/{
|
||||
s//\1/
|
||||
q
|
||||
}
|
||||
s/.*/./; q'
|
||||
`
|
||||
|
||||
test -d "$dstdir"
|
||||
dstdir_status=$?
|
||||
fi
|
||||
fi
|
||||
|
||||
obsolete_mkdir_used=false
|
||||
|
||||
if test $dstdir_status != 0; then
|
||||
case $posix_mkdir in
|
||||
'')
|
||||
# Create intermediate dirs using mode 755 as modified by the umask.
|
||||
# This is like FreeBSD 'install' as of 1997-10-28.
|
||||
umask=`umask`
|
||||
case $stripcmd.$umask in
|
||||
# Optimize common cases.
|
||||
*[2367][2367]) mkdir_umask=$umask;;
|
||||
.*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
|
||||
|
||||
*[0-7])
|
||||
mkdir_umask=`expr $umask + 22 \
|
||||
- $umask % 100 % 40 + $umask % 20 \
|
||||
- $umask % 10 % 4 + $umask % 2
|
||||
`;;
|
||||
*) mkdir_umask=$umask,go-w;;
|
||||
esac
|
||||
|
||||
# With -d, create the new directory with the user-specified mode.
|
||||
# Otherwise, rely on $mkdir_umask.
|
||||
if test -n "$dir_arg"; then
|
||||
mkdir_mode=-m$mode
|
||||
else
|
||||
mkdir_mode=
|
||||
fi
|
||||
|
||||
posix_mkdir=false
|
||||
case $umask in
|
||||
*[123567][0-7][0-7])
|
||||
# POSIX mkdir -p sets u+wx bits regardless of umask, which
|
||||
# is incompatible with FreeBSD 'install' when (umask & 300) != 0.
|
||||
;;
|
||||
*)
|
||||
tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
|
||||
trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
|
||||
|
||||
if (umask $mkdir_umask &&
|
||||
exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
|
||||
then
|
||||
if test -z "$dir_arg" || {
|
||||
# Check for POSIX incompatibilities with -m.
|
||||
# HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
|
||||
# other-writeable bit of parent directory when it shouldn't.
|
||||
# FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
|
||||
ls_ld_tmpdir=`ls -ld "$tmpdir"`
|
||||
case $ls_ld_tmpdir in
|
||||
d????-?r-*) different_mode=700;;
|
||||
d????-?--*) different_mode=755;;
|
||||
*) false;;
|
||||
esac &&
|
||||
$mkdirprog -m$different_mode -p -- "$tmpdir" && {
|
||||
ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
|
||||
test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
|
||||
}
|
||||
}
|
||||
then posix_mkdir=:
|
||||
fi
|
||||
rmdir "$tmpdir/d" "$tmpdir"
|
||||
else
|
||||
# Remove any dirs left behind by ancient mkdir implementations.
|
||||
rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
|
||||
fi
|
||||
trap '' 0;;
|
||||
esac;;
|
||||
esac
|
||||
|
||||
if
|
||||
$posix_mkdir && (
|
||||
umask $mkdir_umask &&
|
||||
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
|
||||
)
|
||||
then :
|
||||
else
|
||||
|
||||
# The umask is ridiculous, or mkdir does not conform to POSIX,
|
||||
# or it failed possibly due to a race condition. Create the
|
||||
# directory the slow way, step by step, checking for races as we go.
|
||||
|
||||
case $dstdir in
|
||||
/*) prefix='/';;
|
||||
[-=\(\)!]*) prefix='./';;
|
||||
*) prefix='';;
|
||||
esac
|
||||
|
||||
eval "$initialize_posix_glob"
|
||||
|
||||
oIFS=$IFS
|
||||
IFS=/
|
||||
$posix_glob set -f
|
||||
set fnord $dstdir
|
||||
shift
|
||||
$posix_glob set +f
|
||||
IFS=$oIFS
|
||||
|
||||
prefixes=
|
||||
|
||||
for d
|
||||
do
|
||||
test X"$d" = X && continue
|
||||
|
||||
prefix=$prefix$d
|
||||
if test -d "$prefix"; then
|
||||
prefixes=
|
||||
else
|
||||
if $posix_mkdir; then
|
||||
(umask=$mkdir_umask &&
|
||||
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
|
||||
# Don't fail if two instances are running concurrently.
|
||||
test -d "$prefix" || exit 1
|
||||
else
|
||||
case $prefix in
|
||||
*\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
|
||||
*) qprefix=$prefix;;
|
||||
esac
|
||||
prefixes="$prefixes '$qprefix'"
|
||||
fi
|
||||
fi
|
||||
prefix=$prefix/
|
||||
done
|
||||
|
||||
if test -n "$prefixes"; then
|
||||
# Don't fail if two instances are running concurrently.
|
||||
(umask $mkdir_umask &&
|
||||
eval "\$doit_exec \$mkdirprog $prefixes") ||
|
||||
test -d "$dstdir" || exit 1
|
||||
obsolete_mkdir_used=true
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if test -n "$dir_arg"; then
|
||||
{ test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
|
||||
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
|
||||
{ test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
|
||||
test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
|
||||
else
|
||||
|
||||
# Make a couple of temp file names in the proper directory.
|
||||
dsttmp=$dstdir/_inst.$$_
|
||||
rmtmp=$dstdir/_rm.$$_
|
||||
|
||||
# Trap to clean up those temp files at exit.
|
||||
trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
|
||||
|
||||
# Copy the file name to the temp name.
|
||||
(umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
|
||||
|
||||
# and set any options; do chmod last to preserve setuid bits.
|
||||
#
|
||||
# If any of these fail, we abort the whole thing. If we want to
|
||||
# ignore errors from any of these, just make sure not to ignore
|
||||
# errors from the above "$doit $cpprog $src $dsttmp" command.
|
||||
#
|
||||
{ test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
|
||||
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
|
||||
{ test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
|
||||
{ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
|
||||
|
||||
# If -C, don't bother to copy if it wouldn't change the file.
|
||||
if $copy_on_change &&
|
||||
old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
|
||||
new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
|
||||
|
||||
eval "$initialize_posix_glob" &&
|
||||
$posix_glob set -f &&
|
||||
set X $old && old=:$2:$4:$5:$6 &&
|
||||
set X $new && new=:$2:$4:$5:$6 &&
|
||||
$posix_glob set +f &&
|
||||
|
||||
test "$old" = "$new" &&
|
||||
$cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
|
||||
then
|
||||
rm -f "$dsttmp"
|
||||
else
|
||||
# Rename the file to the real destination.
|
||||
$doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
|
||||
|
||||
# The rename failed, perhaps because mv can't rename something else
|
||||
# to itself, or perhaps because mv is so ancient that it does not
|
||||
# support -f.
|
||||
{
|
||||
# Now remove or move aside any old file at destination location.
|
||||
# We try this two ways since rm can't unlink itself on some
|
||||
# systems and the destination file might be busy for other
|
||||
# reasons. In this case, the final cleanup might fail but the new
|
||||
# file should still install successfully.
|
||||
{
|
||||
test ! -f "$dst" ||
|
||||
$doit $rmcmd -f "$dst" 2>/dev/null ||
|
||||
{ $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
|
||||
{ $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
|
||||
} ||
|
||||
{ echo "$0: cannot unlink or rename $dst" >&2
|
||||
(exit 1); exit 1
|
||||
}
|
||||
} &&
|
||||
|
||||
# Now rename the file to the real destination.
|
||||
$doit $mvcmd "$dsttmp" "$dst"
|
||||
}
|
||||
fi || exit 1
|
||||
|
||||
trap '' 0
|
||||
fi
|
||||
done
|
||||
|
||||
# Local variables:
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
||||
-331
@@ -1,331 +0,0 @@
|
||||
#! /bin/sh
|
||||
# Common stub for a few missing GNU programs while installing.
|
||||
|
||||
scriptversion=2012-01-06.13; # UTC
|
||||
|
||||
# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005, 2006,
|
||||
# 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
|
||||
# Originally by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
if test $# -eq 0; then
|
||||
echo 1>&2 "Try \`$0 --help' for more information"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
run=:
|
||||
sed_output='s/.* --output[ =]\([^ ]*\).*/\1/p'
|
||||
sed_minuso='s/.* -o \([^ ]*\).*/\1/p'
|
||||
|
||||
# In the cases where this matters, `missing' is being run in the
|
||||
# srcdir already.
|
||||
if test -f configure.ac; then
|
||||
configure_ac=configure.ac
|
||||
else
|
||||
configure_ac=configure.in
|
||||
fi
|
||||
|
||||
msg="missing on your system"
|
||||
|
||||
case $1 in
|
||||
--run)
|
||||
# Try to run requested program, and just exit if it succeeds.
|
||||
run=
|
||||
shift
|
||||
"$@" && exit 0
|
||||
# Exit code 63 means version mismatch. This often happens
|
||||
# when the user try to use an ancient version of a tool on
|
||||
# a file that requires a minimum version. In this case we
|
||||
# we should proceed has if the program had been absent, or
|
||||
# if --run hadn't been passed.
|
||||
if test $? = 63; then
|
||||
run=:
|
||||
msg="probably too old"
|
||||
fi
|
||||
;;
|
||||
|
||||
-h|--h|--he|--hel|--help)
|
||||
echo "\
|
||||
$0 [OPTION]... PROGRAM [ARGUMENT]...
|
||||
|
||||
Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an
|
||||
error status if there is no known handling for PROGRAM.
|
||||
|
||||
Options:
|
||||
-h, --help display this help and exit
|
||||
-v, --version output version information and exit
|
||||
--run try to run the given command, and emulate it if it fails
|
||||
|
||||
Supported PROGRAM values:
|
||||
aclocal touch file \`aclocal.m4'
|
||||
autoconf touch file \`configure'
|
||||
autoheader touch file \`config.h.in'
|
||||
autom4te touch the output file, or create a stub one
|
||||
automake touch all \`Makefile.in' files
|
||||
bison create \`y.tab.[ch]', if possible, from existing .[ch]
|
||||
flex create \`lex.yy.c', if possible, from existing .c
|
||||
help2man touch the output file
|
||||
lex create \`lex.yy.c', if possible, from existing .c
|
||||
makeinfo touch the output file
|
||||
yacc create \`y.tab.[ch]', if possible, from existing .[ch]
|
||||
|
||||
Version suffixes to PROGRAM as well as the prefixes \`gnu-', \`gnu', and
|
||||
\`g' are ignored when checking the name.
|
||||
|
||||
Send bug reports to <bug-automake@gnu.org>."
|
||||
exit $?
|
||||
;;
|
||||
|
||||
-v|--v|--ve|--ver|--vers|--versi|--versio|--version)
|
||||
echo "missing $scriptversion (GNU Automake)"
|
||||
exit $?
|
||||
;;
|
||||
|
||||
-*)
|
||||
echo 1>&2 "$0: Unknown \`$1' option"
|
||||
echo 1>&2 "Try \`$0 --help' for more information"
|
||||
exit 1
|
||||
;;
|
||||
|
||||
esac
|
||||
|
||||
# normalize program name to check for.
|
||||
program=`echo "$1" | sed '
|
||||
s/^gnu-//; t
|
||||
s/^gnu//; t
|
||||
s/^g//; t'`
|
||||
|
||||
# Now exit if we have it, but it failed. Also exit now if we
|
||||
# don't have it and --version was passed (most likely to detect
|
||||
# the program). This is about non-GNU programs, so use $1 not
|
||||
# $program.
|
||||
case $1 in
|
||||
lex*|yacc*)
|
||||
# Not GNU programs, they don't have --version.
|
||||
;;
|
||||
|
||||
*)
|
||||
if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
|
||||
# We have it, but it failed.
|
||||
exit 1
|
||||
elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
|
||||
# Could not run --version or --help. This is probably someone
|
||||
# running `$TOOL --version' or `$TOOL --help' to check whether
|
||||
# $TOOL exists and not knowing $TOOL uses missing.
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# If it does not exist, or fails to run (possibly an outdated version),
|
||||
# try to emulate it.
|
||||
case $program in
|
||||
aclocal*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified \`acinclude.m4' or \`${configure_ac}'. You might want
|
||||
to install the \`Automake' and \`Perl' packages. Grab them from
|
||||
any GNU archive site."
|
||||
touch aclocal.m4
|
||||
;;
|
||||
|
||||
autoconf*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified \`${configure_ac}'. You might want to install the
|
||||
\`Autoconf' and \`GNU m4' packages. Grab them from any GNU
|
||||
archive site."
|
||||
touch configure
|
||||
;;
|
||||
|
||||
autoheader*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified \`acconfig.h' or \`${configure_ac}'. You might want
|
||||
to install the \`Autoconf' and \`GNU m4' packages. Grab them
|
||||
from any GNU archive site."
|
||||
files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}`
|
||||
test -z "$files" && files="config.h"
|
||||
touch_files=
|
||||
for f in $files; do
|
||||
case $f in
|
||||
*:*) touch_files="$touch_files "`echo "$f" |
|
||||
sed -e 's/^[^:]*://' -e 's/:.*//'`;;
|
||||
*) touch_files="$touch_files $f.in";;
|
||||
esac
|
||||
done
|
||||
touch $touch_files
|
||||
;;
|
||||
|
||||
automake*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'.
|
||||
You might want to install the \`Automake' and \`Perl' packages.
|
||||
Grab them from any GNU archive site."
|
||||
find . -type f -name Makefile.am -print |
|
||||
sed 's/\.am$/.in/' |
|
||||
while read f; do touch "$f"; done
|
||||
;;
|
||||
|
||||
autom4te*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is needed, but is $msg.
|
||||
You might have modified some files without having the
|
||||
proper tools for further handling them.
|
||||
You can get \`$1' as part of \`Autoconf' from any GNU
|
||||
archive site."
|
||||
|
||||
file=`echo "$*" | sed -n "$sed_output"`
|
||||
test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
|
||||
if test -f "$file"; then
|
||||
touch $file
|
||||
else
|
||||
test -z "$file" || exec >$file
|
||||
echo "#! /bin/sh"
|
||||
echo "# Created by GNU Automake missing as a replacement of"
|
||||
echo "# $ $@"
|
||||
echo "exit 0"
|
||||
chmod +x $file
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
bison*|yacc*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' $msg. You should only need it if
|
||||
you modified a \`.y' file. You may need the \`Bison' package
|
||||
in order for those modifications to take effect. You can get
|
||||
\`Bison' from any GNU archive site."
|
||||
rm -f y.tab.c y.tab.h
|
||||
if test $# -ne 1; then
|
||||
eval LASTARG=\${$#}
|
||||
case $LASTARG in
|
||||
*.y)
|
||||
SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'`
|
||||
if test -f "$SRCFILE"; then
|
||||
cp "$SRCFILE" y.tab.c
|
||||
fi
|
||||
SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'`
|
||||
if test -f "$SRCFILE"; then
|
||||
cp "$SRCFILE" y.tab.h
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
if test ! -f y.tab.h; then
|
||||
echo >y.tab.h
|
||||
fi
|
||||
if test ! -f y.tab.c; then
|
||||
echo 'main() { return 0; }' >y.tab.c
|
||||
fi
|
||||
;;
|
||||
|
||||
lex*|flex*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified a \`.l' file. You may need the \`Flex' package
|
||||
in order for those modifications to take effect. You can get
|
||||
\`Flex' from any GNU archive site."
|
||||
rm -f lex.yy.c
|
||||
if test $# -ne 1; then
|
||||
eval LASTARG=\${$#}
|
||||
case $LASTARG in
|
||||
*.l)
|
||||
SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'`
|
||||
if test -f "$SRCFILE"; then
|
||||
cp "$SRCFILE" lex.yy.c
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
if test ! -f lex.yy.c; then
|
||||
echo 'main() { return 0; }' >lex.yy.c
|
||||
fi
|
||||
;;
|
||||
|
||||
help2man*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified a dependency of a manual page. You may need the
|
||||
\`Help2man' package in order for those modifications to take
|
||||
effect. You can get \`Help2man' from any GNU archive site."
|
||||
|
||||
file=`echo "$*" | sed -n "$sed_output"`
|
||||
test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
|
||||
if test -f "$file"; then
|
||||
touch $file
|
||||
else
|
||||
test -z "$file" || exec >$file
|
||||
echo ".ab help2man is required to generate this page"
|
||||
exit $?
|
||||
fi
|
||||
;;
|
||||
|
||||
makeinfo*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified a \`.texi' or \`.texinfo' file, or any other file
|
||||
indirectly affecting the aspect of the manual. The spurious
|
||||
call might also be the consequence of using a buggy \`make' (AIX,
|
||||
DU, IRIX). You might want to install the \`Texinfo' package or
|
||||
the \`GNU make' package. Grab either from any GNU archive site."
|
||||
# The file to touch is that specified with -o ...
|
||||
file=`echo "$*" | sed -n "$sed_output"`
|
||||
test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
|
||||
if test -z "$file"; then
|
||||
# ... or it is the one specified with @setfilename ...
|
||||
infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'`
|
||||
file=`sed -n '
|
||||
/^@setfilename/{
|
||||
s/.* \([^ ]*\) *$/\1/
|
||||
p
|
||||
q
|
||||
}' $infile`
|
||||
# ... or it is derived from the source name (dir/f.texi becomes f.info)
|
||||
test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info
|
||||
fi
|
||||
# If the file does not exist, the user really needs makeinfo;
|
||||
# let's fail without touching anything.
|
||||
test -f $file || exit 1
|
||||
touch $file
|
||||
;;
|
||||
|
||||
*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is needed, and is $msg.
|
||||
You might have modified some files without having the
|
||||
proper tools for further handling them. Check the \`README' file,
|
||||
it often tells you about the needed prerequisites for installing
|
||||
this package. You may also peek at any GNU archive site, in case
|
||||
some other package would contain this missing \`$1' program."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
|
||||
# Local variables:
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
||||
-226
@@ -1,226 +0,0 @@
|
||||
#! /bin/sh
|
||||
# ylwrap - wrapper for lex/yacc invocations.
|
||||
|
||||
scriptversion=2011-08-25.18; # UTC
|
||||
|
||||
# Copyright (C) 1996, 1997, 1998, 1999, 2001, 2002, 2003, 2004, 2005,
|
||||
# 2007, 2009, 2010, 2011 Free Software Foundation, Inc.
|
||||
#
|
||||
# Written by Tom Tromey <tromey@cygnus.com>.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
# This file is maintained in Automake, please report
|
||||
# bugs to <bug-automake@gnu.org> or send patches to
|
||||
# <automake-patches@gnu.org>.
|
||||
|
||||
case "$1" in
|
||||
'')
|
||||
echo "$0: No files given. Try \`$0 --help' for more information." 1>&2
|
||||
exit 1
|
||||
;;
|
||||
--basedir)
|
||||
basedir=$2
|
||||
shift 2
|
||||
;;
|
||||
-h|--h*)
|
||||
cat <<\EOF
|
||||
Usage: ylwrap [--help|--version] INPUT [OUTPUT DESIRED]... -- PROGRAM [ARGS]...
|
||||
|
||||
Wrapper for lex/yacc invocations, renaming files as desired.
|
||||
|
||||
INPUT is the input file
|
||||
OUTPUT is one file PROG generates
|
||||
DESIRED is the file we actually want instead of OUTPUT
|
||||
PROGRAM is program to run
|
||||
ARGS are passed to PROG
|
||||
|
||||
Any number of OUTPUT,DESIRED pairs may be used.
|
||||
|
||||
Report bugs to <bug-automake@gnu.org>.
|
||||
EOF
|
||||
exit $?
|
||||
;;
|
||||
-v|--v*)
|
||||
echo "ylwrap $scriptversion"
|
||||
exit $?
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
# The input.
|
||||
input="$1"
|
||||
shift
|
||||
case "$input" in
|
||||
[\\/]* | ?:[\\/]*)
|
||||
# Absolute path; do nothing.
|
||||
;;
|
||||
*)
|
||||
# Relative path. Make it absolute.
|
||||
input="`pwd`/$input"
|
||||
;;
|
||||
esac
|
||||
|
||||
pairlist=
|
||||
while test "$#" -ne 0; do
|
||||
if test "$1" = "--"; then
|
||||
shift
|
||||
break
|
||||
fi
|
||||
pairlist="$pairlist $1"
|
||||
shift
|
||||
done
|
||||
|
||||
# The program to run.
|
||||
prog="$1"
|
||||
shift
|
||||
# Make any relative path in $prog absolute.
|
||||
case "$prog" in
|
||||
[\\/]* | ?:[\\/]*) ;;
|
||||
*[\\/]*) prog="`pwd`/$prog" ;;
|
||||
esac
|
||||
|
||||
# FIXME: add hostname here for parallel makes that run commands on
|
||||
# other machines. But that might take us over the 14-char limit.
|
||||
dirname=ylwrap$$
|
||||
do_exit="cd '`pwd`' && rm -rf $dirname > /dev/null 2>&1;"' (exit $ret); exit $ret'
|
||||
trap "ret=129; $do_exit" 1
|
||||
trap "ret=130; $do_exit" 2
|
||||
trap "ret=141; $do_exit" 13
|
||||
trap "ret=143; $do_exit" 15
|
||||
mkdir $dirname || exit 1
|
||||
|
||||
cd $dirname
|
||||
|
||||
case $# in
|
||||
0) "$prog" "$input" ;;
|
||||
*) "$prog" "$@" "$input" ;;
|
||||
esac
|
||||
ret=$?
|
||||
|
||||
if test $ret -eq 0; then
|
||||
set X $pairlist
|
||||
shift
|
||||
first=yes
|
||||
# Since DOS filename conventions don't allow two dots,
|
||||
# the DOS version of Bison writes out y_tab.c instead of y.tab.c
|
||||
# and y_tab.h instead of y.tab.h. Test to see if this is the case.
|
||||
y_tab_nodot="no"
|
||||
if test -f y_tab.c || test -f y_tab.h; then
|
||||
y_tab_nodot="yes"
|
||||
fi
|
||||
|
||||
# The directory holding the input.
|
||||
input_dir=`echo "$input" | sed -e 's,\([\\/]\)[^\\/]*$,\1,'`
|
||||
# Quote $INPUT_DIR so we can use it in a regexp.
|
||||
# FIXME: really we should care about more than `.' and `\'.
|
||||
input_rx=`echo "$input_dir" | sed 's,\\\\,\\\\\\\\,g;s,\\.,\\\\.,g'`
|
||||
|
||||
while test "$#" -ne 0; do
|
||||
from="$1"
|
||||
# Handle y_tab.c and y_tab.h output by DOS
|
||||
if test $y_tab_nodot = "yes"; then
|
||||
if test $from = "y.tab.c"; then
|
||||
from="y_tab.c"
|
||||
else
|
||||
if test $from = "y.tab.h"; then
|
||||
from="y_tab.h"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
if test -f "$from"; then
|
||||
# If $2 is an absolute path name, then just use that,
|
||||
# otherwise prepend `../'.
|
||||
case "$2" in
|
||||
[\\/]* | ?:[\\/]*) target="$2";;
|
||||
*) target="../$2";;
|
||||
esac
|
||||
|
||||
# We do not want to overwrite a header file if it hasn't
|
||||
# changed. This avoid useless recompilations. However the
|
||||
# parser itself (the first file) should always be updated,
|
||||
# because it is the destination of the .y.c rule in the
|
||||
# Makefile. Divert the output of all other files to a temporary
|
||||
# file so we can compare them to existing versions.
|
||||
if test $first = no; then
|
||||
realtarget="$target"
|
||||
target="tmp-`echo $target | sed s/.*[\\/]//g`"
|
||||
fi
|
||||
# Edit out `#line' or `#' directives.
|
||||
#
|
||||
# We don't want the resulting debug information to point at
|
||||
# an absolute srcdir; it is better for it to just mention the
|
||||
# .y file with no path.
|
||||
#
|
||||
# We want to use the real output file name, not yy.lex.c for
|
||||
# instance.
|
||||
#
|
||||
# We want the include guards to be adjusted too.
|
||||
FROM=`echo "$from" | sed \
|
||||
-e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'\
|
||||
-e 's/[^ABCDEFGHIJKLMNOPQRSTUVWXYZ]/_/g'`
|
||||
TARGET=`echo "$2" | sed \
|
||||
-e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'\
|
||||
-e 's/[^ABCDEFGHIJKLMNOPQRSTUVWXYZ]/_/g'`
|
||||
|
||||
sed -e "/^#/!b" -e "s,$input_rx,," -e "s,$from,$2," \
|
||||
-e "s,$FROM,$TARGET," "$from" >"$target" || ret=$?
|
||||
|
||||
# Check whether header files must be updated.
|
||||
if test $first = no; then
|
||||
if test -f "$realtarget" && cmp -s "$realtarget" "$target"; then
|
||||
echo "$2" is unchanged
|
||||
rm -f "$target"
|
||||
else
|
||||
echo updating "$2"
|
||||
mv -f "$target" "$realtarget"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
# A missing file is only an error for the first file. This
|
||||
# is a blatant hack to let us support using "yacc -d". If -d
|
||||
# is not specified, we don't want an error when the header
|
||||
# file is "missing".
|
||||
if test $first = yes; then
|
||||
ret=1
|
||||
fi
|
||||
fi
|
||||
shift
|
||||
shift
|
||||
first=no
|
||||
done
|
||||
else
|
||||
ret=$?
|
||||
fi
|
||||
|
||||
# Remove the directory.
|
||||
cd ..
|
||||
rm -rf $dirname
|
||||
|
||||
exit $ret
|
||||
|
||||
# Local Variables:
|
||||
# mode: shell-script
|
||||
# sh-indentation: 2
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
||||
@@ -1,73 +0,0 @@
|
||||
AC_INIT([jq], [1.3], [mu@netsoc.tcd.ie],
|
||||
[jq], [http://stedolan.github.com/jq/])
|
||||
|
||||
dnl Created autoconf implementation thompson@dtosolutions, 26NOV12
|
||||
AC_PREREQ([2.61])
|
||||
AC_CONFIG_AUX_DIR([config])
|
||||
AM_INIT_AUTOMAKE([parallel-tests foreign])
|
||||
AM_SILENT_RULES([yes])
|
||||
AC_PROG_CC
|
||||
AC_PROG_CC_STDC
|
||||
AC_PROG_CPP_WERROR
|
||||
AC_PROG_YACC
|
||||
|
||||
AM_PROG_CC_C_O
|
||||
|
||||
dnl couldn't use AM_PROG_LEX as it doesn't support header files like the
|
||||
dnl AC_PROG_YACC macros...
|
||||
|
||||
dnl
|
||||
dnl these program checks should probably be deleted
|
||||
dnl
|
||||
|
||||
AC_PROG_LEX
|
||||
if test "x$LEX" != xflex; then
|
||||
LEX="$SHELL $missing_dir/missing flex"
|
||||
fi
|
||||
|
||||
|
||||
dnl Check for valgrind
|
||||
AC_CHECK_PROGS(valgrind_cmd, valgrind)
|
||||
if test "x$valgrind_cmd" = "x" ; then
|
||||
AC_MSG_WARN([valgrind is required to test jq.])
|
||||
fi
|
||||
|
||||
|
||||
dnl Don't attempt to build docs if there's no Ruby lying around
|
||||
AC_ARG_ENABLE([docs],
|
||||
AC_HELP_STRING([--disable-docs], [don't build docs]))
|
||||
|
||||
AS_IF([test "x$enable_docs" != "xno"],[
|
||||
AC_CHECK_PROGS(bundle_cmd, bundle)
|
||||
|
||||
AC_CACHE_CHECK([for Ruby dependencies], [jq_cv_ruby_deps],
|
||||
[jq_cv_ruby_deps=yes;
|
||||
AS_IF([test "x$bundle_cmd" = "x" || \
|
||||
! bmsg="`cd ${srcdir}/docs; "$bundle_cmd" check 2>/dev/null`"],[
|
||||
AC_MSG_WARN([$bmsg])
|
||||
cat <<EOF
|
||||
*****************************************************************
|
||||
* Ruby dependencies for building jq documentation not found. *
|
||||
* You can still build, install and hack on jq, but the manpage *
|
||||
* will not be rebuilt and some of the tests won't run. *
|
||||
* See docs/README.md for how to install the docs dependencies. *
|
||||
*****************************************************************
|
||||
EOF
|
||||
jq_cv_ruby_deps=no
|
||||
])])
|
||||
|
||||
if test "x$jq_cv_ruby_deps" != "xyes"; then
|
||||
enable_docs=no
|
||||
fi
|
||||
])
|
||||
|
||||
AM_CONDITIONAL([ENABLE_DOCS], [test "x$enable_docs" != xno])
|
||||
AC_SUBST([BUNDLER], ["$bundle_cmd"])
|
||||
|
||||
dnl AC_CONFIG_MACRO_DIR([m4])
|
||||
AC_CONFIG_HEADERS(config.h)
|
||||
AM_INIT_AUTOMAKE([-Wall])
|
||||
AC_CONFIG_FILES([Makefile])
|
||||
AC_OUTPUT
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
.DS_Store
|
||||
.sass-cache
|
||||
output/*
|
||||
|
||||
# Autogenerated from public/css/base.scss
|
||||
public/css/base.css
|
||||
@@ -1,6 +0,0 @@
|
||||
source "https://rubygems.org"
|
||||
|
||||
gem "rake"
|
||||
gem "bonsai"
|
||||
gem "maruku"
|
||||
gem "ronn"
|
||||
@@ -1,63 +0,0 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
POpen4 (0.1.4)
|
||||
Platform (>= 0.4.0)
|
||||
open4
|
||||
Platform (0.4.0)
|
||||
activesupport (3.2.12)
|
||||
i18n (~> 0.6)
|
||||
multi_json (~> 1.0)
|
||||
addressable (2.3.4)
|
||||
bonsai (1.4.8)
|
||||
activesupport (>= 3.0.3)
|
||||
builder (>= 3.0.0)
|
||||
i18n (>= 0.5.0)
|
||||
launchy (>= 0.3.7)
|
||||
liquid (>= 2.2.2)
|
||||
maruku (>= 0.6.0)
|
||||
rack
|
||||
sass
|
||||
sinatra (>= 1.0)
|
||||
tilt (>= 1.3)
|
||||
watch (>= 0.1.0)
|
||||
yui-compressor
|
||||
builder (3.2.0)
|
||||
hpricot (0.8.6)
|
||||
i18n (0.6.4)
|
||||
launchy (2.3.0)
|
||||
addressable (~> 2.3)
|
||||
liquid (2.5.0)
|
||||
maruku (0.6.1)
|
||||
syntax (>= 1.0.0)
|
||||
multi_json (1.7.3)
|
||||
mustache (0.99.4)
|
||||
open4 (1.3.0)
|
||||
rack (1.5.2)
|
||||
rack-protection (1.5.0)
|
||||
rack
|
||||
rake (10.0.3)
|
||||
rdiscount (2.0.7.2)
|
||||
ronn (0.7.3)
|
||||
hpricot (>= 0.8.2)
|
||||
mustache (>= 0.7.0)
|
||||
rdiscount (>= 1.5.8)
|
||||
sass (3.2.8)
|
||||
sinatra (1.4.2)
|
||||
rack (~> 1.5, >= 1.5.2)
|
||||
rack-protection (~> 1.4)
|
||||
tilt (~> 1.3, >= 1.3.4)
|
||||
syntax (1.0.0)
|
||||
tilt (1.4.0)
|
||||
watch (0.1.0)
|
||||
yui-compressor (0.9.6)
|
||||
POpen4 (>= 0.1.4)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
bonsai
|
||||
maruku
|
||||
rake
|
||||
ronn
|
||||
@@ -1,25 +0,0 @@
|
||||
Documentation
|
||||
=============
|
||||
|
||||
The jq website, manpages and some of the tests are generated from this
|
||||
directory. The directory holds a [Bonsai](http://tinytree.info)
|
||||
website, and the manual is a YAML file in `content/3.manual`.
|
||||
|
||||
To build the documentation (including building the jq manpage), you'll
|
||||
need a working Ruby setup. The easiest way to get one is to install
|
||||
RVM and Ruby 1.9.3 like so:
|
||||
|
||||
\curl -L https://get.rvm.io | bash -s stable --ruby=1.9.3
|
||||
|
||||
After that finishes installing, you'll need to make sure RVM is on
|
||||
your path by doing `source $HOME/.rvm/scripts/rvm`, or just opening a
|
||||
new shell. See <http://rvm.io> for more info on RVM.
|
||||
|
||||
Once RVM is installed, you can install all the dependencies for jq's
|
||||
documentation build by running this from the `docs` directory:
|
||||
|
||||
bundle install
|
||||
|
||||
When bundle manages to install the dependencies, rerun `./configure`
|
||||
in the jq root directory and then the Makefile will be able to
|
||||
generate the jq manpage.
|
||||
-146
@@ -1,146 +0,0 @@
|
||||
require 'yaml'
|
||||
require 'bonsai'
|
||||
require 'liquid'
|
||||
require 'maruku'
|
||||
require 'json'
|
||||
require 'ronn'
|
||||
require 'tempfile'
|
||||
require 'yaml'
|
||||
|
||||
module ExtraFilters
|
||||
def markdownify(input)
|
||||
Maruku.new(input).to_html
|
||||
end
|
||||
|
||||
def sanitize(input)
|
||||
input.gsub(/[^a-zA-Z0-9_]/,"")
|
||||
end
|
||||
|
||||
def json(input)
|
||||
input.to_json
|
||||
end
|
||||
|
||||
def unique(input)
|
||||
@n = (@n || 0) + 1
|
||||
input + @n.to_s
|
||||
end
|
||||
end
|
||||
|
||||
Liquid::Template.register_filter(ExtraFilters)
|
||||
|
||||
|
||||
task :serve do
|
||||
begin
|
||||
Bonsai.log "Press Control+C to quit"
|
||||
|
||||
require 'rack'
|
||||
require 'sinatra'
|
||||
require 'watch'
|
||||
require 'launchy'
|
||||
|
||||
Bonsai.root_dir = Dir.pwd
|
||||
|
||||
server = fork {
|
||||
app = Rack::Builder.app {
|
||||
map "/jq" do
|
||||
use Bonsai::StaticPassThrough, :root => Bonsai.root_dir + "/output", :urls => ["/"]
|
||||
end
|
||||
run Bonsai::DevelopmentServer
|
||||
}
|
||||
Rack::Handler.default.run(app, :Port => 5000) do
|
||||
Launchy.open("http://localhost:5000/jq/")
|
||||
end
|
||||
}
|
||||
Watch.new("{content,templates,public}/**/*") { Bonsai::Exporter.process! }
|
||||
rescue Interrupt
|
||||
Process.kill("QUIT", server)
|
||||
Process.wait(server)
|
||||
exit
|
||||
end
|
||||
end
|
||||
|
||||
task :build do
|
||||
Bonsai.root_dir = Dir.pwd
|
||||
Bonsai::Exporter.publish!
|
||||
end
|
||||
|
||||
$BINARIES = {
|
||||
:osx32 => "--host=i686-apple-darwin10 CFLAGS='-m32 -g -O'",
|
||||
:osx64 => "--host=i686-apple-darwin10 CFLAGS='-m64 -g -O'",
|
||||
:win32 => "--host=i686-w64-mingw32 CFLAGS='-g -O'",
|
||||
:win64 => "--host=x86_64-w64-mingw32 CFLAGS='-g -O'",
|
||||
:linux32 => "--host=x86_64-linux-gnu CFLAGS='-m32 -g -O'",
|
||||
:linux64 => "--host=x86_64-linux-gnu CFLAGS='-m64 -g -O'"
|
||||
}
|
||||
|
||||
$BINARIES.each do |name, args|
|
||||
file "../build/#{name}" do |t|
|
||||
sh "MAKEFLAGS=-j4 ../scripts/crosscompile #{name} #{args}"
|
||||
end
|
||||
task :binaries => ["../build/#{name}"]
|
||||
end
|
||||
|
||||
task :binaries => ["output/download"] do
|
||||
$BINARIES.each do |name, args|
|
||||
FileUtils.cp_r "../build/#{name}", "output/download/"
|
||||
end
|
||||
end
|
||||
|
||||
def load_manual
|
||||
YAML::ENGINE.yamler = 'syck'
|
||||
YAML::load(File.open("content/3.manual/manual.yml"))
|
||||
end
|
||||
|
||||
task :manpage do
|
||||
Tempfile.open "manpage" do |f|
|
||||
manual = load_manual
|
||||
f.puts manual['manpage_intro']
|
||||
f.puts manual['body']
|
||||
manual['sections'].each do |section|
|
||||
|
||||
f.puts "## #{section['title'].upcase}\n"
|
||||
f.puts section['body']
|
||||
f.puts ""
|
||||
(section['entries'] || []).each do |entry|
|
||||
f.puts "### #{entry['title']}\n"
|
||||
f.puts entry['body']
|
||||
f.puts ""
|
||||
(entry['examples'] || []).each do |example|
|
||||
f.puts " jq '#{example['program']}'"
|
||||
f.puts " #{example['input']}"
|
||||
f.puts " => #{example['output'].join(", ")}"
|
||||
f.puts
|
||||
end
|
||||
end
|
||||
f.puts ""
|
||||
end
|
||||
f.puts manual['manpage_epilogue']
|
||||
f.close
|
||||
puts Ronn::Document.new(f.path).convert('roff').gsub(/<\/?code>/,"")
|
||||
end
|
||||
end
|
||||
|
||||
task :manpage_default => ["default_manpage.md"] do
|
||||
puts Ronn::Document.new("default_manpage.md").convert('roff').gsub(/<\/?code>/,"")
|
||||
end
|
||||
|
||||
task :mantests do
|
||||
load_manual['sections'].each do |section|
|
||||
(section['entries'] || []).each do |entry|
|
||||
(entry['examples'] || []).each do |example|
|
||||
puts example['program'].gsub("\n", " ")
|
||||
puts example['input']
|
||||
example['output'].each do |s| puts s end
|
||||
puts
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
directory "output/download/source"
|
||||
task :tarball => ["output/download/source"] do
|
||||
sh "cd ..; ./configure && make dist && make distclean"
|
||||
sh "cp ../jq-*.tar.gz output/download/source"
|
||||
end
|
||||
|
||||
task :dist => [:build, :binaries, :tarball]
|
||||
@@ -1,241 +0,0 @@
|
||||
headline: Tutorial
|
||||
body:
|
||||
- text: |
|
||||
|
||||
Twitter has a JSON API, so let's play with that. This URL gets
|
||||
us the last 5 tweets about JSON:
|
||||
|
||||
- command: "curl 'http://search.twitter.com/search.json?q=json&rpp=5&include_entities=true'"
|
||||
result: |
|
||||
{"completed_in":0.108,"max_id":247677287108067328,"max_id_str":"247677287108067328","next_page":"?page=2&max_id=247677287108067328&q=json&rpp=5&include_entities=1","page":1,"query":"json","refresh_url":"?since_id=247677287108067328&q=json&include_entities=1","results":[{"created_at":"Mon, 17 Sep 2012 12:44:01 +0000","entities":{"hashtags":[],"urls":[{"url":"http:\/\/t.co\/XRvh1ZVw","expanded_url":"http:\/\/jase.im\/Ri7I0M","display_url":"jase.im\/Ri7I0M","indices":[112,132]}],"user_mentions":[{"screen_name":"imagemechanics","name":"Jason Cotterell","id":57271393,"id_str":"57271393","indices":[3,18]}]},"from_user":"_AaronNg","from_user_id":79771704,"from_user_id_str":"79771704","from_user_name":"NgChenChong","geo":null,"id":247677287108067328,"id_str":"247677287108067328","iso_language_code":"en","metadata":{"result_type":"recent"},"profile_image_url":"http:\/\/a0.twimg.com\/profile_images\/2523558403\/ek8mo4j4beq84iw28gjl_normal.jpeg","profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/2523558403\/ek8mo4j4beq84iw28gjl_normal.jpeg","source":"<a href="http:\/\/twitter.com\/">web<\/a>","text":"RT @imagemechanics: iPhone 5 website teardown: How Apple compresses video using JPEG, JSON, and <canvas>.\nhttp:\/\/t.co\/XRvh1ZVw","to_user":null,"to_user_id":0,"to_user_id_str":"0","to_user_name":null}, ...
|
||||
|
||||
- text: |
|
||||
|
||||
There's lots of info and no whitespace, so to make it a bit more
|
||||
legible we pipe it through jq, telling jq to just spit the input
|
||||
back at us using the expression `.`:
|
||||
|
||||
|
||||
- command: "curl 'http://search.twitter.com/search.json?q=json&rpp=5&include_entities=true' | jq '.'"
|
||||
result: |
|
||||
|
||||
{
|
||||
"since_id_str": "0",
|
||||
"since_id": 0,
|
||||
"results_per_page": 5,
|
||||
"completed_in": 0.108,
|
||||
"max_id": 247677287108067330,
|
||||
"max_id_str": "247677287108067328",
|
||||
"next_page": "?page=2&max_id=247677287108067328&q=json&rpp=5&include_entities=1",
|
||||
"page": 1,
|
||||
"query": "json",
|
||||
"refresh_url": "?since_id=247677287108067328&q=json&include_entities=1",
|
||||
"results": [
|
||||
{
|
||||
"from_user_name": "NgChenChong",
|
||||
"from_user_id_str": "79771704",
|
||||
"from_user_id": 79771704,
|
||||
"from_user": "_AaronNg",
|
||||
"iso_language_code": "en",
|
||||
"text": "RT @imagemechanics: iPhone 5 website teardown: How Apple compresses video using JPEG, JSON, and <canvas>.\nhttp://t.co/XRvh1ZVw",
|
||||
"to_user": null
|
||||
/* lots more fields... */
|
||||
},
|
||||
/* lots more results... */
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
- text: |
|
||||
|
||||
Let's pull out the first tweet:
|
||||
|
||||
|
||||
- command: "curl 'http://search.twitter.com/search.json?q=json&rpp=5&include_entities=true' | jq '.results[0]'"
|
||||
result: |
|
||||
{
|
||||
"from_user_name": "NgChenChong",
|
||||
"from_user_id_str": "79771704",
|
||||
"from_user_id": 79771704,
|
||||
"from_user": "_AaronNg",
|
||||
"iso_language_code": "en",
|
||||
"text": "RT @imagemechanics: iPhone 5 website teardown: How Apple compresses video using JPEG, JSON, and <canvas>.\nhttp://t.co/XRvh1ZVw",
|
||||
"to_user": null
|
||||
/* lots more fields... */
|
||||
},
|
||||
|
||||
- text: |
|
||||
|
||||
For the rest of the examples, I'll leave out the `curl` command - it's not going to change.
|
||||
|
||||
|
||||
There's still a lot of info we don't care about there, so we'll
|
||||
restrict it down to the most interesting fields.
|
||||
|
||||
|
||||
- command: "jq '.results[0] | {from_user, text}'"
|
||||
result: |
|
||||
{
|
||||
"text": "RT @imagemechanics: iPhone 5 website teardown: How Apple compresses video using JPEG, JSON, and <canvas>.\nhttp://t.co/XRvh1ZVw",
|
||||
"from_user": "_AaronNg"
|
||||
}
|
||||
|
||||
- text: |
|
||||
|
||||
The `|` operator in jq feeds the output of one filter
|
||||
(`.results[0]` which gets the first element of the results
|
||||
array) into the input of another (`{from_user, text}` which
|
||||
builds an object of those two fields).
|
||||
|
||||
Now let's get the rest of the tweets:
|
||||
|
||||
|
||||
|
||||
- command: "jq '.results[] | {from_user, text}'"
|
||||
result: |
|
||||
{
|
||||
"text": "RT @imagemechanics: iPhone 5 website teardown: How Apple compresses video using JPEG, JSON, and <canvas>.\nhttp://t.co/XRvh1ZVw",
|
||||
"from_user": "_AaronNg"
|
||||
}
|
||||
{
|
||||
"text": "RT @_kud: iPhone 5 website teardown: How Apple compresses video using JPEG, JSON, and <canvas> -- http://t.co/Lhp92IqD",
|
||||
"from_user": "blouerat"
|
||||
}
|
||||
{
|
||||
"text": "Dynamic Forms Mobile App by pmadiset: Few Forms details are hosted on our server in the form of JSON. This JSON ... http://t.co/7KALdWaX",
|
||||
"from_user": "cebu_iphone"
|
||||
}
|
||||
{
|
||||
"text": "iPhone 5 website insanity - Video compressing using JPEG, JSON, and <canvas> http://t.co/28Jesbio (Oh #Apple, U So #Awesome...)",
|
||||
"from_user": "dieswaytoofast"
|
||||
}
|
||||
{
|
||||
"text": "RT @umutm: A very nice web-based JSON editor - http://t.co/M70snaIf",
|
||||
"from_user": "Leolik"
|
||||
}
|
||||
|
||||
|
||||
- text: |
|
||||
|
||||
`.results[]` returns each element of the results array, one at a
|
||||
time, which are all fed into `{from_user, text}`.
|
||||
|
||||
|
||||
Data in jq is represented as streams of JSON values - every jq
|
||||
expression runs for each value in its input stream, and can
|
||||
produce any number of values to its output stream.
|
||||
|
||||
Streams are serialised by just separating JSON values with
|
||||
whitespace. This is a `cat`-friendly format - you can just join
|
||||
two JSON streams together and get a valid JSON stream.
|
||||
|
||||
If you want to get the output as a single array, you can tell jq to
|
||||
"collect" all of the answers by wrapping the filter in square
|
||||
brackets:
|
||||
|
||||
- command: "jq '[.results[] | {from_user, text}]'"
|
||||
result: |
|
||||
[
|
||||
{
|
||||
"text": "RT @imagemechanics: iPhone 5 website teardown: How Apple compresses video using JPEG, JSON, and <canvas>.\nhttp://t.co/XRvh1ZVw",
|
||||
"from_user": "_AaronNg"
|
||||
},
|
||||
{
|
||||
"text": "RT @_kud: iPhone 5 website teardown: How Apple compresses video using JPEG, JSON, and <canvas> -- http://t.co/Lhp92IqD",
|
||||
"from_user": "blouerat"
|
||||
},
|
||||
{
|
||||
"text": "Dynamic Forms Mobile App by pmadiset: Few Forms details are hosted on our server in the form of JSON. This JSON ... http://t.co/7KALdWaX",
|
||||
"from_user": "cebu_iphone"
|
||||
},
|
||||
{
|
||||
"text": "iPhone 5 website insanity - Video compressing using JPEG, JSON, and <canvas> http://t.co/28Jesbio (Oh #Apple, U So #Awesome...)",
|
||||
"from_user": "dieswaytoofast"
|
||||
},
|
||||
{
|
||||
"text": "RT @umutm: A very nice web-based JSON editor - http://t.co/M70snaIf",
|
||||
"from_user": "Leolik"
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
- text: |
|
||||
|
||||
- - -
|
||||
|
||||
Next, let's try getting the URLs out of those API results as
|
||||
well. In each tweet, the Twitter API includes a field called
|
||||
"entities" which looks like this:
|
||||
|
||||
{
|
||||
"user_mentions": [],
|
||||
"urls": [{
|
||||
"indices": [83, 103],
|
||||
"display_url": "bit.ly/StniqT",
|
||||
"expanded_url": "http://bit.ly/StniqT",
|
||||
"url": "http://t.co/28Jesbio"
|
||||
}],
|
||||
"hashtags": [
|
||||
{"indices": [108, 114], "text": "Apple" },
|
||||
{"indices": [121, 129], "text": "Awesome"}
|
||||
]
|
||||
}
|
||||
|
||||
We want to pull out all of the "url" fields inside that array of url
|
||||
objects, and make a simple list of strings to go along with the
|
||||
"from_user" and "text" fields we already have.
|
||||
|
||||
|
||||
|
||||
- command: "jq '.results[] | {from_user, text, urls: [.entities.urls[].url]}'"
|
||||
result: |
|
||||
{
|
||||
"urls": [
|
||||
"http://t.co/XRvh1ZVw"
|
||||
],
|
||||
"text": "RT @imagemechanics: iPhone 5 website teardown: How Apple compresses video using JPEG, JSON, and <canvas>.\nhttp://t.co/XRvh1ZVw",
|
||||
"from_user": "_AaronNg"
|
||||
}
|
||||
{
|
||||
"urls": [
|
||||
"http://t.co/Lhp92IqD"
|
||||
],
|
||||
"text": "RT @_kud: iPhone 5 website teardown: How Apple compresses video using JPEG, JSON, and <canvas> -- http://t.co/Lhp92IqD",
|
||||
"from_user": "blouerat"
|
||||
}
|
||||
{
|
||||
"urls": [
|
||||
"http://t.co/7KALdWaX"
|
||||
],
|
||||
"text": "Dynamic Forms Mobile App by pmadiset: Few Forms details are hosted on our server in the form of JSON. This JSON ... http://t.co/7KALdWaX",
|
||||
"from_user": "cebu_iphone"
|
||||
}
|
||||
{
|
||||
"urls": [
|
||||
"http://t.co/28Jesbio"
|
||||
],
|
||||
"text": "iPhone 5 website insanity - Video compressing using JPEG, JSON, and <canvas> http://t.co/28Jesbio (Oh #Apple, U So #Awesome...)",
|
||||
"from_user": "dieswaytoofast"
|
||||
}
|
||||
{
|
||||
"urls": [
|
||||
"http://t.co/M70snaIf"
|
||||
],
|
||||
"text": "RT @umutm: A very nice web-based JSON editor - http://t.co/M70snaIf",
|
||||
"from_user": "Leolik"
|
||||
}
|
||||
|
||||
|
||||
- text: |
|
||||
|
||||
Here we're making an object as before, but this time the urls
|
||||
field is being set to `[.entities.urls[].url]`, which collects
|
||||
all of the URLs defined in the entities object.
|
||||
|
||||
- text: |
|
||||
|
||||
- - -
|
||||
|
||||
Here endeth the tutorial! There's lots more to play with, go
|
||||
read [the manual](../manual/) if you're interested, and [download
|
||||
jq](../download/) if you haven't already.
|
||||
@@ -1,90 +0,0 @@
|
||||
headline: Download jq
|
||||
body:
|
||||
|
||||
- text: |
|
||||
|
||||
jq is written in C and has no runtime dependencies, so it should be
|
||||
possible to build it for nearly any platform. Prebuilt binaries are
|
||||
available for Linux, OS X and Windows.
|
||||
|
||||
The binaries should just run, but on OS X and Linux you may need
|
||||
to make them executable first using `chmod +x jq`.
|
||||
|
||||
jq is licensed under the MIT license. For all of the gory
|
||||
details, read the file `COPYING` in the source distribution.
|
||||
|
||||
|
||||
### Linux
|
||||
|
||||
* Binaries for [64-bit](linux64/jq) or [32-bit](linux32/jq) systems.
|
||||
|
||||
* For Arch users, a PKGBUILD for jq-1.1 is in the
|
||||
[AUR](https://aur.archlinux.org/packages.php?ID=63849), as
|
||||
well as a PKGBUILD for the HEAD of master
|
||||
([jq-git](https://aur.archlinux.org/packages.php?ID=63850)).
|
||||
You can install them using
|
||||
[Yaourt](https://wiki.archlinux.org/index.php/Yaourt) or
|
||||
manually by following instructions on [Arch Linux's
|
||||
Wiki](https://wiki.archlinux.org/index.php/Arch_UseRepository).
|
||||
|
||||
* jq 1.2 is [in Debian](http://packages.debian.org/jq)
|
||||
|
||||
### OS X
|
||||
|
||||
* `brew install jq` using [homebrew](http://mxcl.github.com/homebrew/)
|
||||
|
||||
* Or, grab prebuilt [64-bit binaries](osx64/jq) or [32-bit
|
||||
binaries](osx32/jq)
|
||||
|
||||
### Windows
|
||||
|
||||
* Executables for [64-bit](win64/jq.exe) or [32-bit](win32/jq.exe)
|
||||
|
||||
### From source on Linux or OS X
|
||||
|
||||
* [Source tarball for jq $JQ_VERSION](source/jq-$JQ_VERSION.tar.gz)
|
||||
|
||||
You can build it using the usual `./configure && make && sudo
|
||||
make install` rigmarole.
|
||||
|
||||
If you're interested in using the lastest development version, try:
|
||||
|
||||
git clone https://github.com/stedolan/jq.git
|
||||
cd jq
|
||||
autoreconf
|
||||
./configure
|
||||
make
|
||||
sudo make install
|
||||
|
||||
To build it from a git clone, you'll need to install a few
|
||||
packages first:
|
||||
|
||||
* [Flex](http://www.gnu.org/software/flex/)
|
||||
* [Bison](http://www.gnu.org/software/bison/)
|
||||
* [GCC](http://gcc.gnu.org)
|
||||
* [Make](http://www.gnu.org/software/make)
|
||||
* [Autotools](http://www.gnu.org/software/automake)
|
||||
|
||||
For Linux systems, these will all be in your system's package
|
||||
manager, and if you do development on the machine they're most
|
||||
likely already installed.
|
||||
|
||||
On OS X, these are all included in Apple's command line tools, which
|
||||
can be installed from [Xcode](http://developer.apple.com/technologies/tools/).
|
||||
However, you may find that you need a newer version of Bison than the one provided
|
||||
by Apple. This can be found in [Homebrew](http://mxcl.github.com/homebrew/)
|
||||
or [MacPorts](http://macports.org/).
|
||||
|
||||
`flex` and `bison` are used to generate the lexer and parser for
|
||||
jq, and some python scripts generate the UTF8 encoding tables
|
||||
needed for JSON parsing.
|
||||
|
||||
#### Building the documentation
|
||||
|
||||
jq's documentation is compiled into static HTML using
|
||||
[Bonsai](http://www.tinytree.info). To view the documentation
|
||||
locally, run `rake serve` from the docs/ subdirectory. You'll
|
||||
need a few Ruby dependencies, which can be installed by
|
||||
following the instructions in `docs/README.md`.
|
||||
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,34 +0,0 @@
|
||||
headline: jq
|
||||
|
||||
blurb: |
|
||||
|
||||
jq is a lightweight and flexible command-line JSON processor.
|
||||
|
||||
body1: |
|
||||
|
||||
jq is like `sed` for JSON data - you can use it to slice and filter
|
||||
and map and transform structured data with the same ease that `sed`,
|
||||
`awk`, `grep` and friends let you play with text.
|
||||
|
||||
body2: |
|
||||
|
||||
jq is written in portable C, and it has zero runtime
|
||||
dependencies. You can download a single binary, `scp` it to a far away
|
||||
machine, and expect it to work.
|
||||
|
||||
body3: |
|
||||
|
||||
jq can mangle the data format that you have into the one that you
|
||||
want with very little effort, and the program to do so is often
|
||||
shorter and simpler than you'd expect.
|
||||
|
||||
tail: |
|
||||
Go read the [tutorial](tutorial/) for more, or the [manual](manual/)
|
||||
for *way* more.
|
||||
|
||||
news:
|
||||
- date: 2013-05-19
|
||||
body: |
|
||||
|
||||
jq 1.3 (finally) released! Get it on the [download](download/) page.
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
jq(1) -- Command-line JSON processor
|
||||
====================================
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
`jq` can transform JSON in various ways, by selecting, iterating,
|
||||
reducing and otherwise mangling JSON documents.
|
||||
|
||||
This version of `jq` was built without a manual, so this manpage is a
|
||||
stub. For full documentation of the `jq` language, see:
|
||||
|
||||
http://stedolan.github.com/jq
|
||||
|
||||
## BUGS
|
||||
|
||||
Presumably. Report them or discuss them at:
|
||||
|
||||
https://github.com/stedolan/jq/issues
|
||||
|
||||
## AUTHOR
|
||||
|
||||
Stephen Dolan `<mu@netsoc.tcd.ie>`
|
||||
@@ -1,28 +0,0 @@
|
||||
DirectoryIndex index.html
|
||||
FileETag All
|
||||
|
||||
# Compress all static assets
|
||||
<IfModule mod_deflate.c>
|
||||
# compress content with type html, text, and css
|
||||
AddOutputFilterByType DEFLATE text/css text/html text/javascript application/javascript application/x-javascript text/js text/plain text/xml
|
||||
|
||||
<IfModule mod_headers.c>
|
||||
# properly handle requests coming from behind proxies
|
||||
Header append Vary User-Agent
|
||||
</IfModule>
|
||||
</IfModule>
|
||||
|
||||
# Cache, aggressively
|
||||
<IfModule mod_expires.c>
|
||||
ExpiresActive On
|
||||
ExpiresDefault "access plus 10 days"
|
||||
|
||||
<FilesMatch "\.(eot|ttf|otf)$">
|
||||
ExpiresDefault "access plus 10 years"
|
||||
</filesMatch>
|
||||
</IfModule>
|
||||
|
||||
# Mime-types
|
||||
AddType application/vnd.ms-fontobject .eot
|
||||
AddType font/ttf .ttf
|
||||
AddType font/otf .otf
|
||||
-1058
File diff suppressed because it is too large.
Load diff
@@ -1,9 +0,0 @@
|
||||
/*!
|
||||
* Bootstrap Responsive v2.1.1
|
||||
*
|
||||
* Copyright 2012 Twitter, Inc
|
||||
* Licensed under the Apache License v2.0
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Designed and built with all the love in the world @twitter by @mdo and @fat.
|
||||
*/.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.hidden{display:none;visibility:hidden}.visible-phone{display:none!important}.visible-tablet{display:none!important}.hidden-desktop{display:none!important}.visible-desktop{display:inherit!important}@media(min-width:768px) and (max-width:979px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-tablet{display:inherit!important}.hidden-tablet{display:none!important}}@media(max-width:767px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-phone{display:inherit!important}.hidden-phone{display:none!important}}@media(min-width:1200px){.row{margin-left:-30px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:30px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px}.span12{width:1170px}.span11{width:1070px}.span10{width:970px}.span9{width:870px}.span8{width:770px}.span7{width:670px}.span6{width:570px}.span5{width:470px}.span4{width:370px}.span3{width:270px}.span2{width:170px}.span1{width:70px}.offset12{margin-left:1230px}.offset11{margin-left:1130px}.offset10{margin-left:1030px}.offset9{margin-left:930px}.offset8{margin-left:830px}.offset7{margin-left:730px}.offset6{margin-left:630px}.offset5{margin-left:530px}.offset4{margin-left:430px}.offset3{margin-left:330px}.offset2{margin-left:230px}.offset1{margin-left:130px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.564102564102564%;*margin-left:2.5109110747408616%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.45299145299145%;*width:91.39979996362975%}.row-fluid .span10{width:82.90598290598291%;*width:82.8527914166212%}.row-fluid .span9{width:74.35897435897436%;*width:74.30578286961266%}.row-fluid .span8{width:65.81196581196582%;*width:65.75877432260411%}.row-fluid .span7{width:57.26495726495726%;*width:57.21176577559556%}.row-fluid .span6{width:48.717948717948715%;*width:48.664757228587014%}.row-fluid .span5{width:40.17094017094017%;*width:40.11774868157847%}.row-fluid .span4{width:31.623931623931625%;*width:31.570740134569924%}.row-fluid .span3{width:23.076923076923077%;*width:23.023731587561375%}.row-fluid .span2{width:14.52991452991453%;*width:14.476723040552828%}.row-fluid .span1{width:5.982905982905983%;*width:5.929714493544281%}.row-fluid .offset12{margin-left:105.12820512820512%;*margin-left:105.02182214948171%}.row-fluid .offset12:first-child{margin-left:102.56410256410257%;*margin-left:102.45771958537915%}.row-fluid .offset11{margin-left:96.58119658119658%;*margin-left:96.47481360247316%}.row-fluid .offset11:first-child{margin-left:94.01709401709402%;*margin-left:93.91071103837061%}.row-fluid .offset10{margin-left:88.03418803418803%;*margin-left:87.92780505546462%}.row-fluid .offset10:first-child{margin-left:85.47008547008548%;*margin-left:85.36370249136206%}.row-fluid .offset9{margin-left:79.48717948717949%;*margin-left:79.38079650845607%}.row-fluid .offset9:first-child{margin-left:76.92307692307693%;*margin-left:76.81669394435352%}.row-fluid .offset8{margin-left:70.94017094017094%;*margin-left:70.83378796144753%}.row-fluid .offset8:first-child{margin-left:68.37606837606839%;*margin-left:68.26968539734497%}.row-fluid .offset7{margin-left:62.393162393162385%;*margin-left:62.28677941443899%}.row-fluid .offset7:first-child{margin-left:59.82905982905982%;*margin-left:59.72267685033642%}.row-fluid .offset6{margin-left:53.84615384615384%;*margin-left:53.739770867430444%}.row-fluid .offset6:first-child{margin-left:51.28205128205128%;*margin-left:51.175668303327875%}.row-fluid .offset5{margin-left:45.299145299145295%;*margin-left:45.1927623204219%}.row-fluid .offset5:first-child{margin-left:42.73504273504273%;*margin-left:42.62865975631933%}.row-fluid .offset4{margin-left:36.75213675213675%;*margin-left:36.645753773413354%}.row-fluid .offset4:first-child{margin-left:34.18803418803419%;*margin-left:34.081651209310785%}.row-fluid .offset3{margin-left:28.205128205128204%;*margin-left:28.0987452264048%}.row-fluid .offset3:first-child{margin-left:25.641025641025642%;*margin-left:25.53464266230224%}.row-fluid .offset2{margin-left:19.65811965811966%;*margin-left:19.551736679396257%}.row-fluid .offset2:first-child{margin-left:17.094017094017094%;*maLine truncated
|
||||
-5224
File diff suppressed because it is too large.
Load diff
-9
@@ -1,9 +0,0 @@
|
||||
/*!
|
||||
* Bootstrap v2.1.1
|
||||
*
|
||||
* Copyright 2012 Twitter, Inc
|
||||
* Licensed under the Apache License v2.0
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Designed and built with all the love in the world @twitter by @mdo and @fat.
|
||||
*/article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}a:hover,a:active{outline:0}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{width:auto\9;height:auto;max-width:100%;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic}#map_canvas img{max-width:none}button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle}button,input{*overflow:visible;line-height:normal}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button}input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}textarea{overflow:auto;vertical-align:top}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;color:#333;background-color:#fff}a{color:#08c;text-decoration:none}a:hover{color:#005580;text-decoration:underline}.img-rounded{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.img-polaroid{padding:4px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.1);box-shadow:0 1px 3px rgba(0,0,0,0.1)}.img-circle{-webkit-border-radius:500px;-moz-border-radius:500px;border-radius:500px}.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.span12{width:940px}.span11{width:860px}.span10{width:780px}.span9{width:700px}.span8{width:620px}.span7{width:540px}.span6{width:460px}.span5{width:380px}.span4{width:300px}.span3{width:220px}.span2{width:140px}.span1{width:60px}.offset12{margin-left:980px}.offset11{margin-left:900px}.offset10{margin-left:820px}.offset9{margin-left:740px}.offset8{margin-left:660px}.offset7{margin-left:580px}.offset6{margin-left:500px}.offset5{margin-left:420px}.offset4{margin-left:340px}.offset3{margin-left:260px}.offset2{margin-left:180px}.offset1{margin-left:100px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.127659574468085%;*margin-left:2.074468085106383%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.48936170212765%;*width:91.43617021276594%}.row-fluid .span10{width:82.97872340425532%;*width:82.92553191489361%}.row-fluid .span9{width:74.46808510638297%;*width:74.41489361702126%}.row-fluid .span8{width:65.95744680851064%;*width:65.90425531914893%}.row-fluid .span7{width:57.44680851063829%;*width:57.39361702127659%}.row-fluid .span6{width:48.93617021276595%;*width:48.88297872340425%}.row-fluid .span5{width:40.42553191489362%;*width:40.37234042553192%}.row-fluid .span4{width:31.914893617021278%;*width:31.861702127659576%}.row-fluid .span3{width:23.404255319148934%;*width:23.351063829787233%}.row-fluid .span2{width:14.893617021276595%;*width:14.840425531914894%}.row-fluid .span1{width:6.382978723404255%;*width:6.329787234042553%}.row-fluid .offset12{margin-left:104.25531914893617%;*margin-left:104.14893617021275%}.row-fluid .offset12:first-child{margin-left:102.12765957446808%;*margin-left:102.02127659574467%}.row-fluid .offset11{margin-left:95.74468085106382%;*margin-left:95.6382978723404%}.row-fluid .offset11:first-child{margin-left:93.61702127659574%;*margin-left:93.51063829787232%}.row-fluid .offset10{margin-left:87.23404255319149%;*margin-left:87.12765957446807%}.row-fluid .offset10:first-child{margin-left:85.1063829787234%;*margin-left:84.99999999999999%}.row-fluid .offset9{margin-left:78.72340425531914%;*margin-left:78.61702127659572%}.row-fluid .offset9:first-child{margin-left:76.59574468085106%;*margin-left:76.48936170212764%}.row-fluiLine truncated
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 8.6 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB |
-2027
File diff suppressed because it is too large.
Load diff
-6
@@ -1,6 +0,0 @@
|
||||
/*!
|
||||
* Bootstrap.js by @fat & @mdo
|
||||
* Copyright 2012 Twitter, Inc.
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||
*/
|
||||
!function(e){e(function(){"use strict";e.support.transition=function(){var e=function(){var e=document.createElement("bootstrap"),t={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"},n;for(n in t)if(e.style[n]!==undefined)return t[n]}();return e&&{end:e}}()})}(window.jQuery),!function(e){"use strict";var t='[data-dismiss="alert"]',n=function(n){e(n).on("click",t,this.close)};n.prototype.close=function(t){function s(){i.trigger("closed").remove()}var n=e(this),r=n.attr("data-target"),i;r||(r=n.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,"")),i=e(r),t&&t.preventDefault(),i.length||(i=n.hasClass("alert")?n:n.parent()),i.trigger(t=e.Event("close"));if(t.isDefaultPrevented())return;i.removeClass("in"),e.support.transition&&i.hasClass("fade")?i.on(e.support.transition.end,s):s()},e.fn.alert=function(t){return this.each(function(){var r=e(this),i=r.data("alert");i||r.data("alert",i=new n(this)),typeof t=="string"&&i[t].call(r)})},e.fn.alert.Constructor=n,e(function(){e("body").on("click.alert.data-api",t,n.prototype.close)})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.button.defaults,n)};t.prototype.setState=function(e){var t="disabled",n=this.$element,r=n.data(),i=n.is("input")?"val":"html";e+="Text",r.resetText||n.data("resetText",n[i]()),n[i](r[e]||this.options[e]),setTimeout(function(){e=="loadingText"?n.addClass(t).attr(t,t):n.removeClass(t).removeAttr(t)},0)},t.prototype.toggle=function(){var e=this.$element.closest('[data-toggle="buttons-radio"]');e&&e.find(".active").removeClass("active"),this.$element.toggleClass("active")},e.fn.button=function(n){return this.each(function(){var r=e(this),i=r.data("button"),s=typeof n=="object"&&n;i||r.data("button",i=new t(this,s)),n=="toggle"?i.toggle():n&&i.setState(n)})},e.fn.button.defaults={loadingText:"loading..."},e.fn.button.Constructor=t,e(function(){e("body").on("click.button.data-api","[data-toggle^=button]",function(t){var n=e(t.target);n.hasClass("btn")||(n=n.closest(".btn")),n.button("toggle")})})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=n,this.options.slide&&this.slide(this.options.slide),this.options.pause=="hover"&&this.$element.on("mouseenter",e.proxy(this.pause,this)).on("mouseleave",e.proxy(this.cycle,this))};t.prototype={cycle:function(t){return t||(this.paused=!1),this.options.interval&&!this.paused&&(this.interval=setInterval(e.proxy(this.next,this),this.options.interval)),this},to:function(t){var n=this.$element.find(".item.active"),r=n.parent().children(),i=r.index(n),s=this;if(t>r.length-1||t<0)return;return this.sliding?this.$element.one("slid",function(){s.to(t)}):i==t?this.pause().cycle():this.slide(t>i?"next":"prev",e(r[t]))},pause:function(t){return t||(this.paused=!0),this.$element.find(".next, .prev").length&&e.support.transition.end&&(this.$element.trigger(e.support.transition.end),this.cycle()),clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(t,n){var r=this.$element.find(".item.active"),i=n||r[t](),s=this.interval,o=t=="next"?"left":"right",u=t=="next"?"first":"last",a=this,f=e.Event("slide",{relatedTarget:i[0]});this.sliding=!0,s&&this.pause(),i=i.length?i:this.$element.find(".item")[u]();if(i.hasClass("active"))return;if(e.support.transition&&this.$element.hasClass("slide")){this.$element.trigger(f);if(f.isDefaultPrevented())return;i.addClass(t),i[0].offsetWidth,r.addClass(o),i.addClass(o),this.$element.one(e.support.transition.end,function(){i.removeClass([t,o].join(" ")).addClass("active"),r.removeClass(["active",o].join(" ")),a.sliding=!1,setTimeout(function(){a.$element.trigger("slid")},0)})}else{this.$element.trigger(f);if(f.isDefaultPrevented())return;r.removeClass("active"),i.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return s&&this.cycle(),this}},e.fn.carousel=function(n){return this.each(function(){var r=e(this),i=r.data("carousel"),s=e.extend({},e.fn.carousel.defaults,typeof n=="object"&&n),o=typeof n=="string"?n:s.slide;i||r.data("carousel",i=new t(this,s)),typeof n=="number"?i.to(n):o?i[o]():s.interval&&i.cycle()})},e.fn.carousel.defaults={interval:5e3,pause:"hover"},e.fn.carousel.Constructor=t,e(function(){e("body").on("click.carousel.data-api","[data-slide]",function(t){var n=e(this),r,i=e(n.attr("data-target")||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,"")),s=!i.data("modal")&&e.extend({},i.data(),n.data());i.carousel(s),t.preventDefault()})})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.collapse.defaults,n),this.options.parent&&(this.$parent=e(this.options.parent)),this.options.toggle&&this.toggle()};t.prototype={constructor:t,dimension:function(){var e=this.$element.hLine truncated
|
||||
@@ -1,99 +0,0 @@
|
||||
@charset "utf-8";
|
||||
body {
|
||||
padding-top: 60px;
|
||||
}
|
||||
|
||||
footer {
|
||||
background-color: #F5F5F5;
|
||||
padding: 40px 0;
|
||||
height: 100px;
|
||||
margin-top: 40px;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
section {
|
||||
padding-top: 30px;
|
||||
}
|
||||
|
||||
.manual-example {
|
||||
border-top: 1px solid #E5E5E5;
|
||||
}
|
||||
|
||||
.manual-example td {
|
||||
white-space: pre-wrap;
|
||||
font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
|
||||
}
|
||||
|
||||
.manual-example td.jqprogram {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.manual-example th {
|
||||
text-align: right;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.tutorial-example {
|
||||
position: relative;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tutorial-example pre {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.tutorial-example a {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
padding: 15px 8px;
|
||||
color: #777777;
|
||||
font-weight: bold;
|
||||
line-height: 10px;
|
||||
font-size: 12px;
|
||||
border-left: 1px solid #DDDDDD;
|
||||
bottom: 0;
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#blurb {
|
||||
padding-top: 40px;
|
||||
padding-right: 50px;
|
||||
}
|
||||
|
||||
#blurb p {
|
||||
font-size: 20pt;
|
||||
}
|
||||
|
||||
#blurb .btn-group {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
#multiblurb {
|
||||
line-height: 1.7;
|
||||
text-align: center;
|
||||
font-size: 12pt;
|
||||
}
|
||||
|
||||
#multiblurb code {
|
||||
border: 0;
|
||||
font-size: 12pt;
|
||||
}
|
||||
|
||||
h3 code {
|
||||
border: 0;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#news {
|
||||
font-size: 12pt;
|
||||
.date {
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 8.0 KiB |
@@ -1,2 +0,0 @@
|
||||
User-agent: *
|
||||
Allow: /
|
||||
@@ -1,18 +0,0 @@
|
||||
# The key value pairs found below are available within the templates.
|
||||
|
||||
:url: http://stedolan.github.io/jq
|
||||
|
||||
# This line is modified by the Makefile. To change the version number,
|
||||
# edit the Autoconf version number at the top of configure.ac
|
||||
jq_version: "1.3"
|
||||
|
||||
root: '/jq'
|
||||
footer: |
|
||||
|
||||
This website is made with [Bonsai](http://www.tinytree.info) and
|
||||
[Twitter Bootstrap](http://twitter.github.com/bootstrap/), themed
|
||||
with [Bootswatch](http://bootswatch.com).
|
||||
|
||||
jq is licensed under the MIT license (code) and the
|
||||
[CC-BY-3.0](http://creativecommons.org/licenses/by/3.0/) license
|
||||
(docs).
|
||||
Vendored
-34
@@ -1,34 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
{% include "shared/head" %}
|
||||
<body id="{{slug}}">
|
||||
{% include "shared/header" %}
|
||||
|
||||
<div class="container">
|
||||
|
||||
<div class="row">
|
||||
<h1>{{headline}}</h1>
|
||||
{% for item in body %}
|
||||
{% if item.text %}
|
||||
{{ item.text | replace: '$JQ_VERSION', jq_version | markdownify }}
|
||||
{% endif %}
|
||||
|
||||
{% if item.command %}
|
||||
|
||||
|
||||
{% capture resultID %}{{ "result" | unique}}{% endcapture %}
|
||||
<div class="tutorial-example">
|
||||
<div class="accordion-heading">
|
||||
<pre>{{item.command}}</pre>
|
||||
<a data-toggle="collapse" href="#{{resultID}}">Show result</a>
|
||||
</div>
|
||||
<div id="{{resultID}}" class="accordion-body collapse"><pre>{{item.result}}</pre></div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% include "shared/footer" %}
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Vendored
-51
@@ -1,51 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
{% include "shared/head" %}
|
||||
<body id="{{slug}}">
|
||||
{% include "shared/header" %}
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="hero-unit">
|
||||
<img src="{{root}}/jq.png" style="width: 400px; height: 220px; float: left; margin-right: 60px">
|
||||
<div id="blurb">
|
||||
{{blurb | markdownify}}
|
||||
<div class="btn-group" style="float:right">
|
||||
<a class="btn btn-primary dropdown-toggle" data-toggle="dropdown" href="#">
|
||||
Download jq-{{jq_version}}
|
||||
<span class="caret"></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="{{root}}/download/linux64/jq">Linux (64-bit)</a></li>
|
||||
<li><a href="{{root}}/download/osx64/jq">OS X (64-bit)</a></li>
|
||||
<li><a href="{{root}}/download/">Other platforms and source</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div style="clear:both;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" id="multiblurb">
|
||||
<div class="span4">{{body1 | markdownify}}</div>
|
||||
<div class="span4">{{body2 | markdownify}}</div>
|
||||
<div class="span4">{{body3 | markdownify}}</div>
|
||||
</div>
|
||||
<div class="row" style="text-align:center; margin-top: 30px">
|
||||
{{tail | markdownify}}
|
||||
</div>
|
||||
<div class="row">
|
||||
<h2>News</h2>
|
||||
<div id="news">
|
||||
<ul>
|
||||
{% for item in news %}
|
||||
<li><span class="date">{{ item.date | date: '%m %B %Y' }}</span>{{item.body | markdownify}}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% include "shared/footer" %}
|
||||
</body>
|
||||
</html>
|
||||
Vendored
-110
@@ -1,110 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
{% include "shared/head" %}
|
||||
<body id="{{slug}}" data-spy="scroll" data-target="#navcolumn" data-offset="100">
|
||||
{% include "shared/header" %}
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="span3" id="navcolumn">
|
||||
<div class="affix">
|
||||
<h4>Contents</h4>
|
||||
<ul class="nav nav-pills nav-stacked">
|
||||
{% for section in sections %}
|
||||
<li {% if forloop.first %} class="active" {% endif %}>
|
||||
<a href="#{{section.title | sanitize}}">{{section.title}}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<form class="form-search">
|
||||
<script type="text/javascript">
|
||||
var section_map = {
|
||||
{% for section in sections %}
|
||||
{% for entry in section.entries %}
|
||||
{{entry.title | json}} : {{entry.title | sanitize | json}},
|
||||
{% endfor %}
|
||||
{{section.title | json}} : {{section.title | sanitize | json}}
|
||||
{% unless forloop.last %},{% endunless %}
|
||||
{% endfor %}
|
||||
};
|
||||
var section_names = $.map(section_map, function(v,k){return k});
|
||||
$(function(){
|
||||
$('#searchbox').typeahead({source: section_names})
|
||||
$('#searchbox').change(function() {
|
||||
if ($(this).val() in section_map) {
|
||||
location.hash = '#' + section_map[$(this).val()];
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<input type="text"
|
||||
class="input-medium search-query"
|
||||
placeholder="Search"
|
||||
autocomplete="off"
|
||||
id="searchbox">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="span9">
|
||||
<h1>{{headline}}</h1>
|
||||
{{ body | markdownify }}
|
||||
{% for section in sections %}
|
||||
<section id="{{section.title | sanitize}}">
|
||||
<h2>{{section.title}}</h2>
|
||||
{{section.body | markdownify}}
|
||||
{% for entry in section.entries %}
|
||||
<section id="{{entry.title | sanitize}}">
|
||||
<h3>
|
||||
{{entry.title | markdownify}}
|
||||
{% if entry.subtitle %}<small>{{entry.subtitle}}</small>{% endif %}
|
||||
</h3>
|
||||
{{entry.body | markdownify}}
|
||||
|
||||
{% if entry.examples %}
|
||||
<div>
|
||||
{% capture exampleID %}{{ "" | unique }}{% endcapture %}
|
||||
<a data-toggle="collapse" href="#example{{exampleID}}">
|
||||
<i class="icon-chevron-right"></i>
|
||||
{% if entry.examples[1] %}Examples{%else%}Example{%endif%}
|
||||
</a>
|
||||
<div id="example{{exampleID}}" class="collapse">
|
||||
|
||||
{% for example in entry.examples %}
|
||||
<table class="manual-example">
|
||||
<tr><th></th><td class="jqprogram">jq '{{example.program}}'</td></tr>
|
||||
<tr><th>Input</th><td>{{example.input}}</td></tr>
|
||||
{% unless example.output[0] %}
|
||||
<tr>
|
||||
<th>Output</th>
|
||||
<td><i>none</i></td>
|
||||
</tr>
|
||||
{% endunless %}
|
||||
{% for output in example.output %}
|
||||
<tr>
|
||||
{% if forloop.index == 1 %}
|
||||
<th>Output</th>
|
||||
{% else %}
|
||||
<th></th>
|
||||
{% endif %}
|
||||
<td>{{output}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
</table>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% endfor %}
|
||||
</section>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
{% include "shared/footer" %}
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
||||
Vendored
-5
@@ -1,5 +0,0 @@
|
||||
<footer>
|
||||
<div class="container">
|
||||
{{ footer | markdownify }}
|
||||
</div>
|
||||
</footer>
|
||||
Vendored
-12
@@ -1,12 +0,0 @@
|
||||
<head>
|
||||
<meta charset=UTF-8>
|
||||
<title>{{headline}}</title>
|
||||
<link rel="stylesheet" href="{{root}}/bootstrap/css/bootstrap.css" type="text/css">
|
||||
<link rel="stylesheet" href="{{root}}/css/base.css" type="text/css">
|
||||
<!--[if IE]>
|
||||
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"></script>
|
||||
<script src="{{root}}/bootstrap/js/bootstrap.js"></script>
|
||||
|
||||
</head>
|
||||
Vendored
-24
@@ -1,24 +0,0 @@
|
||||
|
||||
<div class="navbar navbar-inverse navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="brand" href="{{root}}/">jq</a>
|
||||
<div class="nav-collapse collapse">
|
||||
<ul class="nav">
|
||||
{% for item in navigation %}
|
||||
<li {% if item.permalink == permalink %} class="active" {% endif %}>
|
||||
<a href="{{root}}{{item.permalink}}">{{item.name}}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
<li><a href="https://github.com/stedolan/jq/issues">Issues</a></li>
|
||||
<li><a href="https://github.com/stedolan/jq">Source</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,82 +0,0 @@
|
||||
#ifndef EXEC_STACK_H
|
||||
#define EXEC_STACK_H
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
struct determine_alignment {
|
||||
char x;
|
||||
union { int i; double d; uint64_t u64; size_t sz; void* ptr; } u;
|
||||
};
|
||||
enum {ALIGNMENT = offsetof(struct determine_alignment, u)};
|
||||
|
||||
static size_t align_round_up(size_t sz) {
|
||||
return ((sz + (ALIGNMENT - 1)) / ALIGNMENT) * ALIGNMENT;
|
||||
}
|
||||
|
||||
typedef int stack_ptr;
|
||||
|
||||
struct stack {
|
||||
char* mem_end; // one-past-the-end of allocated region
|
||||
stack_ptr bound;
|
||||
stack_ptr limit; // 0 - stack is empty
|
||||
};
|
||||
|
||||
static void stack_init(struct stack* s) {
|
||||
s->mem_end = 0;
|
||||
s->bound = ALIGNMENT;
|
||||
s->limit = 0;
|
||||
}
|
||||
|
||||
static void stack_reset(struct stack* s) {
|
||||
assert(s->limit == 0 && "stack freed while not empty");
|
||||
char* mem_start = s->mem_end - ( -s->bound + ALIGNMENT);
|
||||
free(mem_start);
|
||||
stack_init(s);
|
||||
}
|
||||
|
||||
static int stack_pop_will_free(struct stack* s, stack_ptr p) {
|
||||
return p == s->limit;
|
||||
}
|
||||
|
||||
static void* stack_block(struct stack* s, stack_ptr p) {
|
||||
return (void*)(s->mem_end + p);
|
||||
}
|
||||
|
||||
static stack_ptr* stack_block_next(struct stack* s, stack_ptr p) {
|
||||
return &((stack_ptr*)stack_block(s, p))[-1];
|
||||
}
|
||||
|
||||
static void stack_reallocate(struct stack* s, size_t sz) {
|
||||
int old_mem_length = -(s->bound) + ALIGNMENT;
|
||||
char* old_mem_start = s->mem_end - old_mem_length;
|
||||
|
||||
int new_mem_length = align_round_up((old_mem_length + sz + 256) * 2);
|
||||
char* new_mem_start = realloc(old_mem_start, new_mem_length);
|
||||
memmove(new_mem_start + (new_mem_length - old_mem_length),
|
||||
new_mem_start, old_mem_length);
|
||||
s->mem_end = new_mem_start + new_mem_length;
|
||||
s->bound = -(new_mem_length - ALIGNMENT);
|
||||
}
|
||||
|
||||
static stack_ptr stack_push_block(struct stack* s, stack_ptr p, size_t sz) {
|
||||
int alloc_sz = align_round_up(sz) + ALIGNMENT;
|
||||
stack_ptr r = s->limit - alloc_sz;
|
||||
if (r < s->bound) {
|
||||
stack_reallocate(s, alloc_sz);
|
||||
}
|
||||
s->limit = r;
|
||||
*stack_block_next(s, r) = p;
|
||||
return r;
|
||||
}
|
||||
|
||||
static stack_ptr stack_pop_block(struct stack* s, stack_ptr p, size_t sz) {
|
||||
stack_ptr r = *stack_block_next(s, p);
|
||||
if (p == s->limit) {
|
||||
int alloc_sz = align_round_up(sz) + ALIGNMENT;
|
||||
s->limit += alloc_sz;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
#endif
|
||||
@@ -1,769 +0,0 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "execute.h"
|
||||
|
||||
#include "exec_stack.h"
|
||||
#include "bytecode.h"
|
||||
|
||||
#include "jv_alloc.h"
|
||||
#include "jq_parser.h"
|
||||
#include "locfile.h"
|
||||
#include "jv.h"
|
||||
#include "jv_aux.h"
|
||||
#include "parser.h"
|
||||
#include "builtin.h"
|
||||
|
||||
struct jq_state {
|
||||
void (*nomem_handler)(void *);
|
||||
void *nomem_handler_data;
|
||||
struct bytecode* bc;
|
||||
|
||||
struct stack stk;
|
||||
stack_ptr curr_frame;
|
||||
stack_ptr stk_top;
|
||||
stack_ptr fork_top;
|
||||
|
||||
jv path;
|
||||
int subexp_nest;
|
||||
int debug_trace_enabled;
|
||||
int initial_execution;
|
||||
};
|
||||
|
||||
struct closure {
|
||||
struct bytecode* bc;
|
||||
stack_ptr env;
|
||||
};
|
||||
|
||||
union frame_entry {
|
||||
struct closure closure;
|
||||
jv localvar;
|
||||
};
|
||||
|
||||
struct frame {
|
||||
struct bytecode* bc;
|
||||
stack_ptr env;
|
||||
stack_ptr retdata;
|
||||
uint16_t* retaddr;
|
||||
/* bc->nclosures closures followed by bc->nlocals local variables */
|
||||
union frame_entry entries[0];
|
||||
};
|
||||
|
||||
static int frame_size(struct bytecode* bc) {
|
||||
return sizeof(struct frame) + sizeof(union frame_entry) * (bc->nclosures + bc->nlocals);
|
||||
}
|
||||
|
||||
static struct frame* frame_current(struct jq_state* jq) {
|
||||
struct frame* fp = stack_block(&jq->stk, jq->curr_frame);
|
||||
|
||||
stack_ptr next = *stack_block_next(&jq->stk, jq->curr_frame);
|
||||
if (next) {
|
||||
struct frame* fpnext = stack_block(&jq->stk, next);
|
||||
struct bytecode* bc = fpnext->bc;
|
||||
assert(fp->retaddr >= bc->code && fp->retaddr < bc->code + bc->codelen);
|
||||
} else {
|
||||
assert(fp->retaddr == 0);
|
||||
}
|
||||
return fp;
|
||||
}
|
||||
|
||||
static stack_ptr frame_get_level(struct jq_state* jq, int level) {
|
||||
stack_ptr fr = jq->curr_frame;
|
||||
for (int i=0; i<level; i++) {
|
||||
struct frame* fp = stack_block(&jq->stk, fr);
|
||||
fr = fp->env;
|
||||
}
|
||||
return fr;
|
||||
}
|
||||
|
||||
static jv* frame_local_var(struct jq_state* jq, int var, int level) {
|
||||
struct frame* fr = stack_block(&jq->stk, frame_get_level(jq, level));
|
||||
assert(var >= 0);
|
||||
assert(var < fr->bc->nlocals);
|
||||
return &fr->entries[fr->bc->nclosures + var].localvar;
|
||||
}
|
||||
|
||||
static struct closure make_closure(struct jq_state* jq, uint16_t* pc) {
|
||||
uint16_t level = *pc++;
|
||||
uint16_t idx = *pc++;
|
||||
stack_ptr fridx = frame_get_level(jq, level);
|
||||
struct frame* fr = stack_block(&jq->stk, fridx);
|
||||
if (idx & ARG_NEWCLOSURE) {
|
||||
int subfn_idx = idx & ~ARG_NEWCLOSURE;
|
||||
assert(subfn_idx < fr->bc->nsubfunctions);
|
||||
struct closure cl = {fr->bc->subfunctions[subfn_idx],
|
||||
fridx};
|
||||
return cl;
|
||||
} else {
|
||||
int closure = idx;
|
||||
assert(closure >= 0);
|
||||
assert(closure < fr->bc->nclosures);
|
||||
return fr->entries[closure].closure;
|
||||
}
|
||||
}
|
||||
|
||||
static struct frame* frame_push(struct jq_state* jq, struct closure callee,
|
||||
uint16_t* argdef, int nargs) {
|
||||
stack_ptr new_frame_idx = stack_push_block(&jq->stk, jq->curr_frame, frame_size(callee.bc));
|
||||
struct frame* new_frame = stack_block(&jq->stk, new_frame_idx);
|
||||
new_frame->bc = callee.bc;
|
||||
new_frame->env = callee.env;
|
||||
assert(nargs == new_frame->bc->nclosures);
|
||||
union frame_entry* entries = new_frame->entries;
|
||||
for (int i=0; i<nargs; i++) {
|
||||
entries->closure = make_closure(jq, argdef + i * 2);
|
||||
entries++;
|
||||
}
|
||||
for (int i=0; i<callee.bc->nlocals; i++) {
|
||||
entries->localvar = jv_invalid();
|
||||
entries++;
|
||||
}
|
||||
jq->curr_frame = new_frame_idx;
|
||||
return new_frame;
|
||||
}
|
||||
|
||||
static void frame_pop(struct jq_state* jq) {
|
||||
assert(jq->curr_frame);
|
||||
struct frame* fp = frame_current(jq);
|
||||
if (stack_pop_will_free(&jq->stk, jq->curr_frame)) {
|
||||
int nlocals = fp->bc->nlocals;
|
||||
for (int i=0; i<nlocals; i++) {
|
||||
jv_free(*frame_local_var(jq, i, 0));
|
||||
}
|
||||
}
|
||||
jq->curr_frame = stack_pop_block(&jq->stk, jq->curr_frame, frame_size(fp->bc));
|
||||
}
|
||||
|
||||
void stack_push(jq_state *jq, jv val) {
|
||||
assert(jv_is_valid(val));
|
||||
jq->stk_top = stack_push_block(&jq->stk, jq->stk_top, sizeof(jv));
|
||||
jv* sval = stack_block(&jq->stk, jq->stk_top);
|
||||
*sval = val;
|
||||
}
|
||||
|
||||
jv stack_pop(jq_state *jq) {
|
||||
jv* sval = stack_block(&jq->stk, jq->stk_top);
|
||||
jv val = *sval;
|
||||
if (!stack_pop_will_free(&jq->stk, jq->stk_top)) {
|
||||
val = jv_copy(val);
|
||||
}
|
||||
jq->stk_top = stack_pop_block(&jq->stk, jq->stk_top, sizeof(jv));
|
||||
assert(jv_is_valid(val));
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
struct forkpoint {
|
||||
stack_ptr saved_data_stack;
|
||||
stack_ptr saved_curr_frame;
|
||||
int path_len, subexp_nest;
|
||||
uint16_t* return_address;
|
||||
};
|
||||
|
||||
struct stack_pos {
|
||||
stack_ptr saved_data_stack, saved_curr_frame;
|
||||
};
|
||||
|
||||
struct stack_pos stack_get_pos(jq_state* jq) {
|
||||
struct stack_pos sp = {jq->stk_top, jq->curr_frame};
|
||||
return sp;
|
||||
}
|
||||
|
||||
void stack_save(jq_state *jq, uint16_t* retaddr, struct stack_pos sp){
|
||||
jq->fork_top = stack_push_block(&jq->stk, jq->fork_top, sizeof(struct forkpoint));
|
||||
struct forkpoint* fork = stack_block(&jq->stk, jq->fork_top);
|
||||
fork->saved_data_stack = jq->stk_top;
|
||||
fork->saved_curr_frame = jq->curr_frame;
|
||||
fork->path_len =
|
||||
jv_get_kind(jq->path) == JV_KIND_ARRAY ? jv_array_length(jv_copy(jq->path)) : 0;
|
||||
fork->subexp_nest = jq->subexp_nest;
|
||||
fork->return_address = retaddr;
|
||||
jq->stk_top = sp.saved_data_stack;
|
||||
jq->curr_frame = sp.saved_curr_frame;
|
||||
}
|
||||
|
||||
void path_append(jq_state* jq, jv component) {
|
||||
if (jq->subexp_nest == 0 && jv_get_kind(jq->path) == JV_KIND_ARRAY) {
|
||||
int n1 = jv_array_length(jv_copy(jq->path));
|
||||
jq->path = jv_array_append(jq->path, component);
|
||||
int n2 = jv_array_length(jv_copy(jq->path));
|
||||
assert(n2 == n1 + 1);
|
||||
} else {
|
||||
jv_free(component);
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t* stack_restore(jq_state *jq){
|
||||
while (!stack_pop_will_free(&jq->stk, jq->fork_top)) {
|
||||
if (stack_pop_will_free(&jq->stk, jq->stk_top)) {
|
||||
jv_free(stack_pop(jq));
|
||||
} else if (stack_pop_will_free(&jq->stk, jq->curr_frame)) {
|
||||
frame_pop(jq);
|
||||
} else {
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (jq->fork_top == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct forkpoint* fork = stack_block(&jq->stk, jq->fork_top);
|
||||
uint16_t* retaddr = fork->return_address;
|
||||
jq->stk_top = fork->saved_data_stack;
|
||||
jq->curr_frame = fork->saved_curr_frame;
|
||||
int path_len = fork->path_len;
|
||||
if (jv_get_kind(jq->path) == JV_KIND_ARRAY) {
|
||||
assert(path_len >= 0);
|
||||
jq->path = jv_array_slice(jq->path, 0, path_len);
|
||||
} else {
|
||||
assert(path_len == 0);
|
||||
}
|
||||
jq->subexp_nest = fork->subexp_nest;
|
||||
jq->fork_top = stack_pop_block(&jq->stk, jq->fork_top, sizeof(struct forkpoint));
|
||||
return retaddr;
|
||||
}
|
||||
|
||||
static void jq_reset(jq_state *jq) {
|
||||
while (stack_restore(jq)) {}
|
||||
|
||||
assert(jq->stk_top == 0);
|
||||
assert(jq->fork_top == 0);
|
||||
assert(jq->curr_frame == 0);
|
||||
stack_reset(&jq->stk);
|
||||
|
||||
if (jv_get_kind(jq->path) != JV_KIND_INVALID)
|
||||
jv_free(jq->path);
|
||||
jq->path = jv_null();
|
||||
jq->subexp_nest = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void print_error(jv value) {
|
||||
assert(!jv_is_valid(value));
|
||||
jv msg = jv_invalid_get_msg(value);
|
||||
if (jv_get_kind(msg) == JV_KIND_STRING) {
|
||||
fprintf(stderr, "jq: error: %s\n", jv_string_value(msg));
|
||||
}
|
||||
jv_free(msg);
|
||||
}
|
||||
#define ON_BACKTRACK(op) ((op)+NUM_OPCODES)
|
||||
|
||||
jv jq_next(jq_state *jq) {
|
||||
jv cfunc_input[MAX_CFUNCTION_ARGS];
|
||||
|
||||
jv_nomem_handler(jq->nomem_handler, jq->nomem_handler_data);
|
||||
|
||||
uint16_t* pc = stack_restore(jq);
|
||||
assert(pc);
|
||||
|
||||
int backtracking = !jq->initial_execution;
|
||||
jq->initial_execution = 0;
|
||||
while (1) {
|
||||
uint16_t opcode = *pc;
|
||||
|
||||
if (jq->debug_trace_enabled) {
|
||||
dump_operation(frame_current(jq)->bc, pc);
|
||||
printf("\t");
|
||||
const struct opcode_description* opdesc = opcode_describe(opcode);
|
||||
stack_ptr param = 0;
|
||||
if (!backtracking) {
|
||||
int stack_in = opdesc->stack_in;
|
||||
if (stack_in == -1) stack_in = pc[1];
|
||||
for (int i=0; i<stack_in; i++) {
|
||||
if (i == 0) {
|
||||
param = jq->stk_top;
|
||||
} else {
|
||||
printf(" | ");
|
||||
param = *stack_block_next(&jq->stk, param);
|
||||
}
|
||||
if (!param) break;
|
||||
jv_dump(jv_copy(*(jv*)stack_block(&jq->stk, param)), 0);
|
||||
//printf("<%d>", jv_get_refcnt(param->val));
|
||||
//printf(" -- ");
|
||||
//jv_dump(jv_copy(jq->path), 0);
|
||||
}
|
||||
} else {
|
||||
printf("\t<backtracking>");
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if (backtracking) {
|
||||
opcode = ON_BACKTRACK(opcode);
|
||||
backtracking = 0;
|
||||
}
|
||||
pc++;
|
||||
|
||||
switch (opcode) {
|
||||
default: assert(0 && "invalid instruction");
|
||||
|
||||
case LOADK: {
|
||||
jv v = jv_array_get(jv_copy(frame_current(jq)->bc->constants), *pc++);
|
||||
assert(jv_is_valid(v));
|
||||
jv_free(stack_pop(jq));
|
||||
stack_push(jq, v);
|
||||
break;
|
||||
}
|
||||
|
||||
case DUP: {
|
||||
jv v = stack_pop(jq);
|
||||
stack_push(jq, jv_copy(v));
|
||||
stack_push(jq, v);
|
||||
break;
|
||||
}
|
||||
|
||||
case DUP2: {
|
||||
jv keep = stack_pop(jq);
|
||||
jv v = stack_pop(jq);
|
||||
stack_push(jq, jv_copy(v));
|
||||
stack_push(jq, keep);
|
||||
stack_push(jq, v);
|
||||
break;
|
||||
}
|
||||
|
||||
case SUBEXP_BEGIN: {
|
||||
jv v = stack_pop(jq);
|
||||
stack_push(jq, jv_copy(v));
|
||||
stack_push(jq, v);
|
||||
jq->subexp_nest++;
|
||||
break;
|
||||
}
|
||||
|
||||
case SUBEXP_END: {
|
||||
assert(jq->subexp_nest > 0);
|
||||
jq->subexp_nest--;
|
||||
jv a = stack_pop(jq);
|
||||
jv b = stack_pop(jq);
|
||||
stack_push(jq, a);
|
||||
stack_push(jq, b);
|
||||
break;
|
||||
}
|
||||
|
||||
case POP: {
|
||||
jv_free(stack_pop(jq));
|
||||
break;
|
||||
}
|
||||
|
||||
case APPEND: {
|
||||
jv v = stack_pop(jq);
|
||||
uint16_t level = *pc++;
|
||||
uint16_t vidx = *pc++;
|
||||
jv* var = frame_local_var(jq, vidx, level);
|
||||
assert(jv_get_kind(*var) == JV_KIND_ARRAY);
|
||||
*var = jv_array_append(*var, v);
|
||||
break;
|
||||
}
|
||||
|
||||
case INSERT: {
|
||||
jv stktop = stack_pop(jq);
|
||||
jv v = stack_pop(jq);
|
||||
jv k = stack_pop(jq);
|
||||
jv objv = stack_pop(jq);
|
||||
assert(jv_get_kind(objv) == JV_KIND_OBJECT);
|
||||
if (jv_get_kind(k) == JV_KIND_STRING) {
|
||||
stack_push(jq, jv_object_set(objv, k, v));
|
||||
stack_push(jq, stktop);
|
||||
} else {
|
||||
print_error(jv_invalid_with_msg(jv_string_fmt("Cannot use %s as object key",
|
||||
jv_kind_name(jv_get_kind(k)))));
|
||||
jv_free(stktop);
|
||||
jv_free(v);
|
||||
jv_free(k);
|
||||
jv_free(objv);
|
||||
goto do_backtrack;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ON_BACKTRACK(RANGE):
|
||||
case RANGE: {
|
||||
uint16_t level = *pc++;
|
||||
uint16_t v = *pc++;
|
||||
jv* var = frame_local_var(jq, v, level);
|
||||
jv max = stack_pop(jq);
|
||||
if (jv_get_kind(*var) != JV_KIND_NUMBER ||
|
||||
jv_get_kind(max) != JV_KIND_NUMBER) {
|
||||
print_error(jv_invalid_with_msg(jv_string_fmt("Range bounds must be numeric")));
|
||||
jv_free(max);
|
||||
goto do_backtrack;
|
||||
} else if (jv_number_value(jv_copy(*var)) >= jv_number_value(jv_copy(max))) {
|
||||
/* finished iterating */
|
||||
goto do_backtrack;
|
||||
} else {
|
||||
jv curr = jv_copy(*var);
|
||||
*var = jv_number(jv_number_value(*var) + 1);
|
||||
|
||||
struct stack_pos spos = stack_get_pos(jq);
|
||||
stack_push(jq, jv_copy(max));
|
||||
stack_save(jq, pc - 3, spos);
|
||||
|
||||
stack_push(jq, curr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// FIXME: loadv/storev may do too much copying/freeing
|
||||
case LOADV: {
|
||||
uint16_t level = *pc++;
|
||||
uint16_t v = *pc++;
|
||||
jv* var = frame_local_var(jq, v, level);
|
||||
if (jq->debug_trace_enabled) {
|
||||
printf("V%d = ", v);
|
||||
jv_dump(jv_copy(*var), 0);
|
||||
printf("\n");
|
||||
}
|
||||
jv_free(stack_pop(jq));
|
||||
stack_push(jq, jv_copy(*var));
|
||||
break;
|
||||
}
|
||||
|
||||
// Does a load but replaces the variable with null
|
||||
case LOADVN: {
|
||||
uint16_t level = *pc++;
|
||||
uint16_t v = *pc++;
|
||||
jv* var = frame_local_var(jq, v, level);
|
||||
if (jq->debug_trace_enabled) {
|
||||
printf("V%d = ", v);
|
||||
jv_dump(jv_copy(*var), 0);
|
||||
printf("\n");
|
||||
}
|
||||
jv_free(stack_pop(jq));
|
||||
stack_push(jq, *var);
|
||||
*var = jv_null();
|
||||
break;
|
||||
}
|
||||
|
||||
case STOREV: {
|
||||
uint16_t level = *pc++;
|
||||
uint16_t v = *pc++;
|
||||
jv* var = frame_local_var(jq, v, level);
|
||||
jv val = stack_pop(jq);
|
||||
if (jq->debug_trace_enabled) {
|
||||
printf("V%d = ", v);
|
||||
jv_dump(jv_copy(val), 0);
|
||||
printf("\n");
|
||||
}
|
||||
jv_free(*var);
|
||||
*var = val;
|
||||
break;
|
||||
}
|
||||
|
||||
case PATH_BEGIN: {
|
||||
jv v = stack_pop(jq);
|
||||
stack_push(jq, jq->path);
|
||||
|
||||
stack_save(jq, pc - 1, stack_get_pos(jq));
|
||||
|
||||
stack_push(jq, jv_number(jq->subexp_nest));
|
||||
stack_push(jq, v);
|
||||
|
||||
jq->path = jv_array();
|
||||
jq->subexp_nest = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case PATH_END: {
|
||||
jv v = stack_pop(jq);
|
||||
jv_free(v); // discard value, only keep path
|
||||
|
||||
int old_subexp_nest = (int)jv_number_value(stack_pop(jq));
|
||||
|
||||
jv path = jq->path;
|
||||
jq->path = stack_pop(jq);
|
||||
|
||||
struct stack_pos spos = stack_get_pos(jq);
|
||||
stack_push(jq, jv_copy(path));
|
||||
stack_save(jq, pc - 1, spos);
|
||||
|
||||
stack_push(jq, path);
|
||||
jq->subexp_nest = old_subexp_nest;
|
||||
break;
|
||||
}
|
||||
|
||||
case ON_BACKTRACK(PATH_BEGIN):
|
||||
case ON_BACKTRACK(PATH_END): {
|
||||
jv_free(jq->path);
|
||||
jq->path = stack_pop(jq);
|
||||
goto do_backtrack;
|
||||
}
|
||||
|
||||
case INDEX: {
|
||||
jv t = stack_pop(jq);
|
||||
jv k = stack_pop(jq);
|
||||
path_append(jq, jv_copy(k));
|
||||
jv v = jv_get(t, k);
|
||||
if (jv_is_valid(v)) {
|
||||
stack_push(jq, v);
|
||||
} else {
|
||||
print_error(v);
|
||||
goto do_backtrack;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case JUMP: {
|
||||
uint16_t offset = *pc++;
|
||||
pc += offset;
|
||||
break;
|
||||
}
|
||||
|
||||
case JUMP_F: {
|
||||
uint16_t offset = *pc++;
|
||||
jv t = stack_pop(jq);
|
||||
jv_kind kind = jv_get_kind(t);
|
||||
if (kind == JV_KIND_FALSE || kind == JV_KIND_NULL) {
|
||||
pc += offset;
|
||||
}
|
||||
stack_push(jq, t); // FIXME do this better
|
||||
break;
|
||||
}
|
||||
|
||||
case EACH:
|
||||
stack_push(jq, jv_number(-1));
|
||||
// fallthrough
|
||||
case ON_BACKTRACK(EACH): {
|
||||
int idx = jv_number_value(stack_pop(jq));
|
||||
jv container = stack_pop(jq);
|
||||
|
||||
int keep_going, is_last = 0;
|
||||
jv key, value;
|
||||
if (jv_get_kind(container) == JV_KIND_ARRAY) {
|
||||
if (opcode == EACH) idx = 0;
|
||||
else idx = idx + 1;
|
||||
int len = jv_array_length(jv_copy(container));
|
||||
keep_going = idx < len;
|
||||
is_last = idx == len - 1;
|
||||
if (keep_going) {
|
||||
key = jv_number(idx);
|
||||
value = jv_array_get(jv_copy(container), idx);
|
||||
}
|
||||
} else if (jv_get_kind(container) == JV_KIND_OBJECT) {
|
||||
if (opcode == EACH) idx = jv_object_iter(container);
|
||||
else idx = jv_object_iter_next(container, idx);
|
||||
keep_going = jv_object_iter_valid(container, idx);
|
||||
if (keep_going) {
|
||||
key = jv_object_iter_key(container, idx);
|
||||
value = jv_object_iter_value(container, idx);
|
||||
}
|
||||
} else {
|
||||
assert(opcode == EACH);
|
||||
print_error(jv_invalid_with_msg(jv_string_fmt("Cannot iterate over %s",
|
||||
jv_kind_name(jv_get_kind(container)))));
|
||||
keep_going = 0;
|
||||
}
|
||||
|
||||
if (!keep_going) {
|
||||
jv_free(container);
|
||||
goto do_backtrack;
|
||||
} else if (is_last) {
|
||||
// we don't need to make a backtrack point
|
||||
jv_free(container);
|
||||
path_append(jq, key);
|
||||
stack_push(jq, value);
|
||||
} else {
|
||||
struct stack_pos spos = stack_get_pos(jq);
|
||||
stack_push(jq, container);
|
||||
stack_push(jq, jv_number(idx));
|
||||
stack_save(jq, pc - 1, spos);
|
||||
path_append(jq, key);
|
||||
stack_push(jq, value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
do_backtrack:
|
||||
case BACKTRACK: {
|
||||
pc = stack_restore(jq);
|
||||
if (!pc) {
|
||||
return jv_invalid();
|
||||
}
|
||||
backtracking = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
case FORK: {
|
||||
stack_save(jq, pc - 1, stack_get_pos(jq));
|
||||
pc++; // skip offset this time
|
||||
break;
|
||||
}
|
||||
|
||||
case ON_BACKTRACK(FORK): {
|
||||
uint16_t offset = *pc++;
|
||||
pc += offset;
|
||||
break;
|
||||
}
|
||||
|
||||
case CALL_BUILTIN: {
|
||||
int nargs = *pc++;
|
||||
jv top = stack_pop(jq);
|
||||
jv* in = cfunc_input;
|
||||
in[0] = top;
|
||||
for (int i = 1; i < nargs; i++) {
|
||||
in[i] = stack_pop(jq);
|
||||
}
|
||||
struct cfunction* function = &frame_current(jq)->bc->globals->cfunctions[*pc++];
|
||||
typedef jv (*func_1)(jv);
|
||||
typedef jv (*func_2)(jv,jv);
|
||||
typedef jv (*func_3)(jv,jv,jv);
|
||||
typedef jv (*func_4)(jv,jv,jv,jv);
|
||||
typedef jv (*func_5)(jv,jv,jv,jv,jv);
|
||||
switch (function->nargs) {
|
||||
case 1: top = ((func_1)function->fptr)(in[0]); break;
|
||||
case 2: top = ((func_2)function->fptr)(in[0], in[1]); break;
|
||||
case 3: top = ((func_3)function->fptr)(in[0], in[1], in[2]); break;
|
||||
case 4: top = ((func_4)function->fptr)(in[0], in[1], in[2], in[3]); break;
|
||||
case 5: top = ((func_5)function->fptr)(in[0], in[1], in[2], in[3], in[4]); break;
|
||||
default: return jv_invalid_with_msg(jv_string("Function takes too many arguments"));
|
||||
}
|
||||
|
||||
if (jv_is_valid(top)) {
|
||||
stack_push(jq, top);
|
||||
} else {
|
||||
print_error(top);
|
||||
goto do_backtrack;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case CALL_JQ: {
|
||||
jv input = stack_pop(jq);
|
||||
uint16_t nclosures = *pc++;
|
||||
uint16_t* retaddr = pc + 2 + nclosures*2;
|
||||
struct frame* new_frame = frame_push(jq, make_closure(jq, pc),
|
||||
pc + 2, nclosures);
|
||||
new_frame->retdata = jq->stk_top;
|
||||
new_frame->retaddr = retaddr;
|
||||
pc = new_frame->bc->code;
|
||||
stack_push(jq, input);
|
||||
break;
|
||||
}
|
||||
|
||||
case RET: {
|
||||
jv value = stack_pop(jq);
|
||||
assert(jq->stk_top == frame_current(jq)->retdata);
|
||||
uint16_t* retaddr = frame_current(jq)->retaddr;
|
||||
if (retaddr) {
|
||||
// function return
|
||||
pc = retaddr;
|
||||
frame_pop(jq);
|
||||
} else {
|
||||
// top-level return, yielding value
|
||||
struct stack_pos spos = stack_get_pos(jq);
|
||||
stack_push(jq, jv_null());
|
||||
stack_save(jq, pc - 1, spos);
|
||||
return value;
|
||||
}
|
||||
stack_push(jq, value);
|
||||
break;
|
||||
}
|
||||
case ON_BACKTRACK(RET): {
|
||||
// resumed after top-level return
|
||||
goto do_backtrack;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jq_state *jq_init(void) {
|
||||
jq_state *jq;
|
||||
jq = jv_mem_alloc_unguarded(sizeof(*jq));
|
||||
if (jq == NULL)
|
||||
return NULL;
|
||||
|
||||
jq->bc = 0;
|
||||
|
||||
stack_init(&jq->stk);
|
||||
jq->stk_top = 0;
|
||||
jq->fork_top = 0;
|
||||
jq->curr_frame = 0;
|
||||
|
||||
jq->path = jv_null();
|
||||
return jq;
|
||||
}
|
||||
|
||||
void jq_set_nomem_handler(jq_state *jq, void (*nomem_handler)(void *), void *data) {
|
||||
jv_nomem_handler(nomem_handler, data);
|
||||
jq->nomem_handler = nomem_handler;
|
||||
jq->nomem_handler_data = data;
|
||||
}
|
||||
|
||||
|
||||
void jq_start(jq_state *jq, jv input, int flags) {
|
||||
jv_nomem_handler(jq->nomem_handler, jq->nomem_handler_data);
|
||||
jq_reset(jq);
|
||||
|
||||
struct closure top = {jq->bc, -1};
|
||||
struct frame* top_frame = frame_push(jq, top, 0, 0);
|
||||
top_frame->retdata = 0;
|
||||
top_frame->retaddr = 0;
|
||||
|
||||
stack_push(jq, input);
|
||||
stack_save(jq, jq->bc->code, stack_get_pos(jq));
|
||||
if (flags & JQ_DEBUG_TRACE) {
|
||||
jq->debug_trace_enabled = 1;
|
||||
} else {
|
||||
jq->debug_trace_enabled = 0;
|
||||
}
|
||||
jq->initial_execution = 1;
|
||||
}
|
||||
|
||||
void jq_teardown(jq_state **jq) {
|
||||
jq_state *old_jq = *jq;
|
||||
if (old_jq == NULL)
|
||||
return;
|
||||
*jq = NULL;
|
||||
|
||||
jq_reset(old_jq);
|
||||
bytecode_free(old_jq->bc);
|
||||
old_jq->bc = 0;
|
||||
|
||||
jv_mem_free(old_jq);
|
||||
}
|
||||
|
||||
int jq_compile_args(jq_state *jq, const char* str, jv args) {
|
||||
jv_nomem_handler(jq->nomem_handler, jq->nomem_handler_data);
|
||||
assert(jv_get_kind(args) == JV_KIND_ARRAY);
|
||||
struct locfile locations;
|
||||
locfile_init(&locations, str, strlen(str));
|
||||
block program;
|
||||
jq_reset(jq);
|
||||
if (jq->bc) {
|
||||
bytecode_free(jq->bc);
|
||||
jq->bc = 0;
|
||||
}
|
||||
int nerrors = jq_parse(&locations, &program);
|
||||
if (nerrors == 0) {
|
||||
for (int i=0; i<jv_array_length(jv_copy(args)); i++) {
|
||||
jv arg = jv_array_get(jv_copy(args), i);
|
||||
jv name = jv_object_get(jv_copy(arg), jv_string("name"));
|
||||
jv value = jv_object_get(arg, jv_string("value"));
|
||||
program = gen_var_binding(gen_const(value), jv_string_value(name), program);
|
||||
jv_free(name);
|
||||
}
|
||||
jv_free(args);
|
||||
nerrors = builtins_bind(&program);
|
||||
if (nerrors == 0) {
|
||||
nerrors = block_compile(program, &locations, &jq->bc);
|
||||
}
|
||||
}
|
||||
if (nerrors) {
|
||||
fprintf(stderr, "%d compile %s\n", nerrors, nerrors > 1 ? "errors" : "error");
|
||||
}
|
||||
locfile_free(&locations);
|
||||
return jq->bc != NULL;
|
||||
}
|
||||
|
||||
int jq_compile(jq_state *jq, const char* str) {
|
||||
return jq_compile_args(jq, str, jv_array());
|
||||
}
|
||||
|
||||
void jq_dump_disassembly(jq_state *jq, int indent) {
|
||||
dump_disassembly(indent, jq->bc);
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
#ifndef EXECUTE_H
|
||||
#define EXECUTE_H
|
||||
#include "bytecode.h"
|
||||
|
||||
typedef struct jq_state jq_state;
|
||||
|
||||
int jq_compile(jq_state *, const char* str);
|
||||
|
||||
/* args must be an array of the form [{name:"foo", value:"thing"}, {name:"bar",value:3}] */
|
||||
int jq_compile_args(jq_state *, const char* str, jv args);
|
||||
|
||||
enum {JQ_DEBUG_TRACE = 1};
|
||||
|
||||
jq_state *jq_init(void);
|
||||
void jq_set_nomem_handler(jq_state *, void (*)(void *), void *);
|
||||
void jq_start(jq_state *, jv value, int flags);
|
||||
jv jq_next(jq_state *);
|
||||
void jq_teardown(jq_state **);
|
||||
void jq_dump_disassembly(jq_state *, int);
|
||||
|
||||
#endif
|
||||
@@ -1,32 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
# This program was used to generate jv_utf8_tables.gen.h
|
||||
|
||||
mask = lambda n: (1 << n) - 1
|
||||
|
||||
|
||||
def print_table(type, name, t):
|
||||
assert len(t) == 256
|
||||
print("static const %s %s[] =" % (type, name))
|
||||
first = True
|
||||
for i in range(0,len(t),16):
|
||||
print ((" {" if i == 0 else " ") +
|
||||
", ".join("0x%02x"%n for n in t[i:i+16]) +
|
||||
("," if i + 16 < 256 else "};"))
|
||||
|
||||
|
||||
def utf8info(c):
|
||||
if c < 0x80: return 1, mask(7)
|
||||
if 0x80 <= c <= 0xBF: return 255, mask(6)
|
||||
if 0xC0 <= c <= 0xC1: return 0, 0
|
||||
if 0xC2 <= c <= 0xDF: return 2, mask(5)
|
||||
if 0xE0 <= c <= 0xEF: return 3, mask(4)
|
||||
if 0xF0 <= c <= 0xF4: return 4, mask(3)
|
||||
if 0xF4 <= c <= 0xFF: return 0, 0
|
||||
|
||||
table = lambda i: [utf8info(c)[i] for c in range(256)]
|
||||
|
||||
print("#define UTF8_CONTINUATION_BYTE ((unsigned char)255)")
|
||||
|
||||
print_table("unsigned char", "utf8_coding_length", table(0))
|
||||
print_table("unsigned char", "utf8_coding_bits", table(1))
|
||||
@@ -1,39 +0,0 @@
|
||||
.\" generated with Ronn/v0.7.3
|
||||
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
||||
.
|
||||
.TH "JQ" "1" "May 2013" "" ""
|
||||
.
|
||||
.SH "NAME"
|
||||
\fBjq\fR \- Command\-line JSON processor
|
||||
.
|
||||
.SH "DESCRIPTION"
|
||||
\fBjq\fR can transform JSON in various ways, by selecting, iterating, reducing and otherwise mangling JSON documents\.
|
||||
.
|
||||
.P
|
||||
This version of \fBjq\fR was built without a manual, so this manpage is a stub\. For full documentation of the \fBjq\fR language, see:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
http://stedolan\.github\.com/jq
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SH "BUGS"
|
||||
Presumably\. Report them or discuss them at:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
https://github\.com/stedolan/jq/issues
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SH "AUTHOR"
|
||||
Stephen Dolan \fB<mu@netsoc\.tcd\.ie>\fR
|
||||
@@ -1,65 +0,0 @@
|
||||
# This is spec file maintained by developers of JQ, not by a OS distro.
|
||||
# Your OS of choice will likely ignore this RPM spec file.
|
||||
Summary: Command-line JSON processor
|
||||
Name: jq
|
||||
Version: %{myver}
|
||||
Release: %{myrel}%{?dist}
|
||||
Source0: jq-%{myver}.tar.gz
|
||||
URL: https://github.com/stedolan/jq
|
||||
License: BSD
|
||||
AutoReqProv: no
|
||||
#BuildPrereq: autoconf, libtool, automake, flex, bison, python
|
||||
|
||||
Group: Applications/System
|
||||
# Requires:
|
||||
|
||||
# Disables debug packages and stripping of binaries:
|
||||
%global _enable_debug_package 0
|
||||
%global debug_package %{nil}
|
||||
%global __os_install_post %{nil}
|
||||
|
||||
# Crank up the compression
|
||||
%define _binary_payload w7.lzdio
|
||||
|
||||
%description
|
||||
jq is a command-line JSON processor
|
||||
|
||||
%prep
|
||||
%setup
|
||||
|
||||
%build
|
||||
echo "Building in: \"$(pwd)\""
|
||||
%if "%{devbuild}" == "yes"
|
||||
./configure --prefix=%{_prefix} --enable-devel
|
||||
%else
|
||||
./configure --prefix=%{_prefix}
|
||||
%endif
|
||||
make
|
||||
|
||||
%install
|
||||
echo "Installing to: \"%{buildroot}\""
|
||||
make install DESTDIR=%{buildroot}
|
||||
|
||||
%clean
|
||||
rm -rf %{buildroot}
|
||||
|
||||
%files
|
||||
%defattr(-,root,root)
|
||||
%{_bindir}/jq
|
||||
%if "%{devbuild}" == "yes"
|
||||
%{_libexecdir}/%{name}/jq_test
|
||||
%{_libexecdir}/%{name}/testdata
|
||||
%endif
|
||||
%{_datadir}/doc/%{name}/AUTHORS
|
||||
%{_datadir}/doc/%{name}/COPYING
|
||||
%{_datadir}/doc/%{name}/INSTALL
|
||||
%{_datadir}/doc/%{name}/NEWS
|
||||
%{_datadir}/doc/%{name}/README
|
||||
%{_datadir}/doc/%{name}/README.md
|
||||
%{_datadir}/man/man1/jq.1
|
||||
|
||||
%changelog
|
||||
|
||||
%pre
|
||||
|
||||
%post
|
||||
@@ -1,9 +0,0 @@
|
||||
#ifndef JQ_PARSER_H
|
||||
#define JQ_PARSER_H
|
||||
#include "locfile.h"
|
||||
#include "compile.h"
|
||||
|
||||
int jq_parse(struct locfile* source, block* answer);
|
||||
int jq_parse_library(struct locfile* locations, block* answer);
|
||||
|
||||
#endif
|
||||
@@ -1,251 +0,0 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "jv.h"
|
||||
#include "execute.h"
|
||||
|
||||
static void jv_test();
|
||||
static void run_jq_tests();
|
||||
|
||||
|
||||
int jq_testsuite(int argc, char* argv[]) {
|
||||
FILE *testdata = stdin;
|
||||
jv_test();
|
||||
if (argc > 2) {
|
||||
testdata = fopen(argv[2], "r");
|
||||
if (!testdata) {
|
||||
perror("fopen");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
run_jq_tests(testdata);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int skipline(const char* buf) {
|
||||
int p = 0;
|
||||
while (buf[p] == ' ' || buf[p] == '\t') p++;
|
||||
if (buf[p] == '#' || buf[p] == '\n' || buf[p] == 0) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void run_jq_tests(FILE *testdata) {
|
||||
char buf[4096];
|
||||
int tests = 0, passed = 0, invalid = 0;
|
||||
jq_state *jq = NULL;
|
||||
|
||||
jq = jq_init();
|
||||
assert(jq);
|
||||
|
||||
while (1) {
|
||||
if (!fgets(buf, sizeof(buf), testdata)) break;
|
||||
if (skipline(buf)) continue;
|
||||
if (buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = 0;
|
||||
printf("Testing %s\n", buf);
|
||||
int pass = 1;
|
||||
tests++;
|
||||
int compiled = jq_compile(jq, buf);
|
||||
if (!compiled) {invalid++; continue;}
|
||||
printf("Disassembly:\n");
|
||||
jq_dump_disassembly(jq, 2);
|
||||
printf("\n");
|
||||
if (!fgets(buf, sizeof(buf), testdata)) { invalid++; break; }
|
||||
jv input = jv_parse(buf);
|
||||
if (!jv_is_valid(input)){ invalid++; continue; }
|
||||
jq_start(jq, input, JQ_DEBUG_TRACE);
|
||||
|
||||
while (fgets(buf, sizeof(buf), testdata)) {
|
||||
if (skipline(buf)) break;
|
||||
jv expected = jv_parse(buf);
|
||||
if (!jv_is_valid(expected)){ invalid++; continue; }
|
||||
jv actual = jq_next(jq);
|
||||
if (!jv_is_valid(actual)) {
|
||||
jv_free(actual);
|
||||
printf("*** Insufficient results\n");
|
||||
pass = 0;
|
||||
break;
|
||||
} else if (!jv_equal(jv_copy(expected), jv_copy(actual))) {
|
||||
printf("*** Expected ");
|
||||
jv_dump(jv_copy(expected), 0);
|
||||
printf(", but got ");
|
||||
jv_dump(jv_copy(actual), 0);
|
||||
printf("\n");
|
||||
pass = 0;
|
||||
}
|
||||
jv as_string = jv_dump_string(jv_copy(expected), rand() & ~JV_PRINT_COLOUR);
|
||||
jv reparsed = jv_parse_sized(jv_string_value(as_string), jv_string_length_bytes(jv_copy(as_string)));
|
||||
assert(jv_equal(jv_copy(expected), jv_copy(reparsed)));
|
||||
jv_free(as_string);
|
||||
jv_free(reparsed);
|
||||
jv_free(expected);
|
||||
jv_free(actual);
|
||||
}
|
||||
if (pass) {
|
||||
jv extra = jq_next(jq);
|
||||
if (jv_is_valid(extra)) {
|
||||
printf("*** Superfluous result: ");
|
||||
jv_dump(extra, 0);
|
||||
printf("\n");
|
||||
pass = 0;
|
||||
} else {
|
||||
jv_free(extra);
|
||||
}
|
||||
}
|
||||
passed+=pass;
|
||||
}
|
||||
jq_teardown(&jq);
|
||||
printf("%d of %d tests passed (%d malformed)\n", passed,tests,invalid);
|
||||
if (passed != tests) exit(1);
|
||||
}
|
||||
|
||||
|
||||
static void jv_test() {
|
||||
/// Arrays and numbers
|
||||
{
|
||||
jv a = jv_array();
|
||||
assert(jv_get_kind(a) == JV_KIND_ARRAY);
|
||||
assert(jv_array_length(jv_copy(a)) == 0);
|
||||
assert(jv_array_length(jv_copy(a)) == 0);
|
||||
|
||||
a = jv_array_append(a, jv_number(42));
|
||||
assert(jv_array_length(jv_copy(a)) == 1);
|
||||
assert(jv_number_value(jv_array_get(jv_copy(a), 0)) == 42);
|
||||
|
||||
jv a2 = jv_array_append(jv_array(), jv_number(42));
|
||||
assert(jv_equal(jv_copy(a), jv_copy(a)));
|
||||
assert(jv_equal(jv_copy(a2), jv_copy(a2)));
|
||||
assert(jv_equal(jv_copy(a), jv_copy(a2)));
|
||||
assert(jv_equal(jv_copy(a2), jv_copy(a)));
|
||||
jv_free(a2);
|
||||
|
||||
a2 = jv_array_append(jv_array(), jv_number(19));
|
||||
assert(!jv_equal(jv_copy(a), jv_copy(a2)));
|
||||
assert(!jv_equal(jv_copy(a2), jv_copy(a)));
|
||||
jv_free(a2);
|
||||
|
||||
|
||||
assert(a.val.nontrivial.ptr->count == 1);
|
||||
a = jv_array_append(a, jv_copy(a));
|
||||
assert(a.val.nontrivial.ptr->count == 1);
|
||||
|
||||
assert(jv_array_length(jv_copy(a)) == 2);
|
||||
assert(jv_number_value(jv_array_get(jv_copy(a), 0)) == 42);
|
||||
|
||||
for (int i=0; i<10; i++) {
|
||||
jv subarray = jv_array_get(jv_copy(a), 1);
|
||||
assert(jv_get_kind(subarray) == JV_KIND_ARRAY);
|
||||
assert(jv_array_length(jv_copy(subarray)) == 1);
|
||||
assert(jv_number_value(jv_array_get(jv_copy(subarray), 0)) == 42);
|
||||
jv_free(subarray);
|
||||
}
|
||||
|
||||
|
||||
jv subarray = jv_array_get(jv_copy(a), 1);
|
||||
assert(jv_get_kind(subarray) == JV_KIND_ARRAY);
|
||||
assert(jv_array_length(jv_copy(subarray)) == 1);
|
||||
assert(jv_number_value(jv_array_get(jv_copy(subarray), 0)) == 42);
|
||||
|
||||
jv sub2 = jv_copy(subarray);
|
||||
sub2 = jv_array_append(sub2, jv_number(19));
|
||||
|
||||
assert(jv_get_kind(sub2) == JV_KIND_ARRAY);
|
||||
assert(jv_array_length(jv_copy(sub2)) == 2);
|
||||
assert(jv_number_value(jv_array_get(jv_copy(sub2), 0)) == 42);
|
||||
assert(jv_number_value(jv_array_get(jv_copy(sub2), 1)) == 19);
|
||||
|
||||
assert(jv_get_kind(subarray) == JV_KIND_ARRAY);
|
||||
assert(jv_array_length(jv_copy(subarray)) == 1);
|
||||
assert(jv_number_value(jv_array_get(jv_copy(subarray), 0)) == 42);
|
||||
|
||||
jv_free(subarray);
|
||||
|
||||
void* before = sub2.val.nontrivial.ptr;
|
||||
sub2 = jv_array_append(sub2, jv_number(200));
|
||||
void* after = sub2.val.nontrivial.ptr;
|
||||
assert(before == after);
|
||||
jv_free(sub2);
|
||||
|
||||
jv a3 = jv_array_append(jv_copy(a), jv_number(19));
|
||||
assert(jv_array_length(jv_copy(a3)) == 3);
|
||||
assert(jv_number_value(jv_array_get(jv_copy(a3), 0)) == 42);
|
||||
assert(jv_array_length(jv_array_get(jv_copy(a3), 1)) == 1);
|
||||
assert(jv_number_value(jv_array_get(jv_copy(a3), 2)) == 19);
|
||||
jv_free(a3);
|
||||
|
||||
|
||||
jv a4 = jv_array();
|
||||
a4 = jv_array_append(a4, jv_number(1));
|
||||
a4 = jv_array_append(a4, jv_number(2));
|
||||
jv a5 = jv_copy(a4);
|
||||
a4 = jv_array_append(a4, jv_number(3));
|
||||
a4 = jv_array_slice(a4, 0, 1);
|
||||
assert(jv_array_length(jv_copy(a4)) == 1);
|
||||
a4 = jv_array_append(a4, jv_number(4));
|
||||
assert(jv_array_length(a4) == 2);
|
||||
assert(jv_array_length(a5) == 2);
|
||||
|
||||
|
||||
assert(jv_array_length(jv_copy(a)) == 2);
|
||||
assert(jv_number_value(jv_array_get(jv_copy(a), 0)) == 42);
|
||||
assert(jv_array_length(jv_array_get(jv_copy(a), 1)) == 1);
|
||||
|
||||
|
||||
jv_dump(jv_copy(a), 0); printf("\n");
|
||||
jv_free(a);
|
||||
}
|
||||
|
||||
|
||||
/// Strings
|
||||
{
|
||||
assert(jv_equal(jv_string("foo"), jv_string_sized("foo", 3)));
|
||||
char nasty[] = "foo\0";
|
||||
jv shortstr = jv_string(nasty), longstr = jv_string_sized(nasty, sizeof(nasty));
|
||||
assert(jv_string_length_bytes(shortstr) == (int)strlen(nasty));
|
||||
assert(jv_string_length_bytes(longstr) == (int)sizeof(nasty));
|
||||
|
||||
|
||||
char a1s[] = "hello", a2s[] = "hello", bs[] = "goodbye";
|
||||
jv a1 = jv_string(a1s), a2 = jv_string(a2s), b = jv_string(bs);
|
||||
assert(jv_equal(jv_copy(a1), jv_copy(a2)));
|
||||
assert(jv_equal(jv_copy(a2), jv_copy(a1)));
|
||||
assert(!jv_equal(jv_copy(a1), jv_copy(b)));
|
||||
|
||||
assert(jv_string_hash(jv_copy(a1)) == jv_string_hash(jv_copy(a1)));
|
||||
assert(jv_string_hash(jv_copy(a1)) == jv_string_hash(jv_copy(a2)));
|
||||
assert(jv_string_hash(jv_copy(b)) != jv_string_hash(jv_copy(a1)));
|
||||
jv_free(a1);
|
||||
jv_free(a2);
|
||||
jv_free(b);
|
||||
|
||||
assert(jv_equal(jv_string("hello42!"), jv_string_fmt("hello%d%s", 42, "!")));
|
||||
char big[20000];
|
||||
for (int i=0; i<(int)sizeof(big); i++) big[i] = 'a';
|
||||
big[sizeof(big)-1] = 0;
|
||||
jv str = jv_string_fmt("%s", big);
|
||||
assert(jv_string_length_bytes(jv_copy(str)) == sizeof(big) - 1);
|
||||
assert(!strcmp(big, jv_string_value(str)));
|
||||
jv_free(str);
|
||||
}
|
||||
|
||||
/// Objects
|
||||
{
|
||||
jv o1 = jv_object();
|
||||
o1 = jv_object_set(o1, jv_string("foo"), jv_number(42));
|
||||
o1 = jv_object_set(o1, jv_string("bar"), jv_number(24));
|
||||
assert(jv_number_value(jv_object_get(jv_copy(o1), jv_string("foo"))) == 42);
|
||||
assert(jv_number_value(jv_object_get(jv_copy(o1), jv_string("bar"))) == 24);
|
||||
|
||||
jv o2 = jv_object_set(jv_copy(o1), jv_string("foo"), jv_number(420));
|
||||
o2 = jv_object_set(o2, jv_string("bar"), jv_number(240));
|
||||
assert(jv_number_value(jv_object_get(jv_copy(o1), jv_string("foo"))) == 42);
|
||||
assert(jv_number_value(jv_object_get(jv_copy(o1), jv_string("bar"))) == 24);
|
||||
assert(jv_number_value(jv_object_get(jv_copy(o2), jv_string("foo"))) == 420);
|
||||
jv_free(o1);
|
||||
assert(jv_number_value(jv_object_get(jv_copy(o2), jv_string("bar"))) == 240);
|
||||
|
||||
jv_dump(jv_copy(o2), 0); printf("\n");
|
||||
|
||||
jv_free(o2);
|
||||
}
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
#ifndef JV_H
|
||||
#define JV_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
|
||||
typedef enum {
|
||||
JV_KIND_INVALID,
|
||||
JV_KIND_NULL,
|
||||
JV_KIND_FALSE,
|
||||
JV_KIND_TRUE,
|
||||
JV_KIND_NUMBER,
|
||||
JV_KIND_STRING,
|
||||
JV_KIND_ARRAY,
|
||||
JV_KIND_OBJECT
|
||||
} jv_kind;
|
||||
|
||||
typedef struct {
|
||||
size_t count;
|
||||
} jv_refcnt;
|
||||
|
||||
typedef struct{
|
||||
jv_refcnt* ptr;
|
||||
int i[2];
|
||||
} jv_nontrivial;
|
||||
|
||||
typedef struct {
|
||||
jv_kind kind;
|
||||
union {
|
||||
double number;
|
||||
jv_nontrivial nontrivial;
|
||||
} val;
|
||||
} jv;
|
||||
|
||||
/*
|
||||
* All jv_* functions consume (decref) input and produce (incref) output
|
||||
* Except jv_copy
|
||||
*/
|
||||
|
||||
jv_kind jv_get_kind(jv);
|
||||
const char* jv_kind_name(jv_kind);
|
||||
static int jv_is_valid(jv x) { return jv_get_kind(x) != JV_KIND_INVALID; }
|
||||
|
||||
jv jv_copy(jv);
|
||||
void jv_free(jv);
|
||||
|
||||
int jv_equal(jv, jv);
|
||||
int jv_contains(jv, jv);
|
||||
|
||||
jv jv_invalid();
|
||||
jv jv_invalid_with_msg(jv);
|
||||
jv jv_invalid_get_msg(jv);
|
||||
int jv_invalid_has_msg(jv);
|
||||
|
||||
|
||||
jv jv_null();
|
||||
jv jv_true();
|
||||
jv jv_false();
|
||||
jv jv_bool(int);
|
||||
|
||||
jv jv_number(double);
|
||||
double jv_number_value(jv);
|
||||
|
||||
jv jv_array();
|
||||
jv jv_array_sized(int);
|
||||
int jv_array_length(jv);
|
||||
jv jv_array_get(jv, int);
|
||||
jv jv_array_set(jv, int, jv);
|
||||
jv jv_array_append(jv, jv);
|
||||
jv jv_array_concat(jv, jv);
|
||||
jv jv_array_slice(jv, int, int);
|
||||
#define jv_array_foreach(a, i, x) \
|
||||
for (int jv_len__ = jv_array_length(jv_copy(a)), i=0, jv_j__ = 1; \
|
||||
jv_j__; jv_j__ = 0) \
|
||||
for (jv x; \
|
||||
i < jv_len__ ? \
|
||||
(x = jv_array_get(jv_copy(a), i), 1) : 0; \
|
||||
i++)
|
||||
|
||||
jv jv_string(const char*);
|
||||
jv jv_string_sized(const char*, int);
|
||||
int jv_string_length_bytes(jv);
|
||||
int jv_string_length_codepoints(jv);
|
||||
uint32_t jv_string_hash(jv);
|
||||
const char* jv_string_value(jv);
|
||||
jv jv_string_concat(jv, jv);
|
||||
jv jv_string_fmt(const char*, ...);
|
||||
jv jv_string_append_buf(jv a, const char* buf, int len);
|
||||
jv jv_string_append_str(jv a, const char* str);
|
||||
|
||||
jv jv_object();
|
||||
jv jv_object_get(jv object, jv key);
|
||||
jv jv_object_set(jv object, jv key, jv value);
|
||||
jv jv_object_delete(jv object, jv key);
|
||||
int jv_object_length(jv object);
|
||||
jv jv_object_merge(jv, jv);
|
||||
|
||||
int jv_object_iter(jv);
|
||||
int jv_object_iter_next(jv, int);
|
||||
int jv_object_iter_valid(jv, int);
|
||||
jv jv_object_iter_key(jv, int);
|
||||
jv jv_object_iter_value(jv, int);
|
||||
#define jv_object_foreach(t, k, v) \
|
||||
for (int jv_i__ = jv_object_iter(t), jv_j__ = 1; jv_j__; jv_j__ = 0) \
|
||||
for (jv k, v; \
|
||||
jv_object_iter_valid((t), jv_i__) ? \
|
||||
(k = jv_object_iter_key(t, jv_i__), \
|
||||
v = jv_object_iter_value(t, jv_i__), \
|
||||
1) \
|
||||
: 0; \
|
||||
jv_i__ = jv_object_iter_next(t, jv_i__)) \
|
||||
|
||||
|
||||
|
||||
int jv_get_refcnt(jv);
|
||||
|
||||
enum { JV_PRINT_PRETTY = 1, JV_PRINT_ASCII = 2, JV_PRINT_COLOUR = 4, JV_PRINT_SORTED = 8 };
|
||||
void jv_dump(jv, int flags);
|
||||
jv jv_dump_string(jv, int flags);
|
||||
|
||||
jv jv_parse(const char* string);
|
||||
jv jv_parse_sized(const char* string, int length);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
|
||||
true/false/null:
|
||||
check kind
|
||||
|
||||
number:
|
||||
introduce/eliminate jv
|
||||
to integer
|
||||
|
||||
array:
|
||||
copy
|
||||
free
|
||||
slice
|
||||
index
|
||||
update
|
||||
|
||||
updateslice?
|
||||
|
||||
|
||||
*/
|
||||
-119
@@ -1,119 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "jv_alloc.h"
|
||||
|
||||
struct nomem_handler {
|
||||
jv_nomem_handler_f handler;
|
||||
void *data;
|
||||
};
|
||||
|
||||
#ifndef USE_PTHREAD_KEY
|
||||
#ifdef _MSC_VER
|
||||
static __declspec(thread) struct nomem_handler nomem_handler;
|
||||
#else
|
||||
static __thread struct nomem_handler nomem_handler;
|
||||
#endif
|
||||
|
||||
void jv_nomem_handler(jv_nomem_handler_f handler, void *data) {
|
||||
nomem_handler.handler = handler;
|
||||
}
|
||||
|
||||
static void memory_exhausted() {
|
||||
if (nomem_handler.handler)
|
||||
nomem_handler.handler(nomem_handler.data); // Maybe handler() will longjmp() to safety
|
||||
// Or not
|
||||
fprintf(stderr, "error: cannot allocate memory\n");
|
||||
abort();
|
||||
}
|
||||
#else
|
||||
#ifdef HAVE_PTHREAD_KEY_CREATE
|
||||
#include <pthread.h>
|
||||
|
||||
pthread_key_t nomem_handler_key;
|
||||
pthread_once_t mem_once = PTHREAD_ONCE_INIT;
|
||||
|
||||
static void tsd_init(void) {
|
||||
if (pthread_key_create(&nomem_handler_key, NULL) != 0) {
|
||||
fprintf(stderr, "error: cannot create thread specific key");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
void jv_nomem_handler(jv_nomem_handler_f handler, void *data) {
|
||||
pthread_once(&mem_once, tsd_init); // cannot fail
|
||||
struct nomem_handler *nomem_handler = calloc(1, sizeof (nomem_handler));
|
||||
if (nomem_handler == NULL) {
|
||||
handler(data);
|
||||
fprintf(stderr, "error: cannot allocate memory\n");
|
||||
abort();
|
||||
}
|
||||
nomem_handler.handler = handler;
|
||||
nomem_handler.data = data;
|
||||
if (pthread_setspecific(nomem_handler_key, nomem_handler) != 0) {
|
||||
handler(data);
|
||||
fprintf(stderr, "error: cannot set thread specific data");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void memory_exhausted() {
|
||||
jv_nomem_handler_f handler;
|
||||
|
||||
pthread_once(&mem_once, tsd_init);
|
||||
handler = pthread_getspecific(nomem_handler_key);
|
||||
if (handler)
|
||||
handler(); // Maybe handler() will longjmp() to safety
|
||||
// Or not
|
||||
fprintf(stderr, "error: cannot allocate memory\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static struct nomem_handler nomem_handler;
|
||||
void jv_nomem_handler(jv_nomem_handler_f handler, void *data) {
|
||||
nomem_handler.handler = handler;
|
||||
nomem_handler.data = data;
|
||||
}
|
||||
|
||||
static void memory_exhausted() {
|
||||
fprintf(stderr, "error: cannot allocate memory\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
#endif /* HAVE_PTHREAD_KEY_CREATE */
|
||||
#endif /* !USE_PTHREAD_KEY */
|
||||
|
||||
|
||||
void* jv_mem_alloc(size_t sz) {
|
||||
void* p = malloc(sz);
|
||||
if (!p) {
|
||||
memory_exhausted();
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
void* jv_mem_alloc_unguarded(size_t sz) {
|
||||
return malloc(sz);
|
||||
}
|
||||
|
||||
void jv_mem_free(void* p) {
|
||||
free(p);
|
||||
}
|
||||
|
||||
void* jv_mem_realloc(void* p, size_t sz) {
|
||||
p = realloc(p, sz);
|
||||
if (!p) {
|
||||
memory_exhausted();
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
volatile char jv_mem_uninitialised;
|
||||
__attribute__((constructor)) void jv_mem_uninit_setup(){
|
||||
char* p = malloc(1);
|
||||
jv_mem_uninitialised = *p;
|
||||
free(p);
|
||||
}
|
||||
#endif
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
#ifndef JV_ALLOC_H
|
||||
#define JV_ALLOC_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifndef NDEBUG
|
||||
extern volatile char jv_mem_uninitialised;
|
||||
#endif
|
||||
|
||||
static void jv_mem_invalidate(void* mem, size_t n) {
|
||||
#ifndef NDEBUG
|
||||
char* m = mem;
|
||||
while (n--) *m++ ^= jv_mem_uninitialised ^ jv_mem_uninitialised;
|
||||
#endif
|
||||
}
|
||||
|
||||
typedef void (*jv_nomem_handler_f)(void *);
|
||||
void jv_nomem_handler(jv_nomem_handler_f, void *);
|
||||
void* jv_mem_alloc(size_t);
|
||||
void* jv_mem_alloc_unguarded(size_t);
|
||||
void jv_mem_free(void*);
|
||||
__attribute__((warn_unused_result)) void* jv_mem_realloc(void*, size_t);
|
||||
|
||||
#endif
|
||||
@@ -1,565 +0,0 @@
|
||||
#include "jv_aux.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "jv_alloc.h"
|
||||
|
||||
static int parse_slice(jv array, jv slice, int* pstart, int* pend) {
|
||||
// Array slices
|
||||
int len = jv_array_length(jv_copy(array));
|
||||
jv start_jv = jv_object_get(jv_copy(slice), jv_string("start"));
|
||||
jv end_jv = jv_object_get(slice, jv_string("end"));
|
||||
if (jv_get_kind(start_jv) == JV_KIND_NULL) {
|
||||
jv_free(start_jv);
|
||||
start_jv = jv_number(0);
|
||||
}
|
||||
if (jv_get_kind(end_jv) == JV_KIND_NULL) {
|
||||
jv_free(end_jv);
|
||||
end_jv = jv_number(len);
|
||||
}
|
||||
if (jv_get_kind(start_jv) != JV_KIND_NUMBER ||
|
||||
jv_get_kind(end_jv) != JV_KIND_NUMBER) {
|
||||
jv_free(start_jv);
|
||||
jv_free(end_jv);
|
||||
return 0;
|
||||
} else {
|
||||
int start = (int)jv_number_value(start_jv);
|
||||
int end = (int)jv_number_value(end_jv);
|
||||
if (start < 0) start = len + start;
|
||||
if (end < 0) end = len + end;
|
||||
|
||||
if (start < 0) start = 0;
|
||||
if (start > len) start = len;
|
||||
if (end > len) end = len;
|
||||
if (end < start) end = start;
|
||||
assert(0 <= start && start <= end && end <= len);
|
||||
*pstart = start;
|
||||
*pend = end;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
jv jv_get(jv t, jv k) {
|
||||
jv v;
|
||||
if (jv_get_kind(t) == JV_KIND_OBJECT && jv_get_kind(k) == JV_KIND_STRING) {
|
||||
v = jv_object_get(t, k);
|
||||
if (!jv_is_valid(v)) {
|
||||
jv_free(v);
|
||||
v = jv_null();
|
||||
}
|
||||
} else if (jv_get_kind(t) == JV_KIND_ARRAY && jv_get_kind(k) == JV_KIND_NUMBER) {
|
||||
// FIXME: don't do lookup for noninteger index
|
||||
v = jv_array_get(t, (int)jv_number_value(k));
|
||||
if (!jv_is_valid(v)) {
|
||||
jv_free(v);
|
||||
v = jv_null();
|
||||
}
|
||||
} else if (jv_get_kind(t) == JV_KIND_ARRAY && jv_get_kind(k) == JV_KIND_OBJECT) {
|
||||
int start, end;
|
||||
if (parse_slice(t, k, &start, &end)) {
|
||||
v = jv_array_slice(t, start, end);
|
||||
} else {
|
||||
v = jv_invalid_with_msg(jv_string_fmt("Start and end indices of an array slice must be numbers"));
|
||||
jv_free(t);
|
||||
}
|
||||
} else if (jv_get_kind(t) == JV_KIND_NULL &&
|
||||
(jv_get_kind(k) == JV_KIND_STRING ||
|
||||
jv_get_kind(k) == JV_KIND_NUMBER ||
|
||||
jv_get_kind(k) == JV_KIND_OBJECT)) {
|
||||
jv_free(t);
|
||||
jv_free(k);
|
||||
v = jv_null();
|
||||
} else {
|
||||
v = jv_invalid_with_msg(jv_string_fmt("Cannot index %s with %s",
|
||||
jv_kind_name(jv_get_kind(t)),
|
||||
jv_kind_name(jv_get_kind(k))));
|
||||
jv_free(t);
|
||||
jv_free(k);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
jv jv_set(jv t, jv k, jv v) {
|
||||
if (!jv_is_valid(v)) {
|
||||
jv_free(t);
|
||||
jv_free(k);
|
||||
return v;
|
||||
}
|
||||
int isnull = jv_get_kind(t) == JV_KIND_NULL;
|
||||
if (jv_get_kind(k) == JV_KIND_STRING &&
|
||||
(jv_get_kind(t) == JV_KIND_OBJECT || isnull)) {
|
||||
if (isnull) t = jv_object();
|
||||
t = jv_object_set(t, k, v);
|
||||
} else if (jv_get_kind(k) == JV_KIND_NUMBER &&
|
||||
(jv_get_kind(t) == JV_KIND_ARRAY || isnull)) {
|
||||
if (isnull) t = jv_array();
|
||||
t = jv_array_set(t, (int)jv_number_value(k), v);
|
||||
} else if (jv_get_kind(k) == JV_KIND_OBJECT &&
|
||||
(jv_get_kind(t) == JV_KIND_ARRAY || isnull)) {
|
||||
if (isnull) t = jv_array();
|
||||
int start, end;
|
||||
if (parse_slice(t, k, &start, &end)) {
|
||||
if (jv_get_kind(v) == JV_KIND_ARRAY) {
|
||||
int array_len = jv_array_length(jv_copy(t));
|
||||
assert(0 <= start && start <= end && end <= array_len);
|
||||
int slice_len = end - start;
|
||||
int insert_len = jv_array_length(jv_copy(v));
|
||||
if (slice_len < insert_len) {
|
||||
// array is growing
|
||||
int shift = insert_len - slice_len;
|
||||
for (int i = array_len - 1; i >= end; i--) {
|
||||
t = jv_array_set(t, i + shift, jv_array_get(jv_copy(t), i));
|
||||
}
|
||||
} else if (slice_len > insert_len) {
|
||||
// array is shrinking
|
||||
int shift = slice_len - insert_len;
|
||||
for (int i = end; i < array_len; i++) {
|
||||
t = jv_array_set(t, i - shift, jv_array_get(jv_copy(t), i));
|
||||
}
|
||||
t = jv_array_slice(t, 0, array_len - shift);
|
||||
}
|
||||
for (int i=0; i < insert_len; i++) {
|
||||
t = jv_array_set(t, start + i, jv_array_get(jv_copy(v), i));
|
||||
}
|
||||
jv_free(v);
|
||||
} else {
|
||||
jv_free(t);
|
||||
jv_free(v);
|
||||
t = jv_invalid_with_msg(jv_string_fmt("A slice of an array can only be assigned another array"));
|
||||
}
|
||||
} else {
|
||||
jv_free(t);
|
||||
jv_free(k);
|
||||
jv_free(v);
|
||||
t = jv_invalid_with_msg(jv_string_fmt("Start and end indices of an array slice must be numbers"));
|
||||
}
|
||||
} else {
|
||||
jv err = jv_invalid_with_msg(jv_string_fmt("Cannot update field at %s index of %s",
|
||||
jv_kind_name(jv_get_kind(k)),
|
||||
jv_kind_name(jv_get_kind(t))));
|
||||
jv_free(t);
|
||||
jv_free(k);
|
||||
jv_free(v);
|
||||
t = err;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
jv jv_has(jv t, jv k) {
|
||||
assert(jv_is_valid(t));
|
||||
assert(jv_is_valid(k));
|
||||
jv ret;
|
||||
if (jv_get_kind(t) == JV_KIND_NULL) {
|
||||
jv_free(t);
|
||||
jv_free(k);
|
||||
ret = jv_false();
|
||||
} else if (jv_get_kind(t) == JV_KIND_OBJECT &&
|
||||
jv_get_kind(k) == JV_KIND_STRING) {
|
||||
jv elem = jv_object_get(t, k);
|
||||
ret = jv_bool(jv_is_valid(elem));
|
||||
jv_free(elem);
|
||||
} else if (jv_get_kind(t) == JV_KIND_ARRAY &&
|
||||
jv_get_kind(k) == JV_KIND_NUMBER) {
|
||||
jv elem = jv_array_get(t, (int)jv_number_value(k));
|
||||
ret = jv_bool(jv_is_valid(elem));
|
||||
jv_free(elem);
|
||||
} else {
|
||||
ret = jv_invalid_with_msg(jv_string_fmt("Cannot check whether %s has a %s key",
|
||||
jv_kind_name(jv_get_kind(t)),
|
||||
jv_kind_name(jv_get_kind(k))));
|
||||
jv_free(t);
|
||||
jv_free(k);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// assumes keys is a sorted array
|
||||
jv jv_dels(jv t, jv keys) {
|
||||
assert(jv_get_kind(keys) == JV_KIND_ARRAY);
|
||||
assert(jv_is_valid(t));
|
||||
|
||||
if (jv_get_kind(t) == JV_KIND_NULL || jv_array_length(jv_copy(keys)) == 0) {
|
||||
// no change
|
||||
} else if (jv_get_kind(t) == JV_KIND_ARRAY) {
|
||||
// extract slices, they must be handled differently
|
||||
jv orig_keys = keys;
|
||||
keys = jv_array();
|
||||
jv new_array = jv_array();
|
||||
jv starts = jv_array(), ends = jv_array();
|
||||
jv_array_foreach(orig_keys, i, key) {
|
||||
if (jv_get_kind(key) == JV_KIND_NUMBER) {
|
||||
keys = jv_array_append(keys, key);
|
||||
} else if (jv_get_kind(key) == JV_KIND_OBJECT) {
|
||||
int start, end;
|
||||
if (parse_slice(t, key, &start, &end)) {
|
||||
starts = jv_array_append(starts, jv_number(start));
|
||||
ends = jv_array_append(ends, jv_number(end));
|
||||
} else {
|
||||
jv_free(new_array);
|
||||
jv_free(key);
|
||||
new_array = jv_invalid_with_msg(jv_string_fmt("Start and end indices of an array slice must be numbers"));
|
||||
goto arr_out;
|
||||
}
|
||||
} else {
|
||||
jv_free(new_array);
|
||||
new_array = jv_invalid_with_msg(jv_string_fmt("Cannot delete %s element of array",
|
||||
jv_kind_name(jv_get_kind(key))));
|
||||
jv_free(key);
|
||||
goto arr_out;
|
||||
}
|
||||
}
|
||||
|
||||
int kidx = 0;
|
||||
jv_array_foreach(t, i, elem) {
|
||||
int del = 0;
|
||||
while (kidx < jv_array_length(jv_copy(keys))) {
|
||||
int delidx = (int)jv_number_value(jv_array_get(jv_copy(keys), kidx));
|
||||
if (i == delidx) {
|
||||
del = 1;
|
||||
}
|
||||
if (i < delidx) {
|
||||
break;
|
||||
}
|
||||
kidx++;
|
||||
}
|
||||
for (int sidx=0; !del && sidx<jv_array_length(jv_copy(starts)); sidx++) {
|
||||
if ((int)jv_number_value(jv_array_get(jv_copy(starts), sidx)) <= i &&
|
||||
i < (int)jv_number_value(jv_array_get(jv_copy(ends), sidx))) {
|
||||
del = 1;
|
||||
}
|
||||
}
|
||||
if (!del)
|
||||
new_array = jv_array_append(new_array, elem);
|
||||
else
|
||||
jv_free(elem);
|
||||
}
|
||||
arr_out:
|
||||
jv_free(starts);
|
||||
jv_free(ends);
|
||||
jv_free(orig_keys);
|
||||
jv_free(t);
|
||||
t = new_array;
|
||||
} else if (jv_get_kind(t) == JV_KIND_OBJECT) {
|
||||
jv_array_foreach(keys, i, k) {
|
||||
if (jv_get_kind(k) != JV_KIND_STRING) {
|
||||
jv_free(t);
|
||||
t = jv_invalid_with_msg(jv_string_fmt("Cannot delete %s field of object",
|
||||
jv_kind_name(jv_get_kind(k))));
|
||||
jv_free(k);
|
||||
break;
|
||||
}
|
||||
t = jv_object_delete(t, k);
|
||||
}
|
||||
} else {
|
||||
jv err = jv_invalid_with_msg(jv_string_fmt("Cannot delete fields from %s",
|
||||
jv_kind_name(jv_get_kind(t))));
|
||||
jv_free(t);
|
||||
t = err;
|
||||
}
|
||||
jv_free(keys);
|
||||
return t;
|
||||
}
|
||||
|
||||
jv jv_setpath(jv root, jv path, jv value) {
|
||||
if (jv_get_kind(path) != JV_KIND_ARRAY) {
|
||||
jv_free(value);
|
||||
jv_free(root);
|
||||
jv_free(path);
|
||||
return jv_invalid_with_msg(jv_string("Path must be specified as an array"));
|
||||
}
|
||||
if (!jv_is_valid(root)){
|
||||
jv_free(value);
|
||||
jv_free(path);
|
||||
return root;
|
||||
}
|
||||
if (jv_array_length(jv_copy(path)) == 0) {
|
||||
jv_free(path);
|
||||
jv_free(root);
|
||||
return value;
|
||||
}
|
||||
jv pathcurr = jv_array_get(jv_copy(path), 0);
|
||||
jv pathrest = jv_array_slice(path, 1, jv_array_length(jv_copy(path)));
|
||||
return jv_set(root, pathcurr,
|
||||
jv_setpath(jv_get(jv_copy(root), jv_copy(pathcurr)), pathrest, value));
|
||||
}
|
||||
|
||||
jv jv_getpath(jv root, jv path) {
|
||||
if (jv_get_kind(path) != JV_KIND_ARRAY) {
|
||||
jv_free(root);
|
||||
jv_free(path);
|
||||
return jv_invalid_with_msg(jv_string("Path must be specified as an array"));
|
||||
}
|
||||
if (!jv_is_valid(root)) {
|
||||
jv_free(path);
|
||||
return root;
|
||||
}
|
||||
if (jv_array_length(jv_copy(path)) == 0) {
|
||||
jv_free(path);
|
||||
return root;
|
||||
}
|
||||
jv pathcurr = jv_array_get(jv_copy(path), 0);
|
||||
jv pathrest = jv_array_slice(path, 1, jv_array_length(jv_copy(path)));
|
||||
return jv_getpath(jv_get(root, pathcurr), pathrest);
|
||||
}
|
||||
|
||||
// assumes paths is a sorted array of arrays
|
||||
static jv delpaths_sorted(jv object, jv paths, int start) {
|
||||
jv delkeys = jv_array();
|
||||
for (int i=0; i<jv_array_length(jv_copy(paths));) {
|
||||
int j = i;
|
||||
assert(jv_array_length(jv_array_get(jv_copy(paths), i)) > start);
|
||||
int delkey = jv_array_length(jv_array_get(jv_copy(paths), i)) == start + 1;
|
||||
jv key = jv_array_get(jv_array_get(jv_copy(paths), i), start);
|
||||
while (j < jv_array_length(jv_copy(paths)) &&
|
||||
jv_equal(jv_copy(key), jv_array_get(jv_array_get(jv_copy(paths), j), start)))
|
||||
j++;
|
||||
// if i <= entry < j, then entry starts with key
|
||||
if (delkey) {
|
||||
// deleting this entire key, we don't care about any more specific deletions
|
||||
delkeys = jv_array_append(delkeys, key);
|
||||
} else {
|
||||
// deleting certain sub-parts of this key
|
||||
jv subobject = jv_get(jv_copy(object), jv_copy(key));
|
||||
if (!jv_is_valid(subobject)) {
|
||||
jv_free(key);
|
||||
jv_free(object);
|
||||
object = subobject;
|
||||
break;
|
||||
} else if (jv_get_kind(subobject) == JV_KIND_NULL) {
|
||||
jv_free(key);
|
||||
jv_free(subobject);
|
||||
} else {
|
||||
jv newsubobject = delpaths_sorted(subobject, jv_array_slice(jv_copy(paths), i, j), start+1);
|
||||
if (!jv_is_valid(newsubobject)) {
|
||||
jv_free(key);
|
||||
jv_free(object);
|
||||
object = newsubobject;
|
||||
break;
|
||||
}
|
||||
object = jv_set(object, key, newsubobject);
|
||||
}
|
||||
if (!jv_is_valid(object)) break;
|
||||
}
|
||||
i = j;
|
||||
}
|
||||
jv_free(paths);
|
||||
if (jv_is_valid(object))
|
||||
object = jv_dels(object, delkeys);
|
||||
else
|
||||
jv_free(delkeys);
|
||||
return object;
|
||||
}
|
||||
|
||||
jv jv_delpaths(jv object, jv paths) {
|
||||
paths = jv_sort(paths, jv_copy(paths));
|
||||
jv_array_foreach(paths, i, elem) {
|
||||
if (jv_get_kind(elem) != JV_KIND_ARRAY) {
|
||||
jv_free(object);
|
||||
jv_free(paths);
|
||||
jv err = jv_invalid_with_msg(jv_string_fmt("Path must be specified as array, not %s",
|
||||
jv_kind_name(jv_get_kind(elem))));
|
||||
jv_free(elem);
|
||||
return err;
|
||||
}
|
||||
jv_free(elem);
|
||||
}
|
||||
if (jv_array_length(jv_copy(paths)) == 0) {
|
||||
// nothing is being deleted
|
||||
jv_free(paths);
|
||||
return object;
|
||||
}
|
||||
if (jv_array_length(jv_array_get(jv_copy(paths), 0)) == 0) {
|
||||
// everything is being deleted
|
||||
jv_free(paths);
|
||||
jv_free(object);
|
||||
return jv_null();
|
||||
}
|
||||
return delpaths_sorted(object, paths, 0);
|
||||
}
|
||||
|
||||
|
||||
static int string_cmp(const void* pa, const void* pb){
|
||||
const jv* a = pa;
|
||||
const jv* b = pb;
|
||||
int lena = jv_string_length_bytes(jv_copy(*a));
|
||||
int lenb = jv_string_length_bytes(jv_copy(*b));
|
||||
int minlen = lena < lenb ? lena : lenb;
|
||||
int r = memcmp(jv_string_value(*a), jv_string_value(*b), minlen);
|
||||
if (r == 0) r = lena - lenb;
|
||||
return r;
|
||||
}
|
||||
|
||||
jv jv_keys(jv x) {
|
||||
if (jv_get_kind(x) == JV_KIND_OBJECT) {
|
||||
int nkeys = jv_object_length(jv_copy(x));
|
||||
jv* keys = jv_mem_alloc(sizeof(jv) * nkeys);
|
||||
int kidx = 0;
|
||||
jv_object_foreach(x, key, value) {
|
||||
keys[kidx++] = key;
|
||||
jv_free(value);
|
||||
}
|
||||
qsort(keys, nkeys, sizeof(jv), string_cmp);
|
||||
jv answer = jv_array_sized(nkeys);
|
||||
for (int i = 0; i<nkeys; i++) {
|
||||
answer = jv_array_append(answer, keys[i]);
|
||||
}
|
||||
jv_mem_free(keys);
|
||||
jv_free(x);
|
||||
return answer;
|
||||
} else if (jv_get_kind(x) == JV_KIND_ARRAY) {
|
||||
int n = jv_array_length(x);
|
||||
jv answer = jv_array();
|
||||
for (int i=0; i<n; i++){
|
||||
answer = jv_array_set(answer, i, jv_number(i));
|
||||
}
|
||||
return answer;
|
||||
} else {
|
||||
assert(0 && "jv_keys passed something neither object nor array");
|
||||
return jv_invalid();
|
||||
}
|
||||
}
|
||||
|
||||
int jv_cmp(jv a, jv b) {
|
||||
if (jv_get_kind(a) != jv_get_kind(b)) {
|
||||
int r = (int)jv_get_kind(a) - (int)jv_get_kind(b);
|
||||
jv_free(a);
|
||||
jv_free(b);
|
||||
return r;
|
||||
}
|
||||
int r = 0;
|
||||
switch (jv_get_kind(a)) {
|
||||
default:
|
||||
assert(0 && "invalid kind passed to jv_cmp");
|
||||
case JV_KIND_NULL:
|
||||
case JV_KIND_FALSE:
|
||||
case JV_KIND_TRUE:
|
||||
// there's only one of each of these values
|
||||
r = 0;
|
||||
break;
|
||||
|
||||
case JV_KIND_NUMBER: {
|
||||
double da = jv_number_value(a), db = jv_number_value(b);
|
||||
|
||||
// handle NaN as though it were null
|
||||
if (da != da) r = jv_cmp(jv_null(), jv_number(db));
|
||||
else if (db != db) r = jv_cmp(jv_number(da), jv_null());
|
||||
else if (da < db) r = -1;
|
||||
else if (da == db) r = 0;
|
||||
else r = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
case JV_KIND_STRING: {
|
||||
r = string_cmp(&a, &b);
|
||||
break;
|
||||
}
|
||||
|
||||
case JV_KIND_ARRAY: {
|
||||
// Lexical ordering of arrays
|
||||
int i = 0;
|
||||
while (r == 0) {
|
||||
int a_done = i >= jv_array_length(jv_copy(a));
|
||||
int b_done = i >= jv_array_length(jv_copy(b));
|
||||
if (a_done || b_done) {
|
||||
r = b_done - a_done; //suddenly, logic
|
||||
break;
|
||||
}
|
||||
jv xa = jv_array_get(jv_copy(a), i);
|
||||
jv xb = jv_array_get(jv_copy(b), i);
|
||||
r = jv_cmp(xa, xb);
|
||||
i++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case JV_KIND_OBJECT: {
|
||||
jv keys_a = jv_keys(jv_copy(a));
|
||||
jv keys_b = jv_keys(jv_copy(b));
|
||||
r = jv_cmp(jv_copy(keys_a), keys_b);
|
||||
if (r == 0) {
|
||||
jv_array_foreach(keys_a, i, key) {
|
||||
jv xa = jv_object_get(jv_copy(a), jv_copy(key));
|
||||
jv xb = jv_object_get(jv_copy(b), key);
|
||||
r = jv_cmp(xa, xb);
|
||||
if (r) break;
|
||||
}
|
||||
}
|
||||
jv_free(keys_a);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
jv_free(a);
|
||||
jv_free(b);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
struct sort_entry {
|
||||
jv object;
|
||||
jv key;
|
||||
};
|
||||
|
||||
static int sort_cmp(const void* pa, const void* pb) {
|
||||
const struct sort_entry* a = pa;
|
||||
const struct sort_entry* b = pb;
|
||||
int r = jv_cmp(jv_copy(a->key), jv_copy(b->key));
|
||||
// comparing by address if r == 0 makes the sort stable
|
||||
return r ? r : (int)(a - b);
|
||||
}
|
||||
|
||||
static struct sort_entry* sort_items(jv objects, jv keys) {
|
||||
assert(jv_get_kind(objects) == JV_KIND_ARRAY);
|
||||
assert(jv_get_kind(keys) == JV_KIND_ARRAY);
|
||||
assert(jv_array_length(jv_copy(objects)) == jv_array_length(jv_copy(keys)));
|
||||
int n = jv_array_length(jv_copy(objects));
|
||||
struct sort_entry* entries = jv_mem_alloc(sizeof(struct sort_entry) * n);
|
||||
for (int i=0; i<n; i++) {
|
||||
entries[i].object = jv_array_get(jv_copy(objects), i);
|
||||
entries[i].key = jv_array_get(jv_copy(keys), i);
|
||||
}
|
||||
jv_free(objects);
|
||||
jv_free(keys);
|
||||
qsort(entries, n, sizeof(struct sort_entry), sort_cmp);
|
||||
return entries;
|
||||
}
|
||||
|
||||
jv jv_sort(jv objects, jv keys) {
|
||||
assert(jv_get_kind(objects) == JV_KIND_ARRAY);
|
||||
assert(jv_get_kind(keys) == JV_KIND_ARRAY);
|
||||
assert(jv_array_length(jv_copy(objects)) == jv_array_length(jv_copy(keys)));
|
||||
int n = jv_array_length(jv_copy(objects));
|
||||
struct sort_entry* entries = sort_items(objects, keys);
|
||||
jv ret = jv_array();
|
||||
for (int i=0; i<n; i++) {
|
||||
jv_free(entries[i].key);
|
||||
ret = jv_array_set(ret, i, entries[i].object);
|
||||
}
|
||||
jv_mem_free(entries);
|
||||
return ret;
|
||||
}
|
||||
|
||||
jv jv_group(jv objects, jv keys) {
|
||||
assert(jv_get_kind(objects) == JV_KIND_ARRAY);
|
||||
assert(jv_get_kind(keys) == JV_KIND_ARRAY);
|
||||
assert(jv_array_length(jv_copy(objects)) == jv_array_length(jv_copy(keys)));
|
||||
int n = jv_array_length(jv_copy(objects));
|
||||
struct sort_entry* entries = sort_items(objects, keys);
|
||||
jv ret = jv_array();
|
||||
jv curr_key = entries[0].key;
|
||||
jv group = jv_array_append(jv_array(), entries[0].object);
|
||||
for (int i = 1; i < n; i++) {
|
||||
if (jv_equal(jv_copy(curr_key), jv_copy(entries[i].key))) {
|
||||
jv_free(entries[i].key);
|
||||
} else {
|
||||
jv_free(curr_key);
|
||||
curr_key = entries[i].key;
|
||||
ret = jv_array_append(ret, group);
|
||||
group = jv_array();
|
||||
}
|
||||
group = jv_array_append(group, entries[i].object);
|
||||
}
|
||||
jv_free(curr_key);
|
||||
ret = jv_array_append(ret, group);
|
||||
jv_mem_free(entries);
|
||||
return ret;
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
#ifndef JV_AUX_H
|
||||
#define JV_AUX_H
|
||||
|
||||
#include "jv.h"
|
||||
|
||||
jv jv_get(jv t, jv k);
|
||||
jv jv_set(jv t, jv k, jv v);
|
||||
jv jv_has(jv t, jv k);
|
||||
jv jv_setpath(jv root, jv path, jv value);
|
||||
jv jv_getpath(jv root, jv path);
|
||||
jv jv_delpaths(jv root, jv paths);
|
||||
|
||||
jv jv_keys(jv /*object or array*/);
|
||||
int jv_cmp(jv, jv);
|
||||
|
||||
jv jv_group(jv objects, jv keys);
|
||||
jv jv_sort(jv objects, jv keys);
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,22 +0,0 @@
|
||||
#ifndef JV_DTOA_H
|
||||
#define JV_DTOA_H
|
||||
#define Kmax 7
|
||||
|
||||
struct Bigint;
|
||||
struct dtoa_context {
|
||||
struct Bigint *freelist[Kmax+1];
|
||||
struct Bigint *p5s;
|
||||
};
|
||||
|
||||
void jvp_dtoa_context_init(struct dtoa_context* ctx);
|
||||
void jvp_dtoa_context_free(struct dtoa_context* ctx);
|
||||
|
||||
double jvp_strtod(struct dtoa_context* C, const char* s, char** se);
|
||||
|
||||
|
||||
char* jvp_dtoa(struct dtoa_context* C, double dd, int mode, int ndigits, int *decpt, int *sign, char **rve);
|
||||
void jvp_freedtoa(struct dtoa_context* C, char *s);
|
||||
|
||||
#define JVP_DTOA_FMT_MAX_LEN 32
|
||||
char* jvp_dtoa_fmt(struct dtoa_context* C, register char *b, double x);
|
||||
#endif
|
||||
@@ -1,45 +0,0 @@
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "jv.h"
|
||||
#include "jv_aux.h"
|
||||
#include "jv_parse.h"
|
||||
|
||||
jv jv_load_file(const char* filename, int raw) {
|
||||
FILE* file = fopen(filename, "r");
|
||||
struct jv_parser parser;
|
||||
jv data;
|
||||
if (!file) {
|
||||
return jv_invalid_with_msg(jv_string_fmt("Could not open %s: %s",
|
||||
filename,
|
||||
strerror(errno)));
|
||||
}
|
||||
if (raw) {
|
||||
data = jv_string("");
|
||||
} else {
|
||||
data = jv_array();
|
||||
jv_parser_init(&parser);
|
||||
}
|
||||
while (!feof(file) && !ferror(file)) {
|
||||
char buf[4096];
|
||||
size_t n = fread(buf, 1, sizeof(buf), file);
|
||||
if (raw) {
|
||||
data = jv_string_concat(data, jv_string_sized(buf, (int)n));
|
||||
} else {
|
||||
jv_parser_set_buf(&parser, buf, strlen(buf), !feof(file));
|
||||
jv value;
|
||||
while (jv_is_valid((value = jv_parser_next(&parser))))
|
||||
data = jv_array_append(data, value);
|
||||
}
|
||||
}
|
||||
int badread = ferror(file);
|
||||
fclose(file);
|
||||
if (badread) {
|
||||
jv_free(data);
|
||||
return jv_invalid_with_msg(jv_string_fmt("Error reading from %s",
|
||||
filename));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
#ifndef JV_FILE_H
|
||||
#define JV_FILE_H
|
||||
|
||||
#include "jv.h"
|
||||
|
||||
jv jv_load_file(const char *, int);
|
||||
|
||||
#endif
|
||||
-446
@@ -1,446 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "jv.h"
|
||||
#include "jv_dtoa.h"
|
||||
#include "jv_parse.h"
|
||||
#include "jv_unicode.h"
|
||||
#include "jv_alloc.h"
|
||||
|
||||
typedef const char* presult;
|
||||
|
||||
#define TRY(x) do {presult msg__ = (x); if (msg__) return msg__; } while(0)
|
||||
#ifdef __GNUC__
|
||||
#define pfunc __attribute__((warn_unused_result)) presult
|
||||
#else
|
||||
#define pfunc presult
|
||||
#endif
|
||||
|
||||
void jv_parser_init(struct jv_parser* p) {
|
||||
p->stack = 0;
|
||||
p->stacklen = p->stackpos = 0;
|
||||
p->next = jv_invalid();
|
||||
p->tokenbuf = 0;
|
||||
p->tokenlen = p->tokenpos = 0;
|
||||
p->st = JV_PARSER_NORMAL;
|
||||
p->curr_buf = 0;
|
||||
p->curr_buf_length = p->curr_buf_pos = p->curr_buf_is_partial = 0;
|
||||
p->bom_strip_position = 0;
|
||||
p->line = 1;
|
||||
p->column = 0;
|
||||
jvp_dtoa_context_init(&p->dtoa);
|
||||
}
|
||||
|
||||
void jv_parser_free(struct jv_parser* p) {
|
||||
jv_free(p->next);
|
||||
for (int i=0; i<p->stackpos; i++)
|
||||
jv_free(p->stack[i]);
|
||||
jv_mem_free(p->stack);
|
||||
jv_mem_free(p->tokenbuf);
|
||||
jvp_dtoa_context_free(&p->dtoa);
|
||||
}
|
||||
|
||||
static pfunc value(struct jv_parser* p, jv val) {
|
||||
if (jv_is_valid(p->next)) return "Expected separator between values";
|
||||
jv_free(p->next);
|
||||
p->next = val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void push(struct jv_parser* p, jv v) {
|
||||
assert(p->stackpos <= p->stacklen);
|
||||
if (p->stackpos == p->stacklen) {
|
||||
p->stacklen = p->stacklen * 2 + 10;
|
||||
p->stack = jv_mem_realloc(p->stack, p->stacklen * sizeof(jv));
|
||||
}
|
||||
assert(p->stackpos < p->stacklen);
|
||||
p->stack[p->stackpos++] = v;
|
||||
}
|
||||
|
||||
static pfunc token(struct jv_parser* p, char ch) {
|
||||
switch (ch) {
|
||||
case '[':
|
||||
if (jv_is_valid(p->next)) return "Expected separator between values";
|
||||
push(p, jv_array());
|
||||
break;
|
||||
|
||||
case '{':
|
||||
if (jv_is_valid(p->next)) return "Expected separator between values";
|
||||
push(p, jv_object());
|
||||
break;
|
||||
|
||||
case ':':
|
||||
if (!jv_is_valid(p->next))
|
||||
return "Expected string key before ':'";
|
||||
if (p->stackpos == 0 || jv_get_kind(p->stack[p->stackpos-1]) != JV_KIND_OBJECT)
|
||||
return "':' not as part of an object";
|
||||
if (jv_get_kind(p->next) != JV_KIND_STRING)
|
||||
return "Object keys must be strings";
|
||||
push(p, p->next);
|
||||
p->next = jv_invalid();
|
||||
break;
|
||||
|
||||
case ',':
|
||||
if (!jv_is_valid(p->next))
|
||||
return "Expected value before ','";
|
||||
if (p->stackpos == 0)
|
||||
return "',' not as part of an object or array";
|
||||
if (jv_get_kind(p->stack[p->stackpos-1]) == JV_KIND_ARRAY) {
|
||||
p->stack[p->stackpos-1] = jv_array_append(p->stack[p->stackpos-1], p->next);
|
||||
p->next = jv_invalid();
|
||||
} else if (jv_get_kind(p->stack[p->stackpos-1]) == JV_KIND_STRING) {
|
||||
assert(p->stackpos > 1 && jv_get_kind(p->stack[p->stackpos-2]) == JV_KIND_OBJECT);
|
||||
p->stack[p->stackpos-2] = jv_object_set(p->stack[p->stackpos-2],
|
||||
p->stack[p->stackpos-1], p->next);
|
||||
p->stackpos--;
|
||||
p->next = jv_invalid();
|
||||
} else {
|
||||
// this case hits on input like {"a", "b"}
|
||||
return "Objects must consist of key:value pairs";
|
||||
}
|
||||
break;
|
||||
|
||||
case ']':
|
||||
if (p->stackpos == 0 || jv_get_kind(p->stack[p->stackpos-1]) != JV_KIND_ARRAY)
|
||||
return "Unmatched ']'";
|
||||
if (jv_is_valid(p->next)) {
|
||||
p->stack[p->stackpos-1] = jv_array_append(p->stack[p->stackpos-1], p->next);
|
||||
p->next = jv_invalid();
|
||||
} else {
|
||||
if (jv_array_length(jv_copy(p->stack[p->stackpos-1])) != 0) {
|
||||
// this case hits on input like [1,2,3,]
|
||||
return "Expected another array element";
|
||||
}
|
||||
}
|
||||
jv_free(p->next);
|
||||
p->next = p->stack[--p->stackpos];
|
||||
break;
|
||||
|
||||
case '}':
|
||||
if (p->stackpos == 0)
|
||||
return "Unmatched '}'";
|
||||
if (jv_is_valid(p->next)) {
|
||||
if (jv_get_kind(p->stack[p->stackpos-1]) != JV_KIND_STRING)
|
||||
return "Objects must consist of key:value pairs";
|
||||
assert(p->stackpos > 1 && jv_get_kind(p->stack[p->stackpos-2]) == JV_KIND_OBJECT);
|
||||
p->stack[p->stackpos-2] = jv_object_set(p->stack[p->stackpos-2],
|
||||
p->stack[p->stackpos-1], p->next);
|
||||
p->stackpos--;
|
||||
p->next = jv_invalid();
|
||||
} else {
|
||||
if (jv_get_kind(p->stack[p->stackpos-1]) != JV_KIND_OBJECT)
|
||||
return "Unmatched '}'";
|
||||
if (jv_object_length(jv_copy(p->stack[p->stackpos-1])) != 0)
|
||||
return "Expected another key-value pair";
|
||||
}
|
||||
jv_free(p->next);
|
||||
p->next = p->stack[--p->stackpos];
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void tokenadd(struct jv_parser* p, char c) {
|
||||
assert(p->tokenpos <= p->tokenlen);
|
||||
if (p->tokenpos == p->tokenlen) {
|
||||
p->tokenlen = p->tokenlen*2 + 256;
|
||||
p->tokenbuf = jv_mem_realloc(p->tokenbuf, p->tokenlen);
|
||||
}
|
||||
assert(p->tokenpos < p->tokenlen);
|
||||
p->tokenbuf[p->tokenpos++] = c;
|
||||
}
|
||||
|
||||
static int unhex4(char* hex) {
|
||||
int r = 0;
|
||||
for (int i=0; i<4; i++) {
|
||||
char c = *hex++;
|
||||
int n;
|
||||
if ('0' <= c && c <= '9') n = c - '0';
|
||||
else if ('a' <= c && c <= 'f') n = c - 'a' + 10;
|
||||
else if ('A' <= c && c <= 'F') n = c - 'A' + 10;
|
||||
else return -1;
|
||||
r <<= 4;
|
||||
r |= n;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static pfunc found_string(struct jv_parser* p) {
|
||||
char* in = p->tokenbuf;
|
||||
char* out = p->tokenbuf;
|
||||
char* end = p->tokenbuf + p->tokenpos;
|
||||
|
||||
while (in < end) {
|
||||
char c = *in++;
|
||||
if (c == '\\') {
|
||||
if (in >= end)
|
||||
return "Expected escape character at end of string";
|
||||
c = *in++;
|
||||
switch (c) {
|
||||
case '\\':
|
||||
case '"':
|
||||
case '/': *out++ = c; break;
|
||||
case 'b': *out++ = '\b'; break;
|
||||
case 'f': *out++ = '\f'; break;
|
||||
case 't': *out++ = '\t'; break;
|
||||
case 'n': *out++ = '\n'; break;
|
||||
case 'r': *out++ = '\r'; break;
|
||||
|
||||
case 'u':
|
||||
/* ahh, the complicated case */
|
||||
if (in + 4 > end)
|
||||
return "Invalid \\uXXXX escape";
|
||||
int hexvalue = unhex4(in);
|
||||
if (hexvalue < 0)
|
||||
return "Invalid characters in \\uXXXX escape";
|
||||
unsigned long codepoint = (unsigned long)hexvalue;
|
||||
in += 4;
|
||||
if (0xD800 <= codepoint && codepoint <= 0xDBFF) {
|
||||
/* who thought UTF-16 surrogate pairs were a good idea? */
|
||||
if (in + 6 > end || in[0] != '\\' || in[1] != 'u')
|
||||
return "Invalid \\uXXXX\\uXXXX surrogate pair escape";
|
||||
unsigned long surrogate = unhex4(in+2);
|
||||
if (!(0xDC00 <= surrogate && surrogate <= 0xDFFF))
|
||||
return "Invalid \\uXXXX\\uXXXX surrogate pair escape";
|
||||
in += 6;
|
||||
codepoint = 0x10000 + (((codepoint - 0xD800) << 10)
|
||||
|(surrogate - 0xDC00));
|
||||
}
|
||||
// FIXME assert valid codepoint
|
||||
out += jvp_utf8_encode(codepoint, out);
|
||||
break;
|
||||
|
||||
default:
|
||||
return "Invalid escape";
|
||||
}
|
||||
} else {
|
||||
*out++ = c;
|
||||
}
|
||||
}
|
||||
TRY(value(p, jv_string_sized(p->tokenbuf, out - p->tokenbuf)));
|
||||
p->tokenpos = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static pfunc check_literal(struct jv_parser* p) {
|
||||
if (p->tokenpos == 0) return 0;
|
||||
|
||||
const char* pattern = 0;
|
||||
int plen;
|
||||
jv v;
|
||||
switch (p->tokenbuf[0]) {
|
||||
case 't': pattern = "true"; plen = 4; v = jv_true(); break;
|
||||
case 'f': pattern = "false"; plen = 5; v = jv_false(); break;
|
||||
case 'n': pattern = "null"; plen = 4; v = jv_null(); break;
|
||||
}
|
||||
if (pattern) {
|
||||
if (p->tokenpos != plen) return "Invalid literal";
|
||||
for (int i=0; i<plen; i++)
|
||||
if (p->tokenbuf[i] != pattern[i])
|
||||
return "Invalid literal";
|
||||
TRY(value(p, v));
|
||||
} else {
|
||||
// FIXME: better parser
|
||||
p->tokenbuf[p->tokenpos] = 0; // FIXME: invalid
|
||||
char* end = 0;
|
||||
double d = jvp_strtod(&p->dtoa, p->tokenbuf, &end);
|
||||
if (end == 0 || *end != 0)
|
||||
return "Invalid numeric literal";
|
||||
TRY(value(p, jv_number(d)));
|
||||
}
|
||||
p->tokenpos = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
LITERAL,
|
||||
WHITESPACE,
|
||||
STRUCTURE,
|
||||
QUOTE,
|
||||
INVALID
|
||||
} chclass;
|
||||
|
||||
static chclass classify(char c) {
|
||||
switch (c) {
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\r':
|
||||
case '\n':
|
||||
return WHITESPACE;
|
||||
case '"':
|
||||
return QUOTE;
|
||||
case '[':
|
||||
case ',':
|
||||
case ']':
|
||||
case '{':
|
||||
case ':':
|
||||
case '}':
|
||||
return STRUCTURE;
|
||||
default:
|
||||
return LITERAL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const presult OK = "output produced";
|
||||
|
||||
static int check_done(struct jv_parser* p, jv* out) {
|
||||
if (p->stackpos == 0 && jv_is_valid(p->next)) {
|
||||
*out = p->next;
|
||||
p->next = jv_invalid();
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static pfunc scan(struct jv_parser* p, char ch, jv* out) {
|
||||
p->column++;
|
||||
if (ch == '\n') {
|
||||
p->line++;
|
||||
p->column = 0;
|
||||
}
|
||||
presult answer = 0;
|
||||
if (p->st == JV_PARSER_NORMAL) {
|
||||
chclass cls = classify(ch);
|
||||
if (cls != LITERAL) {
|
||||
TRY(check_literal(p));
|
||||
if (check_done(p, out)) answer = OK;
|
||||
}
|
||||
switch (cls) {
|
||||
case LITERAL:
|
||||
tokenadd(p, ch);
|
||||
break;
|
||||
case WHITESPACE:
|
||||
break;
|
||||
case QUOTE:
|
||||
p->st = JV_PARSER_STRING;
|
||||
break;
|
||||
case STRUCTURE:
|
||||
TRY(token(p, ch));
|
||||
break;
|
||||
case INVALID:
|
||||
return "Invalid character";
|
||||
}
|
||||
if (check_done(p, out)) answer = OK;
|
||||
} else {
|
||||
if (ch == '"' && p->st == JV_PARSER_STRING) {
|
||||
TRY(found_string(p));
|
||||
p->st = JV_PARSER_NORMAL;
|
||||
if (check_done(p, out)) answer = OK;
|
||||
} else {
|
||||
tokenadd(p, ch);
|
||||
if (ch == '\\' && p->st == JV_PARSER_STRING) {
|
||||
p->st = JV_PARSER_STRING_ESCAPE;
|
||||
} else {
|
||||
p->st = JV_PARSER_STRING;
|
||||
}
|
||||
}
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
static const unsigned char UTF8_BOM[] = {0xEF,0xBB,0xBF};
|
||||
|
||||
void jv_parser_set_buf(struct jv_parser* p, const char* buf, int length, int is_partial) {
|
||||
assert((p->curr_buf == 0 || p->curr_buf_pos == p->curr_buf_length)
|
||||
&& "previous buffer not exhausted");
|
||||
while (p->bom_strip_position < sizeof(UTF8_BOM)) {
|
||||
if ((unsigned char)*buf == UTF8_BOM[p->bom_strip_position]) {
|
||||
// matched a BOM character
|
||||
buf++;
|
||||
length--;
|
||||
p->bom_strip_position++;
|
||||
} else {
|
||||
if (p->bom_strip_position == 0) {
|
||||
// no BOM in this document
|
||||
p->bom_strip_position = sizeof(UTF8_BOM);
|
||||
} else {
|
||||
// malformed BOM (prefix present, rest missing)
|
||||
p->bom_strip_position = 0xff;
|
||||
}
|
||||
}
|
||||
}
|
||||
p->curr_buf = buf;
|
||||
p->curr_buf_length = length;
|
||||
p->curr_buf_pos = 0;
|
||||
p->curr_buf_is_partial = is_partial;
|
||||
}
|
||||
|
||||
jv jv_parser_next(struct jv_parser* p) {
|
||||
assert(p->curr_buf && "a buffer must be provided");
|
||||
if (p->bom_strip_position == 0xff) return jv_invalid_with_msg(jv_string("Malformed BOM"));
|
||||
jv value;
|
||||
presult msg = 0;
|
||||
while (!msg && p->curr_buf_pos < p->curr_buf_length) {
|
||||
char ch = p->curr_buf[p->curr_buf_pos++];
|
||||
msg = scan(p, ch, &value);
|
||||
}
|
||||
if (msg == OK) {
|
||||
return value;
|
||||
} else if (msg) {
|
||||
return jv_invalid_with_msg(jv_string_fmt("%s at line %d, column %d", msg, p->line, p->column));
|
||||
} else if (p->curr_buf_is_partial) {
|
||||
assert(p->curr_buf_pos == p->curr_buf_length);
|
||||
// need another buffer
|
||||
return jv_invalid();
|
||||
} else {
|
||||
assert(p->curr_buf_pos == p->curr_buf_length);
|
||||
// at EOF
|
||||
if (p->st != JV_PARSER_NORMAL)
|
||||
return jv_invalid_with_msg(jv_string("Unfinished string"));
|
||||
if ((msg = check_literal(p)))
|
||||
return jv_invalid_with_msg(jv_string(msg));
|
||||
if (p->stackpos != 0)
|
||||
return jv_invalid_with_msg(jv_string("Unfinished JSON term"));
|
||||
// p->next is either invalid (nothing here but no syntax error)
|
||||
// or valid (this is the value). either way it's the thing to return
|
||||
value = p->next;
|
||||
p->next = jv_invalid();
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
jv jv_parse_sized(const char* string, int length) {
|
||||
struct jv_parser parser;
|
||||
jv_parser_init(&parser);
|
||||
jv_parser_set_buf(&parser, string, length, 0);
|
||||
jv value = jv_parser_next(&parser);
|
||||
if (jv_is_valid(value)) {
|
||||
jv next = jv_parser_next(&parser);
|
||||
if (jv_is_valid(next)) {
|
||||
// multiple JSON values, we only wanted one
|
||||
jv_free(value);
|
||||
jv_free(next);
|
||||
value = jv_invalid_with_msg(jv_string("Unexpected extra JSON values"));
|
||||
} else if (jv_invalid_has_msg(jv_copy(next))) {
|
||||
// parser error after the first JSON value
|
||||
jv_free(value);
|
||||
value = next;
|
||||
} else {
|
||||
// a single valid JSON value
|
||||
jv_free(next);
|
||||
}
|
||||
} else if (jv_invalid_has_msg(jv_copy(value))) {
|
||||
// parse error, we'll return it
|
||||
} else {
|
||||
// no value at all
|
||||
jv_free(value);
|
||||
value = jv_invalid_with_msg(jv_string("Expected JSON value"));
|
||||
}
|
||||
jv_parser_free(&parser);
|
||||
|
||||
if (!jv_is_valid(value) && jv_invalid_has_msg(jv_copy(value))) {
|
||||
jv msg = jv_invalid_get_msg(value);
|
||||
value = jv_invalid_with_msg(jv_string_fmt("%s (while parsing '%s')",
|
||||
jv_string_value(msg),
|
||||
string));
|
||||
jv_free(msg);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
jv jv_parse(const char* string) {
|
||||
return jv_parse_sized(string, strlen(string));
|
||||
}
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
#ifndef JV_PARSE_H
|
||||
#define JV_PARSE_H
|
||||
#include "jv_dtoa.h"
|
||||
struct jv_parser {
|
||||
const char* curr_buf;
|
||||
int curr_buf_length;
|
||||
int curr_buf_pos;
|
||||
int curr_buf_is_partial;
|
||||
unsigned bom_strip_position;
|
||||
|
||||
jv* stack;
|
||||
int stackpos;
|
||||
int stacklen;
|
||||
jv next;
|
||||
|
||||
char* tokenbuf;
|
||||
int tokenpos;
|
||||
int tokenlen;
|
||||
|
||||
int line, column;
|
||||
|
||||
struct dtoa_context dtoa;
|
||||
|
||||
enum {
|
||||
JV_PARSER_NORMAL,
|
||||
JV_PARSER_STRING,
|
||||
JV_PARSER_STRING_ESCAPE
|
||||
} st;
|
||||
};
|
||||
|
||||
void jv_parser_init(struct jv_parser* p);
|
||||
void jv_parser_free(struct jv_parser* p);
|
||||
|
||||
void jv_parser_set_buf(struct jv_parser* p, const char* buf, int length, int is_partial);
|
||||
|
||||
jv jv_parser_next(struct jv_parser* p);
|
||||
#endif
|
||||
-277
@@ -1,277 +0,0 @@
|
||||
#include "jv.h"
|
||||
#include <stdio.h>
|
||||
#include <float.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "jv_dtoa.h"
|
||||
#include "jv_unicode.h"
|
||||
#include "jv_aux.h"
|
||||
|
||||
#define ESC "\033"
|
||||
#define COL(c) (ESC "[" c "m")
|
||||
#define COLRESET (ESC "[0m")
|
||||
|
||||
// Colour table. See http://en.wikipedia.org/wiki/ANSI_escape_code#Colors
|
||||
// for how to choose these.
|
||||
static const jv_kind colour_kinds[] =
|
||||
{JV_KIND_NULL, JV_KIND_FALSE, JV_KIND_TRUE, JV_KIND_NUMBER,
|
||||
JV_KIND_STRING, JV_KIND_ARRAY, JV_KIND_OBJECT};
|
||||
static const char* const colours[] =
|
||||
{COL("30;1"), COL("0"), COL("0"), COL("0"),
|
||||
COL("32"), COL("37"), COL("37")};
|
||||
#define FIELD_COLOUR COL("34;1")
|
||||
|
||||
static void put_buf(const char* s, int len, FILE* fout, jv* strout) {
|
||||
if (strout) {
|
||||
*strout = jv_string_append_buf(*strout, s, len);
|
||||
} else {
|
||||
fwrite(s, 1, len, fout);
|
||||
}
|
||||
}
|
||||
|
||||
static void put_char(char c, FILE* fout, jv* strout) {
|
||||
put_buf(&c, 1, fout, strout);
|
||||
}
|
||||
|
||||
static void put_str(const char* s, FILE* fout, jv* strout) {
|
||||
put_buf(s, strlen(s), fout, strout);
|
||||
}
|
||||
|
||||
static void put_space(int n, FILE* fout, jv* strout) {
|
||||
while (n--) {
|
||||
put_char(' ', fout, strout);
|
||||
}
|
||||
}
|
||||
|
||||
static void jvp_dump_string(jv str, int ascii_only, FILE* F, jv* S) {
|
||||
assert(jv_get_kind(str) == JV_KIND_STRING);
|
||||
const char* i = jv_string_value(str);
|
||||
const char* end = i + jv_string_length_bytes(jv_copy(str));
|
||||
const char* cstart;
|
||||
int c = 0;
|
||||
char buf[32];
|
||||
put_char('"', F, S);
|
||||
while ((i = jvp_utf8_next((cstart = i), end, &c))) {
|
||||
assert(c != -1);
|
||||
int unicode_escape = 0;
|
||||
if (0x20 <= c && c <= 0x7E) {
|
||||
// printable ASCII
|
||||
if (c == '"' || c == '\\') {
|
||||
put_char('\\', F, S);
|
||||
}
|
||||
put_char(c, F, S);
|
||||
} else if (c < 0x20 || c == 0x7F) {
|
||||
// ASCII control character
|
||||
switch (c) {
|
||||
case '\b':
|
||||
put_char('\\', F, S);
|
||||
put_char('b', F, S);
|
||||
break;
|
||||
case '\t':
|
||||
put_char('\\', F, S);
|
||||
put_char('t', F, S);
|
||||
break;
|
||||
case '\r':
|
||||
put_char('\\', F, S);
|
||||
put_char('r', F, S);
|
||||
break;
|
||||
case '\n':
|
||||
put_char('\\', F, S);
|
||||
put_char('n', F, S);
|
||||
break;
|
||||
case '\f':
|
||||
put_char('\\', F, S);
|
||||
put_char('f', F, S);
|
||||
break;
|
||||
default:
|
||||
unicode_escape = 1;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (ascii_only) {
|
||||
unicode_escape = 1;
|
||||
} else {
|
||||
put_buf(cstart, i - cstart, F, S);
|
||||
}
|
||||
}
|
||||
if (unicode_escape) {
|
||||
if (c <= 0xffff) {
|
||||
sprintf(buf, "\\u%04x", c);
|
||||
} else {
|
||||
c -= 0x10000;
|
||||
sprintf(buf, "\\u%04x\\u%04x",
|
||||
0xD800 | ((c & 0xffc00) >> 10),
|
||||
0xDC00 | (c & 0x003ff));
|
||||
}
|
||||
put_str(buf, F, S);
|
||||
}
|
||||
}
|
||||
assert(c != -1);
|
||||
put_char('"', F, S);
|
||||
}
|
||||
|
||||
enum { INDENT = 2 };
|
||||
|
||||
static void jv_dump_term(struct dtoa_context* C, jv x, int flags, int indent, FILE* F, jv* S) {
|
||||
char buf[JVP_DTOA_FMT_MAX_LEN];
|
||||
const char* colour = 0;
|
||||
if (flags & JV_PRINT_COLOUR) {
|
||||
for (unsigned i=0; i<sizeof(colour_kinds)/sizeof(colour_kinds[0]); i++) {
|
||||
if (jv_get_kind(x) == colour_kinds[i]) {
|
||||
colour = colours[i];
|
||||
put_str(colour, F, S);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
switch (jv_get_kind(x)) {
|
||||
default:
|
||||
case JV_KIND_INVALID:
|
||||
assert(0 && "Invalid value");
|
||||
break;
|
||||
case JV_KIND_NULL:
|
||||
put_str("null", F, S);
|
||||
break;
|
||||
case JV_KIND_FALSE:
|
||||
put_str("false", F, S);
|
||||
break;
|
||||
case JV_KIND_TRUE:
|
||||
put_str("true", F, S);
|
||||
break;
|
||||
case JV_KIND_NUMBER: {
|
||||
double d = jv_number_value(x);
|
||||
if (d != d) {
|
||||
// JSON doesn't have NaN, so we'll render it as "null"
|
||||
put_str("null", F, S);
|
||||
} else {
|
||||
// Normalise infinities to something we can print in valid JSON
|
||||
if (d > DBL_MAX) d = DBL_MAX;
|
||||
if (d < -DBL_MAX) d = -DBL_MAX;
|
||||
put_str(jvp_dtoa_fmt(C, buf, d), F, S);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case JV_KIND_STRING:
|
||||
jvp_dump_string(x, flags & JV_PRINT_ASCII, F, S);
|
||||
break;
|
||||
case JV_KIND_ARRAY: {
|
||||
if (jv_array_length(jv_copy(x)) == 0) {
|
||||
put_str("[]", F, S);
|
||||
break;
|
||||
}
|
||||
put_str("[", F, S);
|
||||
if (flags & JV_PRINT_PRETTY) {
|
||||
put_char('\n', F, S);
|
||||
put_space(indent+INDENT, F, S);
|
||||
}
|
||||
jv_array_foreach(x, i, elem) {
|
||||
if (i!=0) {
|
||||
if (flags & JV_PRINT_PRETTY) {
|
||||
put_str(",\n", F, S);
|
||||
put_space(indent+INDENT, F, S);
|
||||
} else {
|
||||
put_str(",", F, S);
|
||||
}
|
||||
}
|
||||
jv_dump_term(C, elem, flags, indent + INDENT, F, S);
|
||||
if (colour) put_str(colour, F, S);
|
||||
}
|
||||
if (flags & JV_PRINT_PRETTY) {
|
||||
put_char('\n', F, S);
|
||||
put_space(indent, F, S);
|
||||
}
|
||||
if (colour) put_str(colour, F, S);
|
||||
put_char(']', F, S);
|
||||
break;
|
||||
}
|
||||
case JV_KIND_OBJECT: {
|
||||
if (jv_object_length(jv_copy(x)) == 0) {
|
||||
put_str("{}", F, S);
|
||||
break;
|
||||
}
|
||||
put_char('{', F, S);
|
||||
if (flags & JV_PRINT_PRETTY) {
|
||||
put_char('\n', F, S);
|
||||
put_space(indent+INDENT, F, S);
|
||||
}
|
||||
int first = 1;
|
||||
int i = 0;
|
||||
jv keyset = jv_null();
|
||||
while (1) {
|
||||
jv key, value;
|
||||
if (flags & JV_PRINT_SORTED) {
|
||||
if (first) {
|
||||
keyset = jv_keys(jv_copy(x));
|
||||
i = 0;
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
if (i >= jv_array_length(jv_copy(keyset))) {
|
||||
jv_free(keyset);
|
||||
break;
|
||||
}
|
||||
key = jv_array_get(jv_copy(keyset), i);
|
||||
value = jv_object_get(jv_copy(x), jv_copy(key));
|
||||
} else {
|
||||
if (first) {
|
||||
i = jv_object_iter(x);
|
||||
} else {
|
||||
i = jv_object_iter_next(x, i);
|
||||
}
|
||||
if (!jv_object_iter_valid(x, i)) break;
|
||||
key = jv_object_iter_key(x, i);
|
||||
value = jv_object_iter_value(x, i);
|
||||
}
|
||||
|
||||
if (!first) {
|
||||
if (flags & JV_PRINT_PRETTY){
|
||||
put_str(",\n", F, S);
|
||||
put_space(indent+INDENT, F, S);
|
||||
} else {
|
||||
put_str(",", F, S);
|
||||
}
|
||||
}
|
||||
if (colour) put_str(COLRESET, F, S);
|
||||
|
||||
first = 0;
|
||||
if (colour) put_str(FIELD_COLOUR, F, S);
|
||||
jvp_dump_string(key, flags & JV_PRINT_ASCII, F, S);
|
||||
jv_free(key);
|
||||
if (colour) put_str(COLRESET, F, S);
|
||||
|
||||
if (colour) put_str(colour, F, S);
|
||||
put_str((flags & JV_PRINT_PRETTY) ? ": " : ":", F, S);
|
||||
if (colour) put_str(COLRESET, F, S);
|
||||
|
||||
jv_dump_term(C, value, flags, indent + INDENT, F, S);
|
||||
if (colour) put_str(colour, F, S);
|
||||
}
|
||||
if (flags & JV_PRINT_PRETTY) {
|
||||
put_char('\n', F, S);
|
||||
put_space(indent, F, S);
|
||||
}
|
||||
if (colour) put_str(colour, F, S);
|
||||
put_char('}', F, S);
|
||||
}
|
||||
}
|
||||
jv_free(x);
|
||||
if (colour) {
|
||||
put_str(COLRESET, F, S);
|
||||
}
|
||||
}
|
||||
|
||||
void jv_dump(jv x, int flags) {
|
||||
struct dtoa_context C;
|
||||
jvp_dtoa_context_init(&C);
|
||||
jv_dump_term(&C, x, flags, 0, stdout, 0);
|
||||
jvp_dtoa_context_free(&C);
|
||||
}
|
||||
|
||||
jv jv_dump_string(jv x, int flags) {
|
||||
struct dtoa_context C;
|
||||
jvp_dtoa_context_init(&C);
|
||||
jv s = jv_string("");
|
||||
jv_dump_term(&C, x, flags, 0, 0, &s);
|
||||
jvp_dtoa_context_free(&C);
|
||||
return s;
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include "jv_unicode.h"
|
||||
#include "jv_utf8_tables.h"
|
||||
|
||||
const char* jvp_utf8_next(const char* in, const char* end, int* codepoint) {
|
||||
if (in == end) {
|
||||
codepoint = 0;
|
||||
return 0;
|
||||
}
|
||||
unsigned char first = (unsigned char)in[0];
|
||||
int length = utf8_coding_length[first];
|
||||
if (length == 0 || length == UTF8_CONTINUATION_BYTE || in + length > end) {
|
||||
*codepoint = -1;
|
||||
return 0;
|
||||
}
|
||||
*codepoint = ((unsigned)in[0]) & utf8_coding_bits[first];
|
||||
for (int i=1; i<length; i++) {
|
||||
int ch = (unsigned char)in[i];
|
||||
if (utf8_coding_length[(unsigned char)in[i]] != UTF8_CONTINUATION_BYTE){
|
||||
*codepoint = -1;
|
||||
return 0;
|
||||
}
|
||||
*codepoint = (*codepoint << 6) | (ch & 0x3f);
|
||||
}
|
||||
return in + length;
|
||||
}
|
||||
|
||||
int jvp_utf8_verify(const char* in, const char* end) {
|
||||
int codepoint = 0;
|
||||
while ((in = jvp_utf8_next(in, end, &codepoint))) {
|
||||
if (codepoint == -1) return 0;
|
||||
}
|
||||
return codepoint != -1;
|
||||
}
|
||||
|
||||
int jvp_utf8_encode_length(int codepoint) {
|
||||
if (codepoint <= 0x7F) return 1;
|
||||
else if (codepoint <= 0x7FF) return 2;
|
||||
else if (codepoint <= 0xFFFF) return 3;
|
||||
else return 4;
|
||||
}
|
||||
|
||||
int jvp_utf8_encode(int codepoint, char* out) {
|
||||
assert(codepoint >= 0 && codepoint <= 0x10FFFF);
|
||||
char* start = out;
|
||||
if (codepoint <= 0x7F) {
|
||||
*out++ = codepoint;
|
||||
} else if (codepoint <= 0x7FF) {
|
||||
*out++ = 0xC0 + ((codepoint & 0x7C0) >> 6);
|
||||
*out++ = 0x80 + ((codepoint & 0x03F));
|
||||
} else if(codepoint <= 0xFFFF) {
|
||||
*out++ = 0xE0 + ((codepoint & 0xF000) >> 12);
|
||||
*out++ = 0x80 + ((codepoint & 0x0FC0) >> 6);
|
||||
*out++ = 0x80 + ((codepoint & 0x003F));
|
||||
} else {
|
||||
*out++ = 0xF0 + ((codepoint & 0x1C0000) >> 18);
|
||||
*out++ = 0x80 + ((codepoint & 0x03F000) >> 12);
|
||||
*out++ = 0x80 + ((codepoint & 0x000FC0) >> 6);
|
||||
*out++ = 0x80 + ((codepoint & 0x00003F));
|
||||
}
|
||||
assert(out - start == jvp_utf8_encode_length(codepoint));
|
||||
return out - start;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
#ifndef JV_UNICODE_H
|
||||
#define JV_UNICODE_H
|
||||
|
||||
const char* jvp_utf8_next(const char* in, const char* end, int* codepoint);
|
||||
|
||||
|
||||
int jvp_utf8_decode_length(char startchar);
|
||||
|
||||
int jvp_utf8_encode_length(int codepoint);
|
||||
int jvp_utf8_encode(int codepoint, char* out);
|
||||
#endif
|
||||
@@ -1,35 +0,0 @@
|
||||
#define UTF8_CONTINUATION_BYTE ((unsigned char)255)
|
||||
static const unsigned char utf8_coding_length[] =
|
||||
{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
|
||||
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
||||
0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
static const unsigned char utf8_coding_bits[] =
|
||||
{0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
|
||||
0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
|
||||
0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
|
||||
0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
|
||||
0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
|
||||
0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
|
||||
0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
|
||||
0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
|
||||
0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
|
||||
0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
|
||||
0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
|
||||
0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
|
||||
0x00, 0x00, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
|
||||
0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
|
||||
0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
|
||||
0x07, 0x07, 0x07, 0x07, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
@@ -1,172 +0,0 @@
|
||||
%{
|
||||
#include "jv_alloc.h"
|
||||
#include "compile.h"
|
||||
|
||||
struct lexer_param;
|
||||
|
||||
#include "parser.h" /* Generated by bison. */
|
||||
|
||||
#define YY_USER_ACTION \
|
||||
do { \
|
||||
yylloc->start = yyget_extra(yyscanner); \
|
||||
yylloc->end = yylloc->start + yyleng; \
|
||||
yyset_extra(yylloc->end, yyscanner); \
|
||||
} while (0);
|
||||
|
||||
%}
|
||||
|
||||
%s IN_PAREN
|
||||
%s IN_BRACKET
|
||||
%s IN_BRACE
|
||||
%s IN_QQINTERP
|
||||
%x IN_QQSTRING
|
||||
%{
|
||||
static int enter(int opening, int state, yyscan_t yyscanner);
|
||||
static int try_exit(int closing, int state, yyscan_t yyscanner);
|
||||
%}
|
||||
|
||||
%option noyywrap nounput noinput nodefault
|
||||
%option noyyalloc noyyrealloc noyyfree
|
||||
%option reentrant
|
||||
%option extra-type="int"
|
||||
%option bison-bridge bison-locations
|
||||
%option prefix="jq_yy"
|
||||
%option stack
|
||||
|
||||
|
||||
%%
|
||||
|
||||
"#"[^\r\n]* { /* comments */ }
|
||||
|
||||
"!=" { return NEQ; }
|
||||
"==" { return EQ; }
|
||||
"as" { return AS; }
|
||||
"def" { return DEF; }
|
||||
"if" { return IF; }
|
||||
"then" { return THEN; }
|
||||
"else" { return ELSE; }
|
||||
"elif" { return ELSE_IF; }
|
||||
"and" { return AND; }
|
||||
"or" { return OR; }
|
||||
"end" { return END; }
|
||||
"reduce" { return REDUCE; }
|
||||
"//" { return DEFINEDOR; }
|
||||
"|=" { return SETPIPE; }
|
||||
"+=" { return SETPLUS; }
|
||||
"-=" { return SETMINUS; }
|
||||
"*=" { return SETMULT; }
|
||||
"/=" { return SETDIV; }
|
||||
"//=" { return SETDEFINEDOR; }
|
||||
"<=" { return LESSEQ; }
|
||||
">=" { return GREATEREQ; }
|
||||
"."|"="|";"|","|":"|"|"|"+"|"-"|"*"|"/"|"\$"|"<"|">" { return yytext[0];}
|
||||
|
||||
"["|"{"|"(" {
|
||||
return enter(yytext[0], YY_START, yyscanner);
|
||||
}
|
||||
|
||||
"]"|"}"|")" {
|
||||
return try_exit(yytext[0], YY_START, yyscanner);
|
||||
}
|
||||
|
||||
"@"[a-zA-Z0-9_]+ {
|
||||
yylval->literal = jv_string_sized(yytext + 1, yyleng - 1); return FORMAT;
|
||||
}
|
||||
|
||||
[0-9.]+([eE][+-]?[0-9]+)? {
|
||||
yylval->literal = jv_parse_sized(yytext, yyleng); return LITERAL;
|
||||
}
|
||||
|
||||
"\"" {
|
||||
yy_push_state(IN_QQSTRING, yyscanner);
|
||||
return QQSTRING_START;
|
||||
}
|
||||
|
||||
<IN_QQSTRING>{
|
||||
"\\(" {
|
||||
return enter(QQSTRING_INTERP_START, YY_START, yyscanner);
|
||||
}
|
||||
"\"" {
|
||||
yy_pop_state(yyscanner);
|
||||
return QQSTRING_END;
|
||||
}
|
||||
(\\[^u(]|\\u[a-zA-Z0-9]{0,4})+ {
|
||||
/* pass escapes to the json parser */
|
||||
jv escapes = jv_string_fmt("\"%.*s\"", yyleng, yytext);
|
||||
yylval->literal = jv_parse_sized(jv_string_value(escapes), jv_string_length_bytes(jv_copy(escapes)));
|
||||
jv_free(escapes);
|
||||
return QQSTRING_TEXT;
|
||||
}
|
||||
[^\\\"]+ {
|
||||
yylval->literal = jv_string_sized(yytext, yyleng);
|
||||
return QQSTRING_TEXT;
|
||||
}
|
||||
. {
|
||||
return INVALID_CHARACTER;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[a-zA-Z_][a-zA-Z_0-9]* { yylval->literal = jv_string(yytext); return IDENT;}
|
||||
\.[a-zA-Z_][a-zA-Z_0-9]* { yylval->literal = jv_string(yytext+1); return FIELD;}
|
||||
|
||||
[ \n\t]+ {}
|
||||
|
||||
. { return INVALID_CHARACTER; }
|
||||
|
||||
%%
|
||||
/* perhaps these should be calls... */
|
||||
/*
|
||||
"true" { return TRUE; }
|
||||
"false" { return FALSE; }
|
||||
"null" { return NULL; }
|
||||
*/
|
||||
static int try_exit(int c, int state, yyscan_t yyscanner) {
|
||||
char match = 0;
|
||||
int ret;
|
||||
switch (state) {
|
||||
case IN_PAREN: match = ret = ')'; break;
|
||||
case IN_BRACKET: match = ret = ']'; break;
|
||||
case IN_BRACE: match = ret = '}'; break;
|
||||
|
||||
case IN_QQINTERP:
|
||||
match = ')';
|
||||
ret = QQSTRING_INTERP_END;
|
||||
break;
|
||||
|
||||
default:
|
||||
// may not be the best error to give
|
||||
return INVALID_CHARACTER;
|
||||
}
|
||||
assert(match);
|
||||
if (match == c) {
|
||||
yy_pop_state(yyscanner);
|
||||
return ret;
|
||||
} else {
|
||||
// FIXME: should we pop? Give a better error at least
|
||||
return INVALID_CHARACTER;
|
||||
}
|
||||
}
|
||||
|
||||
static int enter(int c, int currstate, yyscan_t yyscanner) {
|
||||
int state = 0;
|
||||
switch (c) {
|
||||
case '(': state = IN_PAREN; break;
|
||||
case '[': state = IN_BRACKET; break;
|
||||
case '{': state = IN_BRACE; break;
|
||||
case QQSTRING_INTERP_START: state = IN_QQINTERP; break;
|
||||
}
|
||||
assert(state);
|
||||
yy_push_state(state, yyscanner);
|
||||
return c;
|
||||
}
|
||||
|
||||
void* yyalloc(size_t sz, void* extra) {
|
||||
return jv_mem_alloc(sz);
|
||||
}
|
||||
void* yyrealloc(void* p, size_t sz, void* extra) {
|
||||
return jv_mem_realloc(p, sz);
|
||||
}
|
||||
void yyfree(void* p, void* extra) {
|
||||
jv_mem_free(p);
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include "jv_alloc.h"
|
||||
#include "locfile.h"
|
||||
|
||||
|
||||
void locfile_init(struct locfile* l, const char* data, int length) {
|
||||
l->data = data;
|
||||
l->length = length;
|
||||
l->nlines = 1;
|
||||
for (int i=0; i<length; i++) {
|
||||
if (data[i] == '\n') l->nlines++;
|
||||
}
|
||||
l->linemap = jv_mem_alloc(sizeof(int) * (l->nlines + 1));
|
||||
l->linemap[0] = 0;
|
||||
int line = 1;
|
||||
for (int i=0; i<length; i++) {
|
||||
if (data[i] == '\n') {
|
||||
l->linemap[line] = i+1; // at start of line, not of \n
|
||||
line++;
|
||||
}
|
||||
}
|
||||
l->linemap[l->nlines] = length+1; // virtual last \n
|
||||
}
|
||||
|
||||
void locfile_free(struct locfile* l) {
|
||||
jv_mem_free(l->linemap);
|
||||
}
|
||||
|
||||
static int locfile_get_line(struct locfile* l, int pos) {
|
||||
assert(pos < l->length);
|
||||
int line = 1;
|
||||
while (l->linemap[line] <= pos) line++; // == if pos at start (before, never ==, because pos never on \n)
|
||||
assert(line-1 < l->nlines);
|
||||
return line-1;
|
||||
}
|
||||
|
||||
static int locfile_line_length(struct locfile* l, int line) {
|
||||
assert(line < l->nlines);
|
||||
return l->linemap[line+1] - l->linemap[line] -1; // -1 to omit \n
|
||||
}
|
||||
|
||||
void locfile_locate(struct locfile* l, location loc, const char* fmt, ...) {
|
||||
va_list fmtargs;
|
||||
va_start(fmtargs, fmt);
|
||||
vfprintf(stderr, fmt, fmtargs);
|
||||
va_end(fmtargs);
|
||||
fprintf(stderr, "\n");
|
||||
if (loc.start == -1) {
|
||||
fprintf(stderr, "<unknown location>\n");
|
||||
return;
|
||||
}
|
||||
int startline = locfile_get_line(l, loc.start);
|
||||
int offset = l->linemap[startline];
|
||||
fprintf(stderr, "%.*s\n", locfile_line_length(l, startline), l->data + offset);
|
||||
fprintf(stderr, "%*s", loc.start - offset, "");
|
||||
for (int i = loc.start;
|
||||
i < loc.end && i < offset + locfile_line_length(l, startline);
|
||||
i++){
|
||||
fprintf(stderr, "^");
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
#ifndef _LOCFILE_H
|
||||
#define _LOCFILE_H
|
||||
|
||||
typedef struct {
|
||||
int start, end;
|
||||
} location;
|
||||
|
||||
static const location UNKNOWN_LOCATION = {-1, -1};
|
||||
|
||||
struct locfile {
|
||||
const char* data;
|
||||
int length;
|
||||
int* linemap;
|
||||
int nlines;
|
||||
};
|
||||
|
||||
void locfile_init(struct locfile* l, const char* data, int length);
|
||||
|
||||
void locfile_free(struct locfile* l);
|
||||
|
||||
void locfile_locate(struct locfile* l, location loc, const char* fmt, ...);
|
||||
|
||||
#endif
|
||||
@@ -1,309 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <unistd.h>
|
||||
#include "compile.h"
|
||||
#include "jv.h"
|
||||
#include "jv_file.h"
|
||||
#include "jv_parse.h"
|
||||
#include "execute.h"
|
||||
#include "config.h" /* Autoconf generated header file */
|
||||
#include "jv_alloc.h"
|
||||
#include "version.h"
|
||||
|
||||
int jq_testsuite(int argc, char* argv[]);
|
||||
|
||||
static const char* progname;
|
||||
|
||||
static void usage() {
|
||||
fprintf(stderr, "\njq - commandline JSON processor [version %s]\n", JQ_VERSION);
|
||||
fprintf(stderr, "Usage: %s [options] <jq filter> [file...]\n\n", progname);
|
||||
fprintf(stderr, "For a description of the command line options and\n");
|
||||
fprintf(stderr, "how to write jq filters (and why you might want to)\n");
|
||||
fprintf(stderr, "see the jq manpage, or the online documentation at\n");
|
||||
fprintf(stderr, "http://stedolan.github.com/jq\n\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void die() {
|
||||
fprintf(stderr, "Use %s --help for help with command-line options,\n", progname);
|
||||
fprintf(stderr, "or see the jq documentation at http://stedolan.github.com/jq\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static int isoptish(const char* text) {
|
||||
return text[0] == '-' && (text[1] == '-' || isalpha(text[1]));
|
||||
}
|
||||
|
||||
static int isoption(const char* text, char shortopt, const char* longopt) {
|
||||
if (text[0] != '-') return 0;
|
||||
if (strlen(text) == 2 && text[1] == shortopt) return 1;
|
||||
if (text[1] == '-' && !strcmp(text+2, longopt)) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum {
|
||||
SLURP = 1,
|
||||
RAW_INPUT = 2,
|
||||
PROVIDE_NULL = 4,
|
||||
|
||||
RAW_OUTPUT = 8,
|
||||
COMPACT_OUTPUT = 16,
|
||||
ASCII_OUTPUT = 32,
|
||||
COLOUR_OUTPUT = 64,
|
||||
NO_COLOUR_OUTPUT = 128,
|
||||
SORTED_OUTPUT = 256,
|
||||
|
||||
FROM_FILE = 512,
|
||||
|
||||
/* debugging only */
|
||||
DUMP_DISASM = 2048,
|
||||
};
|
||||
static int options = 0;
|
||||
|
||||
static void process(jq_state *jq, jv value, int flags) {
|
||||
jq_start(jq, value, flags);
|
||||
jv result;
|
||||
while (jv_is_valid(result = jq_next(jq))) {
|
||||
if ((options & RAW_OUTPUT) && jv_get_kind(result) == JV_KIND_STRING) {
|
||||
fwrite(jv_string_value(result), 1, jv_string_length_bytes(jv_copy(result)), stdout);
|
||||
jv_free(result);
|
||||
} else {
|
||||
int dumpopts;
|
||||
/* Disable colour by default on Windows builds as Windows
|
||||
terminals tend not to display it correctly */
|
||||
#ifdef WIN32
|
||||
dumpopts = 0;
|
||||
#else
|
||||
dumpopts = isatty(fileno(stdout)) ? JV_PRINT_COLOUR : 0;
|
||||
#endif
|
||||
if (options & SORTED_OUTPUT) dumpopts |= JV_PRINT_SORTED;
|
||||
if (!(options & COMPACT_OUTPUT)) dumpopts |= JV_PRINT_PRETTY;
|
||||
if (options & ASCII_OUTPUT) dumpopts |= JV_PRINT_ASCII;
|
||||
if (options & COLOUR_OUTPUT) dumpopts |= JV_PRINT_COLOUR;
|
||||
if (options & NO_COLOUR_OUTPUT) dumpopts &= ~JV_PRINT_COLOUR;
|
||||
jv_dump(result, dumpopts);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
jv_free(result);
|
||||
}
|
||||
|
||||
FILE* current_input;
|
||||
const char** input_filenames;
|
||||
int ninput_files;
|
||||
int next_input_idx;
|
||||
static int read_more(char* buf, size_t size) {
|
||||
while (!current_input || feof(current_input)) {
|
||||
if (current_input) {
|
||||
fclose(current_input);
|
||||
current_input = 0;
|
||||
}
|
||||
if (next_input_idx == ninput_files) {
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(input_filenames[next_input_idx], "-")) {
|
||||
current_input = stdin;
|
||||
} else {
|
||||
current_input = fopen(input_filenames[next_input_idx], "r");
|
||||
}
|
||||
if (!current_input) {
|
||||
fprintf(stderr, "%s: %s: %s\n", progname, input_filenames[next_input_idx], strerror(errno));
|
||||
}
|
||||
next_input_idx++;
|
||||
}
|
||||
|
||||
if (!fgets(buf, size, current_input)) buf[0] = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
jq_state *jq;
|
||||
int ret = 0;
|
||||
int compiled = 0;
|
||||
|
||||
if (argc) progname = argv[0];
|
||||
|
||||
if (argc > 1 && !strcmp(argv[1], "--run-tests")) {
|
||||
return jq_testsuite(argc, argv);
|
||||
}
|
||||
|
||||
jq = jq_init();
|
||||
if (jq == NULL) {
|
||||
perror("malloc");
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char* program = 0;
|
||||
input_filenames = jv_mem_alloc(sizeof(const char*) * argc);
|
||||
ninput_files = 0;
|
||||
int further_args_are_files = 0;
|
||||
int jq_flags = 0;
|
||||
jv program_arguments = jv_array();
|
||||
for (int i=1; i<argc; i++) {
|
||||
if (further_args_are_files) {
|
||||
input_filenames[ninput_files++] = argv[i];
|
||||
} else if (!strcmp(argv[i], "--")) {
|
||||
if (!program) usage();
|
||||
further_args_are_files = 1;
|
||||
} else if (!isoptish(argv[i])) {
|
||||
if (program) {
|
||||
input_filenames[ninput_files++] = argv[i];
|
||||
} else {
|
||||
program = argv[i];
|
||||
}
|
||||
} else if (isoption(argv[i], 's', "slurp")) {
|
||||
options |= SLURP;
|
||||
} else if (isoption(argv[i], 'r', "raw-output")) {
|
||||
options |= RAW_OUTPUT;
|
||||
} else if (isoption(argv[i], 'c', "compact-output")) {
|
||||
options |= COMPACT_OUTPUT;
|
||||
} else if (isoption(argv[i], 'C', "color-output")) {
|
||||
options |= COLOUR_OUTPUT;
|
||||
} else if (isoption(argv[i], 'M', "monochrome-output")) {
|
||||
options |= NO_COLOUR_OUTPUT;
|
||||
} else if (isoption(argv[i], 'a', "ascii-output")) {
|
||||
options |= ASCII_OUTPUT;
|
||||
} else if (isoption(argv[i], 'S', "sort-keys")) {
|
||||
options |= SORTED_OUTPUT;
|
||||
} else if (isoption(argv[i], 'R', "raw-input")) {
|
||||
options |= RAW_INPUT;
|
||||
} else if (isoption(argv[i], 'n', "null-input")) {
|
||||
options |= PROVIDE_NULL;
|
||||
} else if (isoption(argv[i], 'f', "from-file")) {
|
||||
options |= FROM_FILE;
|
||||
} else if (isoption(argv[i], 0, "arg")) {
|
||||
if (i >= argc - 2) {
|
||||
fprintf(stderr, "%s: --arg takes two parameters (e.g. -a varname value)\n", progname);
|
||||
die();
|
||||
}
|
||||
jv arg = jv_object();
|
||||
arg = jv_object_set(arg, jv_string("name"), jv_string(argv[i+1]));
|
||||
arg = jv_object_set(arg, jv_string("value"), jv_string(argv[i+2]));
|
||||
program_arguments = jv_array_append(program_arguments, arg);
|
||||
i += 2; // skip the next two arguments
|
||||
} else if (isoption(argv[i], 0, "argfile")) {
|
||||
if (i >= argc - 2) {
|
||||
fprintf(stderr, "%s: --argfile takes two parameters (e.g. -a varname filename)\n", progname);
|
||||
die();
|
||||
}
|
||||
jv arg = jv_object();
|
||||
arg = jv_object_set(arg, jv_string("name"), jv_string(argv[i+1]));
|
||||
jv data = jv_load_file(argv[i+2], 0);
|
||||
if (!jv_is_valid(data)) {
|
||||
data = jv_invalid_get_msg(data);
|
||||
fprintf(stderr, "%s: Bad JSON in --argfile %s %s: %s\n", progname,
|
||||
argv[i+1], argv[i+2], jv_string_value(data));
|
||||
jv_free(data);
|
||||
return 1;
|
||||
}
|
||||
if (jv_array_length(data) == 1)
|
||||
data = jv_array_get(data, 0);
|
||||
arg = jv_object_set(arg, jv_string("value"), data);
|
||||
program_arguments = jv_array_append(program_arguments, arg);
|
||||
i += 2; // skip the next two arguments
|
||||
} else if (isoption(argv[i], 0, "debug-dump-disasm")) {
|
||||
options |= DUMP_DISASM;
|
||||
} else if (isoption(argv[i], 0, "debug-trace")) {
|
||||
jq_flags |= JQ_DEBUG_TRACE;
|
||||
} else if (isoption(argv[i], 'h', "help")) {
|
||||
usage();
|
||||
} else if (isoption(argv[i], 'V', "version")) {
|
||||
printf("jq-%s\n", JQ_VERSION);
|
||||
return 0;
|
||||
} else {
|
||||
fprintf(stderr, "%s: Unknown option %s\n", progname, argv[i]);
|
||||
die();
|
||||
}
|
||||
}
|
||||
if (!program) usage();
|
||||
if (ninput_files == 0) current_input = stdin;
|
||||
|
||||
if ((options & PROVIDE_NULL) && (options & (RAW_INPUT | SLURP))) {
|
||||
fprintf(stderr, "%s: --null-input cannot be used with --raw-input or --slurp\n", progname);
|
||||
die();
|
||||
}
|
||||
|
||||
if (options & FROM_FILE) {
|
||||
jv data = jv_load_file(program, 1);
|
||||
if (!jv_is_valid(data)) {
|
||||
data = jv_invalid_get_msg(data);
|
||||
fprintf(stderr, "%s: %s\n", progname, jv_string_value(data));
|
||||
jv_free(data);
|
||||
return 1;
|
||||
}
|
||||
compiled = jq_compile_args(jq, jv_string_value(data), program_arguments);
|
||||
jv_free(data);
|
||||
} else {
|
||||
compiled = jq_compile_args(jq, program, program_arguments);
|
||||
}
|
||||
if (!compiled) return 1;
|
||||
|
||||
if (options & DUMP_DISASM) {
|
||||
jq_dump_disassembly(jq, 0);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if (options & PROVIDE_NULL) {
|
||||
process(jq, jv_null(), jq_flags);
|
||||
} else {
|
||||
jv slurped;
|
||||
if (options & SLURP) {
|
||||
if (options & RAW_INPUT) {
|
||||
slurped = jv_string("");
|
||||
} else {
|
||||
slurped = jv_array();
|
||||
}
|
||||
}
|
||||
struct jv_parser parser;
|
||||
jv_parser_init(&parser);
|
||||
char buf[4096];
|
||||
while (read_more(buf, sizeof(buf))) {
|
||||
if (options & RAW_INPUT) {
|
||||
int len = strlen(buf);
|
||||
if (len > 0) {
|
||||
if (options & SLURP) {
|
||||
slurped = jv_string_concat(slurped, jv_string(buf));
|
||||
} else {
|
||||
if (buf[len-1] == '\n') buf[len-1] = 0;
|
||||
process(jq, jv_string(buf), jq_flags);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
jv_parser_set_buf(&parser, buf, strlen(buf), !feof(stdin));
|
||||
jv value;
|
||||
while (jv_is_valid((value = jv_parser_next(&parser)))) {
|
||||
if (options & SLURP) {
|
||||
slurped = jv_array_append(slurped, value);
|
||||
} else {
|
||||
process(jq, value, jq_flags);
|
||||
}
|
||||
}
|
||||
if (jv_invalid_has_msg(jv_copy(value))) {
|
||||
jv msg = jv_invalid_get_msg(value);
|
||||
fprintf(stderr, "parse error: %s\n", jv_string_value(msg));
|
||||
jv_free(msg);
|
||||
ret = 1;
|
||||
break;
|
||||
} else {
|
||||
jv_free(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
jv_parser_free(&parser);
|
||||
if (ret != 0)
|
||||
goto out;
|
||||
if (options & SLURP) {
|
||||
process(jq, slurped, jq_flags);
|
||||
}
|
||||
}
|
||||
out:
|
||||
jv_mem_free(input_filenames);
|
||||
jq_teardown(&jq);
|
||||
return ret;
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
OP(LOADK, CONSTANT, 1, 1)
|
||||
OP(DUP, NONE, 1, 2)
|
||||
OP(DUP2, NONE, 2, 3)
|
||||
OP(POP, NONE, 1, 0)
|
||||
OP(LOADV, VARIABLE, 1, 1)
|
||||
OP(LOADVN, VARIABLE, 1, 1)
|
||||
OP(STOREV, VARIABLE, 1, 0)
|
||||
OP(INDEX, NONE, 2, 1)
|
||||
OP(EACH, NONE, 1, 1)
|
||||
OP(FORK, BRANCH, 0, 0)
|
||||
OP(JUMP, BRANCH, 0, 0)
|
||||
OP(JUMP_F,BRANCH, 1, 0)
|
||||
OP(BACKTRACK, NONE, 0, 0)
|
||||
OP(APPEND, VARIABLE,1, 0)
|
||||
OP(INSERT, NONE, 4, 2)
|
||||
OP(RANGE, VARIABLE, 1, 1)
|
||||
|
||||
OP(SUBEXP_BEGIN, NONE, 1, 2)
|
||||
OP(SUBEXP_END, NONE, 2, 2)
|
||||
|
||||
OP(PATH_BEGIN, NONE, 1, 2)
|
||||
OP(PATH_END, NONE, 2, 1)
|
||||
|
||||
OP(CALL_BUILTIN, CFUNC, -1, 1)
|
||||
|
||||
OP(CALL_JQ, UFUNC, 1, 1)
|
||||
OP(RET, NONE, 1, 1)
|
||||
|
||||
OP(CLOSURE_PARAM, DEFINITION, 0, 0)
|
||||
OP(CLOSURE_REF, CLOSURE_REF_IMM, 0, 0)
|
||||
OP(CLOSURE_CREATE, DEFINITION, 0, 0)
|
||||
OP(CLOSURE_CREATE_C, DEFINITION, 0, 0)
|
||||
@@ -1,536 +0,0 @@
|
||||
%{
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "compile.h"
|
||||
#include "jv_alloc.h"
|
||||
#define YYMALLOC jv_mem_alloc
|
||||
#define YYFREE jv_mem_free
|
||||
%}
|
||||
%code requires {
|
||||
#include "locfile.h"
|
||||
struct lexer_param;
|
||||
|
||||
#define YYLTYPE location
|
||||
#define YYLLOC_DEFAULT(Loc, Rhs, N) \
|
||||
do { \
|
||||
if (N) { \
|
||||
(Loc).start = YYRHSLOC(Rhs, 1).start; \
|
||||
(Loc).end = YYRHSLOC(Rhs, N).end; \
|
||||
} else { \
|
||||
(Loc).start = YYRHSLOC(Rhs, 0).end; \
|
||||
(Loc).end = YYRHSLOC(Rhs, 0).end; \
|
||||
} \
|
||||
} while (0)
|
||||
}
|
||||
|
||||
%locations
|
||||
%error-verbose
|
||||
%define api.pure
|
||||
%union {
|
||||
jv literal;
|
||||
block blk;
|
||||
}
|
||||
|
||||
%destructor { jv_free($$); } <literal>
|
||||
%destructor { block_free($$); } <blk>
|
||||
|
||||
%parse-param {block* answer}
|
||||
%parse-param {int* errors}
|
||||
%parse-param {struct locfile* locations}
|
||||
%parse-param {struct lexer_param* lexer_param_ptr}
|
||||
%lex-param {block* answer}
|
||||
%lex-param {int* errors}
|
||||
%lex-param {struct locfile* locations}
|
||||
%lex-param {struct lexer_param* lexer_param_ptr}
|
||||
|
||||
|
||||
%token INVALID_CHARACTER
|
||||
%token <literal> IDENT
|
||||
%token <literal> FIELD
|
||||
%token <literal> LITERAL
|
||||
%token <literal> FORMAT
|
||||
%token EQ "=="
|
||||
%token NEQ "!="
|
||||
%token DEFINEDOR "//"
|
||||
%token AS "as"
|
||||
%token DEF "def"
|
||||
%token IF "if"
|
||||
%token THEN "then"
|
||||
%token ELSE "else"
|
||||
%token ELSE_IF "elif"
|
||||
%token REDUCE "reduce"
|
||||
%token END "end"
|
||||
%token AND "and"
|
||||
%token OR "or"
|
||||
%token SETPIPE "|="
|
||||
%token SETPLUS "+="
|
||||
%token SETMINUS "-="
|
||||
%token SETMULT "*="
|
||||
%token SETDIV "/="
|
||||
%token SETDEFINEDOR "//="
|
||||
%token LESSEQ "<="
|
||||
%token GREATEREQ ">="
|
||||
|
||||
%token QQSTRING_START
|
||||
%token <literal> QQSTRING_TEXT
|
||||
%token QQSTRING_INTERP_START
|
||||
%token QQSTRING_INTERP_END
|
||||
%token QQSTRING_END
|
||||
|
||||
/* revolting hack */
|
||||
%left ';'
|
||||
%right '|'
|
||||
%left ','
|
||||
%right "//"
|
||||
%nonassoc '=' SETPIPE SETPLUS SETMINUS SETMULT SETDIV SETDEFINEDOR
|
||||
%left OR
|
||||
%left AND
|
||||
%nonassoc NEQ EQ '<' '>' LESSEQ GREATEREQ
|
||||
%left '+' '-'
|
||||
%left '*' '/'
|
||||
|
||||
|
||||
%type <blk> Exp Term MkDict MkDictPair ExpD ElseBody QQString FuncDef FuncDefs String
|
||||
%{
|
||||
#include "lexer.h"
|
||||
struct lexer_param {
|
||||
yyscan_t lexer;
|
||||
};
|
||||
#define FAIL(loc, msg) \
|
||||
do { \
|
||||
location l = loc; \
|
||||
yyerror(&l, answer, errors, locations, lexer_param_ptr, msg); \
|
||||
/*YYERROR*/; \
|
||||
} while (0)
|
||||
|
||||
void yyerror(YYLTYPE* loc, block* answer, int* errors,
|
||||
struct locfile* locations, struct lexer_param* lexer_param_ptr, const char *s){
|
||||
(*errors)++;
|
||||
locfile_locate(locations, *loc, "error: %s", s);
|
||||
}
|
||||
|
||||
int yylex(YYSTYPE* yylval, YYLTYPE* yylloc, block* answer, int* errors,
|
||||
struct locfile* locations, struct lexer_param* lexer_param_ptr) {
|
||||
yyscan_t lexer = lexer_param_ptr->lexer;
|
||||
int tok = jq_yylex(yylval, yylloc, lexer);
|
||||
if ((tok == LITERAL || tok == QQSTRING_TEXT) && !jv_is_valid(yylval->literal)) {
|
||||
jv msg = jv_invalid_get_msg(jv_copy(yylval->literal));
|
||||
if (jv_get_kind(msg) == JV_KIND_STRING) {
|
||||
FAIL(*yylloc, jv_string_value(msg));
|
||||
} else {
|
||||
FAIL(*yylloc, "Invalid literal");
|
||||
}
|
||||
jv_free(msg);
|
||||
jv_free(yylval->literal);
|
||||
yylval->literal = jv_null();
|
||||
}
|
||||
return tok;
|
||||
}
|
||||
|
||||
static block gen_dictpair(block k, block v) {
|
||||
return BLOCK(gen_subexp(k), gen_subexp(v), gen_op_simple(INSERT));
|
||||
}
|
||||
|
||||
static block gen_index(block obj, block key) {
|
||||
return BLOCK(gen_subexp(key), obj, gen_op_simple(INDEX));
|
||||
}
|
||||
|
||||
static block gen_slice_index(block obj, block start, block end) {
|
||||
block key = BLOCK(gen_subexp(gen_const(jv_object())),
|
||||
gen_subexp(gen_const(jv_string("start"))),
|
||||
gen_subexp(start),
|
||||
gen_op_simple(INSERT),
|
||||
gen_subexp(gen_const(jv_string("end"))),
|
||||
gen_subexp(end),
|
||||
gen_op_simple(INSERT));
|
||||
return BLOCK(key, obj, gen_op_simple(INDEX));
|
||||
}
|
||||
|
||||
static block gen_binop(block a, block b, int op) {
|
||||
const char* funcname = 0;
|
||||
switch (op) {
|
||||
case '+': funcname = "_plus"; break;
|
||||
case '-': funcname = "_minus"; break;
|
||||
case '*': funcname = "_multiply"; break;
|
||||
case '/': funcname = "_divide"; break;
|
||||
case EQ: funcname = "_equal"; break;
|
||||
case NEQ: funcname = "_notequal"; break;
|
||||
case '<': funcname = "_less"; break;
|
||||
case '>': funcname = "_greater"; break;
|
||||
case LESSEQ: funcname = "_lesseq"; break;
|
||||
case GREATEREQ: funcname = "_greatereq"; break;
|
||||
}
|
||||
assert(funcname);
|
||||
|
||||
return gen_call(funcname, BLOCK(gen_lambda(a), gen_lambda(b)));
|
||||
}
|
||||
|
||||
static block gen_format(block a, jv fmt) {
|
||||
return BLOCK(a, gen_call("format", BLOCK(gen_lambda(gen_const(fmt)))));
|
||||
}
|
||||
|
||||
static block gen_definedor_assign(block object, block val) {
|
||||
block tmp = gen_op_var_fresh(STOREV, "tmp");
|
||||
return BLOCK(gen_op_simple(DUP),
|
||||
val, tmp,
|
||||
gen_call("_modify", BLOCK(gen_lambda(object),
|
||||
gen_lambda(gen_definedor(gen_noop(),
|
||||
gen_op_bound(LOADV, tmp))))));
|
||||
}
|
||||
|
||||
static block gen_update(block object, block val, int optype) {
|
||||
block tmp = gen_op_var_fresh(STOREV, "tmp");
|
||||
return BLOCK(gen_op_simple(DUP),
|
||||
val,
|
||||
tmp,
|
||||
gen_call("_modify", BLOCK(gen_lambda(object),
|
||||
gen_lambda(gen_binop(gen_noop(),
|
||||
gen_op_bound(LOADV, tmp),
|
||||
optype)))));
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
%%
|
||||
TopLevel:
|
||||
Exp {
|
||||
*answer = $1;
|
||||
} |
|
||||
FuncDefs {
|
||||
*answer = $1;
|
||||
}
|
||||
|
||||
FuncDefs:
|
||||
/* empty */ {
|
||||
$$ = gen_noop();
|
||||
} |
|
||||
FuncDef FuncDefs {
|
||||
$$ = block_join($1, $2);
|
||||
}
|
||||
|
||||
Exp:
|
||||
FuncDef Exp %prec ';' {
|
||||
$$ = block_bind($1, $2, OP_IS_CALL_PSEUDO);
|
||||
} |
|
||||
|
||||
Term "as" '$' IDENT '|' Exp {
|
||||
$$ = gen_var_binding($1, jv_string_value($4), $6);
|
||||
jv_free($4);
|
||||
} |
|
||||
|
||||
"reduce" Term "as" '$' IDENT '(' Exp ';' Exp ')' {
|
||||
$$ = gen_reduce(jv_string_value($5), $2, $7, $9);
|
||||
jv_free($5);
|
||||
} |
|
||||
|
||||
"if" Exp "then" Exp ElseBody {
|
||||
$$ = gen_cond($2, $4, $5);
|
||||
} |
|
||||
"if" Exp "then" error {
|
||||
FAIL(@$, "Possibly unterminated 'if' statment");
|
||||
$$ = $2;
|
||||
} |
|
||||
|
||||
Exp '=' Exp {
|
||||
$$ = gen_call("_assign", BLOCK(gen_lambda($1), gen_lambda($3)));
|
||||
} |
|
||||
|
||||
Exp "or" Exp {
|
||||
$$ = gen_or($1, $3);
|
||||
} |
|
||||
|
||||
Exp "and" Exp {
|
||||
$$ = gen_and($1, $3);
|
||||
} |
|
||||
|
||||
Exp "//" Exp {
|
||||
$$ = gen_definedor($1, $3);
|
||||
} |
|
||||
|
||||
Exp "//=" Exp {
|
||||
$$ = gen_definedor_assign($1, $3);
|
||||
} |
|
||||
|
||||
Exp "|=" Exp {
|
||||
$$ = gen_call("_modify", BLOCK(gen_lambda($1), gen_lambda($3)));
|
||||
} |
|
||||
|
||||
Exp '|' Exp {
|
||||
$$ = block_join($1, $3);
|
||||
} |
|
||||
|
||||
Exp ',' Exp {
|
||||
$$ = gen_both($1, $3);
|
||||
} |
|
||||
|
||||
Exp '+' Exp {
|
||||
$$ = gen_binop($1, $3, '+');
|
||||
} |
|
||||
|
||||
Exp "+=" Exp {
|
||||
$$ = gen_update($1, $3, '+');
|
||||
} |
|
||||
|
||||
'-' Exp {
|
||||
$$ = BLOCK($2, gen_call("_negate", gen_noop()));
|
||||
} |
|
||||
|
||||
Exp '-' Exp {
|
||||
$$ = gen_binop($1, $3, '-');
|
||||
} |
|
||||
|
||||
Exp "-=" Exp {
|
||||
$$ = gen_update($1, $3, '-');
|
||||
} |
|
||||
|
||||
Exp '*' Exp {
|
||||
$$ = gen_binop($1, $3, '*');
|
||||
} |
|
||||
|
||||
Exp "*=" Exp {
|
||||
$$ = gen_update($1, $3, '*');
|
||||
} |
|
||||
|
||||
Exp '/' Exp {
|
||||
$$ = gen_binop($1, $3, '/');
|
||||
} |
|
||||
|
||||
Exp "/=" Exp {
|
||||
$$ = gen_update($1, $3, '/');
|
||||
} |
|
||||
|
||||
Exp "==" Exp {
|
||||
$$ = gen_binop($1, $3, EQ);
|
||||
} |
|
||||
|
||||
Exp "!=" Exp {
|
||||
$$ = gen_binop($1, $3, NEQ);
|
||||
} |
|
||||
|
||||
Exp '<' Exp {
|
||||
$$ = gen_binop($1, $3, '<');
|
||||
} |
|
||||
|
||||
Exp '>' Exp {
|
||||
$$ = gen_binop($1, $3, '>');
|
||||
} |
|
||||
|
||||
Exp "<=" Exp {
|
||||
$$ = gen_binop($1, $3, LESSEQ);
|
||||
} |
|
||||
|
||||
Exp ">=" Exp {
|
||||
$$ = gen_binop($1, $3, GREATEREQ);
|
||||
} |
|
||||
|
||||
Term {
|
||||
$$ = $1;
|
||||
}
|
||||
|
||||
FuncDef:
|
||||
"def" IDENT ':' Exp ';' {
|
||||
$$ = gen_function(jv_string_value($2), gen_noop(), $4);
|
||||
jv_free($2);
|
||||
} |
|
||||
|
||||
"def" IDENT '(' IDENT ')' ':' Exp ';' {
|
||||
$$ = gen_function(jv_string_value($2),
|
||||
gen_param(jv_string_value($4)),
|
||||
$7);
|
||||
jv_free($2);
|
||||
jv_free($4);
|
||||
} |
|
||||
|
||||
"def" IDENT '(' IDENT ';' IDENT ')' ':' Exp ';' {
|
||||
$$ = gen_function(jv_string_value($2),
|
||||
BLOCK(gen_param(jv_string_value($4)),
|
||||
gen_param(jv_string_value($6))),
|
||||
$9);
|
||||
jv_free($2);
|
||||
jv_free($4);
|
||||
jv_free($6);
|
||||
}
|
||||
|
||||
|
||||
|
||||
String:
|
||||
QQSTRING_START { $<literal>$ = jv_string("text"); } QQString QQSTRING_END {
|
||||
$$ = $3;
|
||||
jv_free($<literal>2);
|
||||
} |
|
||||
FORMAT QQSTRING_START { $<literal>$ = $1; } QQString QQSTRING_END {
|
||||
$$ = $4;
|
||||
jv_free($<literal>3);
|
||||
}
|
||||
|
||||
|
||||
QQString:
|
||||
/* empty */ {
|
||||
$$ = gen_const(jv_string(""));
|
||||
} |
|
||||
QQString QQSTRING_TEXT {
|
||||
$$ = gen_binop($1, gen_const($2), '+');
|
||||
} |
|
||||
QQString QQSTRING_INTERP_START Exp QQSTRING_INTERP_END {
|
||||
$$ = gen_binop($1, gen_format($3, jv_copy($<literal>0)), '+');
|
||||
}
|
||||
|
||||
|
||||
ElseBody:
|
||||
"elif" Exp "then" Exp ElseBody {
|
||||
$$ = gen_cond($2, $4, $5);
|
||||
} |
|
||||
"else" Exp "end" {
|
||||
$$ = $2;
|
||||
}
|
||||
|
||||
ExpD:
|
||||
ExpD '|' ExpD {
|
||||
$$ = block_join($1, $3);
|
||||
} |
|
||||
|
||||
Term {
|
||||
$$ = $1;
|
||||
}
|
||||
|
||||
|
||||
Term:
|
||||
'.' {
|
||||
$$ = gen_noop();
|
||||
} |
|
||||
Term FIELD {
|
||||
$$ = gen_index($1, gen_const($2));
|
||||
} |
|
||||
FIELD {
|
||||
$$ = gen_index(gen_noop(), gen_const($1));
|
||||
} |
|
||||
Term '.' String {
|
||||
$$ = gen_index($1, $3);
|
||||
} |
|
||||
'.' String {
|
||||
$$ = gen_index(gen_noop(), $2);
|
||||
} |
|
||||
'.' error {
|
||||
FAIL(@$, "try .[\"field\"] instead of .field for unusually named fields");
|
||||
$$ = gen_noop();
|
||||
} |
|
||||
'.' IDENT error {
|
||||
jv_free($2);
|
||||
FAIL(@$, "try .[\"field\"] instead of .field for unusually named fields");
|
||||
$$ = gen_noop();
|
||||
} |
|
||||
/* FIXME: string literals */
|
||||
Term '[' Exp ']' {
|
||||
$$ = gen_index($1, $3);
|
||||
} |
|
||||
Term '[' ']' {
|
||||
$$ = block_join($1, gen_op_simple(EACH));
|
||||
} |
|
||||
Term '[' Exp ':' Exp ']' {
|
||||
$$ = gen_slice_index($1, $3, $5);
|
||||
} |
|
||||
Term '[' Exp ':' ']' {
|
||||
$$ = gen_slice_index($1, $3, gen_const(jv_null()));
|
||||
} |
|
||||
Term '[' ':' Exp ']' {
|
||||
$$ = gen_slice_index($1, gen_const(jv_null()), $4);
|
||||
} |
|
||||
LITERAL {
|
||||
$$ = gen_const($1);
|
||||
} |
|
||||
String {
|
||||
$$ = $1;
|
||||
} |
|
||||
FORMAT {
|
||||
$$ = gen_format(gen_noop(), $1);
|
||||
} |
|
||||
'(' Exp ')' {
|
||||
$$ = $2;
|
||||
} |
|
||||
'[' Exp ']' {
|
||||
$$ = gen_collect($2);
|
||||
} |
|
||||
'[' ']' {
|
||||
$$ = gen_const(jv_array());
|
||||
} |
|
||||
'{' MkDict '}' {
|
||||
$$ = BLOCK(gen_subexp(gen_const(jv_object())), $2, gen_op_simple(POP));
|
||||
} |
|
||||
'$' IDENT {
|
||||
$$ = gen_location(@$, gen_op_unbound(LOADV, jv_string_value($2)));
|
||||
jv_free($2);
|
||||
} |
|
||||
IDENT {
|
||||
$$ = gen_location(@$, gen_call(jv_string_value($1), gen_noop()));
|
||||
jv_free($1);
|
||||
} |
|
||||
IDENT '(' Exp ')' {
|
||||
$$ = gen_call(jv_string_value($1), gen_lambda($3));
|
||||
$$ = gen_location(@1, $$);
|
||||
jv_free($1);
|
||||
} |
|
||||
IDENT '(' Exp ';' Exp ')' {
|
||||
$$ = gen_call(jv_string_value($1), BLOCK(gen_lambda($3), gen_lambda($5)));
|
||||
$$ = gen_location(@1, $$);
|
||||
jv_free($1);
|
||||
} |
|
||||
'(' error ')' { $$ = gen_noop(); } |
|
||||
'[' error ']' { $$ = gen_noop(); } |
|
||||
Term '[' error ']' { $$ = $1; } |
|
||||
'{' error '}' { $$ = gen_noop(); }
|
||||
|
||||
MkDict:
|
||||
{
|
||||
$$=gen_noop();
|
||||
} |
|
||||
MkDictPair { $$ = $1; }
|
||||
| MkDictPair ',' MkDict { $$=block_join($1, $3); }
|
||||
| error ',' MkDict { $$ = $3; }
|
||||
|
||||
MkDictPair
|
||||
: IDENT ':' ExpD {
|
||||
$$ = gen_dictpair(gen_const($1), $3);
|
||||
}
|
||||
| String ':' ExpD {
|
||||
$$ = gen_dictpair($1, $3);
|
||||
}
|
||||
| String {
|
||||
$$ = gen_dictpair($1, BLOCK(gen_op_simple(POP), gen_op_simple(DUP2),
|
||||
gen_op_simple(DUP2), gen_op_simple(INDEX)));
|
||||
}
|
||||
| IDENT {
|
||||
$$ = gen_dictpair(gen_const(jv_copy($1)),
|
||||
gen_index(gen_noop(), gen_const($1)));
|
||||
}
|
||||
| '(' Exp ')' ':' ExpD {
|
||||
$$ = gen_dictpair($2, $5);
|
||||
}
|
||||
| '(' error ')' ':' ExpD { $$ = $5; }
|
||||
%%
|
||||
|
||||
int jq_parse(struct locfile* locations, block* answer) {
|
||||
struct lexer_param scanner;
|
||||
YY_BUFFER_STATE buf;
|
||||
jq_yylex_init_extra(0, &scanner.lexer);
|
||||
buf = jq_yy_scan_bytes(locations->data, locations->length, scanner.lexer);
|
||||
int errors = 0;
|
||||
*answer = gen_noop();
|
||||
yyparse(answer, &errors, locations, &scanner);
|
||||
jq_yy_delete_buffer(buf, scanner.lexer);
|
||||
jq_yylex_destroy(scanner.lexer);
|
||||
if (errors > 0) {
|
||||
block_free(*answer);
|
||||
*answer = gen_noop();
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
|
||||
int jq_parse_library(struct locfile* locations, block* answer) {
|
||||
int errs = jq_parse(locations, answer);
|
||||
if (errs) return errs;
|
||||
if (!block_has_only_binders(*answer, OP_IS_CALL_PSEUDO)) {
|
||||
locfile_locate(locations, UNKNOWN_LOCATION, "error: library should only have function definitions, not a main expression");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This script is used to cross-compile binaries for
|
||||
# platforms other than the current one
|
||||
|
||||
# Usage: $0 <platformname> <configure options>
|
||||
# <platformname> is arbitrary, it is the name
|
||||
# of the directory which will be created to contain
|
||||
# the output binaries.
|
||||
|
||||
# e.g. $0 win32 --host=i686-w64-mingw32
|
||||
|
||||
set -e
|
||||
cd `dirname "$0"`/../build
|
||||
|
||||
plat="$1"
|
||||
[ -z "$plat" ] && exit 1
|
||||
shift
|
||||
|
||||
[ -d "$plat" ] || mkdir "$plat"
|
||||
rm -rf "$plat/tmp"
|
||||
mkdir "$plat/tmp"
|
||||
cd "$plat/tmp"
|
||||
mkdir install_other
|
||||
../../../configure \
|
||||
--prefix="`pwd`/install_other" \
|
||||
--bindir="`pwd`/.." \
|
||||
"$@"
|
||||
make install-binaries
|
||||
cd ..
|
||||
rm -rf tmp
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
cd `dirname "$0"`
|
||||
git rev-parse --verify -q jq-1.0 > /dev/null 2>&1
|
||||
git describe --always --match 'jq-*' --dirty | sed 's/^jq-//'
|
||||
@@ -1,32 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# RUN ME after pulling the code from git!
|
||||
#
|
||||
# This script is handy after pulling the code out of git without having
|
||||
# to remember all the typing needed to build up and tear down all the
|
||||
# crap that autotools puts in your source repo. The intent is to keep
|
||||
# all generated files out of git.
|
||||
|
||||
if [ "clean" == "$1" ]; then
|
||||
make distclean || echo "continuing..."
|
||||
rm -rf tmp autom4te.cache config
|
||||
rm -f INSTALL Makefile.in aclocal.m4 configure config.h.in ChangeLog
|
||||
rm -f jv_utf8_tables.gen.h lexer.c lexer.h parser.c parser.h
|
||||
elif [ "superclean" == "$1" ]; then
|
||||
# if autoconf errors during distcheck, it leaves files that need chmod'ing
|
||||
ver=$(tr -d '\n' <VERSION)
|
||||
if [ "x${ver}" != "x" ]; then
|
||||
if [ -d jq-${ver} ]; then
|
||||
chmod -R u+w jq-${ver}
|
||||
rm -rf jq-${ver}
|
||||
fi
|
||||
rm -f jq-${ver}*
|
||||
fi
|
||||
else
|
||||
autoreconf --install
|
||||
./configure --prefix=/opt/junk
|
||||
make check
|
||||
mkdir tmp
|
||||
make DESTDIR=./tmp install
|
||||
make distcheck
|
||||
fi
|
||||
@@ -0,0 +1,11 @@
|
||||
def map(f) = [.[] | f];
|
||||
|
||||
def first = .[0];
|
||||
# def last = .[count-1];
|
||||
def next = .[count];
|
||||
|
||||
# ([])[] would be a decent definition of "empty"
|
||||
# except ([]) is defined as syntactic sugar for empty
|
||||
def empty = {}[];
|
||||
|
||||
def sort = [group(.) | .[]];
|
||||
-606
@@ -1,606 +0,0 @@
|
||||
# Tests are groups of three lines: program, input, expected output
|
||||
# Blank lines and lines starting with # are ignored
|
||||
|
||||
#
|
||||
# Simple value tests to check parser. Input is irrelevant
|
||||
#
|
||||
|
||||
true
|
||||
null
|
||||
true
|
||||
|
||||
false
|
||||
null
|
||||
false
|
||||
|
||||
null
|
||||
42
|
||||
null
|
||||
|
||||
1
|
||||
null
|
||||
1
|
||||
|
||||
|
||||
-1
|
||||
null
|
||||
-1
|
||||
|
||||
# FIXME: much more number testing needed
|
||||
|
||||
{}
|
||||
null
|
||||
{}
|
||||
|
||||
[]
|
||||
null
|
||||
[]
|
||||
|
||||
# The input line starts with a 0xFEFF (byte order mark) codepoint
|
||||
# No, there is no reason to have a byte order mark in UTF8 text.
|
||||
# But apparently people do, so jq shouldn't break on it.
|
||||
.
|
||||
"byte order mark"
|
||||
"byte order mark"
|
||||
|
||||
# We test escapes by matching them against Unicode codepoints
|
||||
# FIXME: more tests needed for weird unicode stuff (e.g. utf16 pairs)
|
||||
"Aa\r\n\t\b\f\u03bc"
|
||||
null
|
||||
"Aa\u000d\u000a\u0009\u0008\u000c\u03bc"
|
||||
|
||||
.
|
||||
"Aa\r\n\t\b\f\u03bc"
|
||||
"Aa\u000d\u000a\u0009\u0008\u000c\u03bc"
|
||||
|
||||
"inter\("pol" + "ation")"
|
||||
null
|
||||
"interpolation"
|
||||
|
||||
@text,@json,([1,.] | @csv),@html,@uri,@sh,@base64
|
||||
"<>&'\""
|
||||
"<>&'\""
|
||||
"\"<>&'\\\"\""
|
||||
"1,\"<>&'\"\"\""
|
||||
"<>&'""
|
||||
"%3c%3e%26'%22"
|
||||
"'<>&'\\''\"'"
|
||||
"PD4mJyI="
|
||||
|
||||
@uri
|
||||
"\u03bc"
|
||||
"%ce%bc"
|
||||
|
||||
@html "<b>\(.)</b>"
|
||||
"<script>hax</script>"
|
||||
"<b><script>hax</script></b>"
|
||||
|
||||
#
|
||||
# Dictionary construction syntax
|
||||
#
|
||||
|
||||
{a: 1}
|
||||
null
|
||||
{"a":1}
|
||||
|
||||
{a,b,(.d):.a,e:.b}
|
||||
{"a":1, "b":2, "c":3, "d":"c"}
|
||||
{"a":1, "b":2, "c":1, "e":2}
|
||||
|
||||
{"a",b,"a$\(1+1)"}
|
||||
{"a":1, "b":2, "c":3, "a$2":4}
|
||||
{"a":1, "b":2, "a$2":4}
|
||||
|
||||
#
|
||||
# Field access, piping
|
||||
#
|
||||
|
||||
.foo
|
||||
{"foo": 42, "bar": 43}
|
||||
42
|
||||
|
||||
.foo | .bar
|
||||
{"foo": {"bar": 42}, "bar": "badvalue"}
|
||||
42
|
||||
|
||||
.foo.bar
|
||||
{"foo": {"bar": 42}, "bar": "badvalue"}
|
||||
42
|
||||
|
||||
.foo_bar
|
||||
{"foo_bar": 2}
|
||||
2
|
||||
|
||||
.["foo"].bar
|
||||
{"foo": {"bar": 42}, "bar": "badvalue"}
|
||||
42
|
||||
|
||||
."foo"."bar"
|
||||
{"foo": {"bar": 20}}
|
||||
20
|
||||
|
||||
|
||||
#
|
||||
# Multiple outputs, iteration
|
||||
#
|
||||
|
||||
.[]
|
||||
[1,2,3]
|
||||
1
|
||||
2
|
||||
3
|
||||
|
||||
1,1
|
||||
[]
|
||||
1
|
||||
1
|
||||
|
||||
1,.
|
||||
[]
|
||||
1
|
||||
[]
|
||||
|
||||
[.]
|
||||
[2]
|
||||
[[2]]
|
||||
|
||||
[[2]]
|
||||
[3]
|
||||
[[2]]
|
||||
|
||||
[{}]
|
||||
[2]
|
||||
[{}]
|
||||
|
||||
[.[]]
|
||||
["a"]
|
||||
["a"]
|
||||
|
||||
[(.,1),((.,.[]),(2,3))]
|
||||
["a","b"]
|
||||
[["a","b"],1,["a","b"],"a","b",2,3]
|
||||
|
||||
[([5,5][]),.,.[]]
|
||||
[1,2,3]
|
||||
[5,5,[1,2,3],1,2,3]
|
||||
|
||||
{x: (1,2)},{x:3} | .x
|
||||
null
|
||||
1
|
||||
2
|
||||
3
|
||||
|
||||
#
|
||||
# Slices
|
||||
#
|
||||
|
||||
[.[3:2], .[-5:4], .[:-2], .[-2:], .[3:3][1:], .[10:]]
|
||||
[0,1,2,3,4,5,6]
|
||||
[[], [2,3], [0,1,2,3,4], [5,6], [], []]
|
||||
|
||||
del(.[2:4],.[0],.[-2:])
|
||||
[0,1,2,3,4,5,6,7]
|
||||
[1,4,5]
|
||||
|
||||
.[2:4] = ([], ["a","b"], ["a","b","c"])
|
||||
[0,1,2,3,4,5,6,7]
|
||||
[0,1,4,5,6,7]
|
||||
[0,1,"a","b",4,5,6,7]
|
||||
[0,1,"a","b","c",4,5,6,7]
|
||||
|
||||
|
||||
#
|
||||
# Variables
|
||||
#
|
||||
|
||||
1 as $x | 2 as $y | [$x,$y,$x]
|
||||
null
|
||||
[1,2,1]
|
||||
|
||||
[1,2,3][] as $x | [[4,5,6,7][$x]]
|
||||
null
|
||||
[5]
|
||||
[6]
|
||||
[7]
|
||||
|
||||
42 as $x | . | . | . + 432 | $x + 1
|
||||
34324
|
||||
43
|
||||
|
||||
1 as $x | [$x,$x,$x as $x | $x]
|
||||
null
|
||||
[1,1,1]
|
||||
|
||||
# [.,(.[] | {x:.},.),.,.[]]
|
||||
|
||||
#
|
||||
# Builtin functions
|
||||
#
|
||||
|
||||
1+1
|
||||
null
|
||||
2
|
||||
|
||||
1+1
|
||||
"wtasdf"
|
||||
2.0
|
||||
|
||||
2-1
|
||||
null
|
||||
1
|
||||
|
||||
2-(-1)
|
||||
null
|
||||
3
|
||||
|
||||
1e+0+0.001e3
|
||||
"I wonder what this will be?"
|
||||
20e-1
|
||||
|
||||
.+4
|
||||
15
|
||||
19.0
|
||||
|
||||
.+null
|
||||
{"a":42}
|
||||
{"a":42}
|
||||
|
||||
null+.
|
||||
null
|
||||
null
|
||||
|
||||
.a+.b
|
||||
{"a":42}
|
||||
42
|
||||
|
||||
[1,2,3] + [.]
|
||||
null
|
||||
[1,2,3,null]
|
||||
|
||||
{"a":1} + {"b":2} + {"c":3}
|
||||
"asdfasdf"
|
||||
{"a":1, "b":2, "c":3}
|
||||
|
||||
"asdf" + "jkl;" + . + . + .
|
||||
"some string"
|
||||
"asdfjkl;some stringsome stringsome string"
|
||||
|
||||
"\u0000\u0020\u0000" + .
|
||||
"\u0000\u0020\u0000"
|
||||
"\u0000 \u0000\u0000 \u0000"
|
||||
|
||||
42 - .
|
||||
11
|
||||
31
|
||||
|
||||
[1,2,3,4,1] - [.,3]
|
||||
1
|
||||
[2,4]
|
||||
|
||||
[10 * 20, 20 / .]
|
||||
4
|
||||
[200, 5]
|
||||
|
||||
1 + 2 * 2 + 10 / 2
|
||||
null
|
||||
10
|
||||
|
||||
[16 / 4 / 2, 16 / 4 * 2, 16 - 4 - 2, 16 - 4 + 2]
|
||||
null
|
||||
[2, 8, 10, 14]
|
||||
|
||||
1 + tonumber + ("10" | tonumber)
|
||||
4
|
||||
15
|
||||
|
||||
[{"a":42},.object,10,.num,false,true,null,"b",[1,4]] | .[] as $x | [$x == .[]]
|
||||
{"object": {"a":42}, "num":10.0}
|
||||
[true, true, false, false, false, false, false, false, false]
|
||||
[true, true, false, false, false, false, false, false, false]
|
||||
[false, false, true, true, false, false, false, false, false]
|
||||
[false, false, true, true, false, false, false, false, false]
|
||||
[false, false, false, false, true, false, false, false, false]
|
||||
[false, false, false, false, false, true, false, false, false]
|
||||
[false, false, false, false, false, false, true, false, false]
|
||||
[false, false, false, false, false, false, false, true, false]
|
||||
[false, false, false, false, false, false, false, false, true ]
|
||||
|
||||
[.[] | length]
|
||||
[[], {}, [1,2], {"a":42}, "asdf", "\u03bc"]
|
||||
[0, 0, 2, 1, 4, 1]
|
||||
|
||||
map(keys)
|
||||
[{}, {"abcd":1,"abc":2,"abcde":3}, {"x":1, "z": 3, "y":2}]
|
||||
[[], ["abc","abcd","abcde"], ["x","y","z"]]
|
||||
|
||||
[1,2,empty,3,empty,4]
|
||||
null
|
||||
[1,2,3,4]
|
||||
|
||||
map(add)
|
||||
[[], [1,2,3], ["a","b","c"], [[3],[4,5],[6]], [{"a":1}, {"b":2}, {"a":3}]]
|
||||
[null, 6, "abc", [3,4,5,6], {"a":3, "b": 2}]
|
||||
|
||||
#
|
||||
# User-defined functions
|
||||
# Oh god.
|
||||
#
|
||||
|
||||
def f: . + 1; def g: def g: . + 100; f | g | f; (f | g), g
|
||||
3.0
|
||||
106.0
|
||||
105.0
|
||||
|
||||
def f: (1000,2000); f
|
||||
123412345
|
||||
1000
|
||||
2000
|
||||
|
||||
([1,2] + [4,5])
|
||||
[1,2,3]
|
||||
[1,2,4,5]
|
||||
|
||||
true
|
||||
[1]
|
||||
true
|
||||
|
||||
null,1,null
|
||||
"hello"
|
||||
null
|
||||
1
|
||||
null
|
||||
|
||||
[1,2,3]
|
||||
[5,6]
|
||||
[1,2,3]
|
||||
|
||||
def f(x): x | x; f([.], . + [42])
|
||||
[1,2,3]
|
||||
[[[1,2,3]]]
|
||||
[[1,2,3],42]
|
||||
[[1,2,3,42]]
|
||||
[1,2,3,42,42]
|
||||
|
||||
# test closures and lexical scoping
|
||||
def id(x):x; 2000 as $x | def f(x):1 as $x | id([$x, x, x]); def g(x): 100 as $x | f($x,$x+x); g($x)
|
||||
"more testing"
|
||||
[1,100,2100.0,100,2100.0]
|
||||
|
||||
# test backtracking through function calls and returns
|
||||
# this test is *evil*
|
||||
[[20,10][1,0] as $x | def f: (100,200) as $y | def g: [$x + $y, .]; . + $x | g; f[0] | [f][0][1] | f]
|
||||
999999999
|
||||
[[110.0, 130.0], [210.0, 130.0], [110.0, 230.0], [210.0, 230.0], [120.0, 160.0], [220.0, 160.0], [120.0, 260.0], [220.0, 260.0]]
|
||||
|
||||
# test recursion
|
||||
def fac: if . == 1 then 1 else . * (. - 1 | fac) end; [.[] | fac]
|
||||
[1,2,3,4]
|
||||
[1,2,6,24]
|
||||
|
||||
# test stack overflow and reallocation
|
||||
# this test is disabled for now, it takes a realllllly long time.
|
||||
# def f: if length > 1000 then . else .+[1]|f end; f | length
|
||||
# []
|
||||
# 1001
|
||||
|
||||
reduce .[] as $x (0; . + $x)
|
||||
[1,2,4]
|
||||
7
|
||||
|
||||
#
|
||||
# Paths
|
||||
#
|
||||
|
||||
path(.foo[0,1])
|
||||
null
|
||||
["foo", 0]
|
||||
["foo", 1]
|
||||
|
||||
path(.[] | select(.>3))
|
||||
[1,5,3]
|
||||
[1]
|
||||
|
||||
path(.)
|
||||
42
|
||||
[]
|
||||
|
||||
["foo",1] as $p | getpath($p), setpath($p; 20), delpaths([$p])
|
||||
{"bar": 42, "foo": ["a", "b", "c", "d"]}
|
||||
"b"
|
||||
{"bar": 42, "foo": ["a", 20, "c", "d"]}
|
||||
{"bar": 42, "foo": ["a", "c", "d"]}
|
||||
|
||||
map(getpath([2])), map(setpath([2]; 42)), map(delpaths([[2]]))
|
||||
[[0], [0,1], [0,1,2]]
|
||||
[null, null, 2]
|
||||
[[0,null,42], [0,1,42], [0,1,42]]
|
||||
[[0], [0,1], [0,1]]
|
||||
|
||||
map(delpaths([[0,"foo"]]))
|
||||
[[{"foo":2, "x":1}], [{"bar":2}]]
|
||||
[[{"x":1}], [{"bar":2}]]
|
||||
|
||||
["foo",1] as $p | getpath($p), setpath($p; 20), delpaths([$p])
|
||||
{"bar":false}
|
||||
null
|
||||
{"bar":false, "foo": [null, 20]}
|
||||
{"bar":false}
|
||||
|
||||
delpaths([[-200]])
|
||||
[1,2,3]
|
||||
[1,2,3]
|
||||
|
||||
del(.), del(empty), del((.foo,.bar,.baz) | .[2,3,0]), del(.foo[0], .bar[0], .foo, .baz.bar[0].x)
|
||||
{"foo": [0,1,2,3,4], "bar": [0,1]}
|
||||
null
|
||||
{"foo": [0,1,2,3,4], "bar": [0,1]}
|
||||
{"foo": [1,4], "bar": [1]}
|
||||
{"bar": [1]}
|
||||
|
||||
#
|
||||
# Assignment
|
||||
#
|
||||
.message = "goodbye"
|
||||
{"message": "hello"}
|
||||
{"message": "goodbye"}
|
||||
|
||||
.foo = .bar
|
||||
{"bar":42}
|
||||
{"foo":42, "bar":42}
|
||||
|
||||
.foo |= .+1
|
||||
{"foo": 42}
|
||||
{"foo": 43}
|
||||
|
||||
.[] += 2, .[] *= 2, .[] -= 2, .[] /= 2
|
||||
[1,3,5]
|
||||
[3,5,7]
|
||||
[2,6,10]
|
||||
[-1,1,3]
|
||||
[0.5, 1.5, 2.5]
|
||||
|
||||
.foo += .foo
|
||||
{"foo":2}
|
||||
{"foo":4}
|
||||
|
||||
.[0].a |= {"old":., "new":(.+1)}
|
||||
[{"a":1,"b":2}]
|
||||
[{"a":{"old":1, "new":2},"b":2}]
|
||||
|
||||
def inc(x): x |= .+1; inc(.[].a)
|
||||
[{"a":1,"b":2},{"a":2,"b":4},{"a":7,"b":8}]
|
||||
[{"a":2,"b":2},{"a":3,"b":4},{"a":8,"b":8}]
|
||||
|
||||
.[2][3] = 1
|
||||
[4]
|
||||
[4, null, [null, null, null, 1]]
|
||||
|
||||
.foo[2].bar = 1
|
||||
{"foo":[11], "bar":42}
|
||||
{"foo":[11,null,{"bar":1}], "bar":42}
|
||||
|
||||
#
|
||||
# Conditionals
|
||||
#
|
||||
|
||||
[.[] | if .foo then "yep" else "nope" end]
|
||||
[{"foo":0},{"foo":1},{"foo":[]},{"foo":true},{"foo":false},{"foo":null},{"foo":"foo"},{}]
|
||||
["yep","yep","yep","yep","nope","nope","yep","nope"]
|
||||
|
||||
[.[] | if .baz then "strange" elif .foo then "yep" else "nope" end]
|
||||
[{"foo":0},{"foo":1},{"foo":[]},{"foo":true},{"foo":false},{"foo":null},{"foo":"foo"},{}]
|
||||
["yep","yep","yep","yep","nope","nope","yep","nope"]
|
||||
|
||||
|
||||
# FIXME: define/test behaviour of 'if (.foo,.bar) then A else B end'
|
||||
|
||||
[.[] | [.foo[] // .bar]]
|
||||
[{"foo":[1,2], "bar": 42}, {"foo":[1], "bar": null}, {"foo":[null,false,3], "bar": 18}, {"foo":[], "bar":42}, {"foo": [null,false,null], "bar": 41}]
|
||||
[[1,2], [1], [3], [42], [41]]
|
||||
|
||||
.[] //= .[0]
|
||||
["hello",true,false,[false],null]
|
||||
["hello",true,"hello",[false],"hello"]
|
||||
|
||||
.[] | [.[0] and .[1], .[0] or .[1]]
|
||||
[[true,[]], [false,1], [42,null], [null,false]]
|
||||
[true,true]
|
||||
[false,true]
|
||||
[false,true]
|
||||
[false,false]
|
||||
|
||||
[.[] | not]
|
||||
[1,0,false,null,true,"hello"]
|
||||
[false,false,true,true,false,false]
|
||||
|
||||
# Check numeric comparison binops
|
||||
[10 > 0, 10 > 10, 10 > 20, 10 < 0, 10 < 10, 10 < 20]
|
||||
{}
|
||||
[true,false,false,false,false,true]
|
||||
|
||||
[10 >= 0, 10 >= 10, 10 >= 20, 10 <= 0, 10 <= 10, 10 <= 20]
|
||||
{}
|
||||
[true,true,false,false,true,true]
|
||||
|
||||
# And some in/equality tests
|
||||
[ 10 == 10, 10 != 10, 10 != 11, 10 == 11]
|
||||
{}
|
||||
[true,false,true,false]
|
||||
|
||||
["hello" == "hello", "hello" != "hello", "hello" == "world", "hello" != "world" ]
|
||||
{}
|
||||
[true,false,false,true]
|
||||
|
||||
[[1,2,3] == [1,2,3], [1,2,3] != [1,2,3], [1,2,3] == [4,5,6], [1,2,3] != [4,5,6]]
|
||||
{}
|
||||
[true,false,false,true]
|
||||
|
||||
[{"foo":42} == {"foo":42},{"foo":42} != {"foo":42}, {"foo":42} != {"bar":42}, {"foo":42} == {"bar":42}]
|
||||
{}
|
||||
[true,false,true,false]
|
||||
|
||||
# ugly complicated thing
|
||||
[{"foo":[1,2,{"bar":18},"world"]} == {"foo":[1,2,{"bar":18},"world"]},{"foo":[1,2,{"bar":18},"world"]} == {"foo":[1,2,{"bar":19},"world"]}]
|
||||
{}
|
||||
[true,false]
|
||||
|
||||
# containment operator
|
||||
[("foo" | contains("foo")), ("foobar" | contains("foo")), ("foo" | contains("foobar"))]
|
||||
{}
|
||||
[true, true, false]
|
||||
|
||||
map(.[1] as $needle | .[0] | contains($needle))
|
||||
[[[],[]], [[1,2,3], [1,2]], [[1,2,3], [3,1]], [[1,2,3], [4]], [[1,2,3], [1,4]]]
|
||||
[true, true, true, false, false]
|
||||
|
||||
map(.[1] as $needle | .[0] | contains($needle))
|
||||
[[["foobar", "foobaz"], ["baz", "bar"]], [["foobar", "foobaz"], ["foo"]], [["foobar", "foobaz"], ["blap"]]]
|
||||
[true, true, false]
|
||||
|
||||
[({foo: 12, bar:13} | contains({foo: 12})), ({foo: 12} | contains({})), ({foo: 12, bar:13} | contains({baz:14}))]
|
||||
{}
|
||||
[true, true, false]
|
||||
|
||||
{foo: {baz: 12, blap: {bar: 13}}, bar: 14} | contains({bar: 14, foo: {blap: {}}})
|
||||
{}
|
||||
true
|
||||
|
||||
{foo: {baz: 12, blap: {bar: 13}}, bar: 14} | contains({bar: 14, foo: {blap: {bar: 14}}})
|
||||
{}
|
||||
false
|
||||
|
||||
sort
|
||||
[42,[2,5,3,11],10,{"a":42,"b":2},{"a":42},true,2,[2,6],"hello",null,[2,5,6],{"a":[],"b":1},"abc","ab",[3,10],{},false,"abcd",null]
|
||||
[null,null,false,true,2,10,42,"ab","abc","abcd","hello",[2,5,3,11],[2,5,6],[2,6],[3,10],{},{"a":42},{"a":42,"b":2},{"a":[],"b":1}]
|
||||
|
||||
(sort_by(.b) | sort_by(.a)), sort_by(.a, .b), sort_by(.b, .c), group_by(.b), group_by(.a + .b - .c == 2)
|
||||
[{"a": 1, "b": 4, "c": 14}, {"a": 4, "b": 1, "c": 3}, {"a": 1, "b": 4, "c": 3}, {"a": 0, "b": 2, "c": 43}]
|
||||
[{"a": 0, "b": 2, "c": 43}, {"a": 1, "b": 4, "c": 14}, {"a": 1, "b": 4, "c": 3}, {"a": 4, "b": 1, "c": 3}]
|
||||
[{"a": 0, "b": 2, "c": 43}, {"a": 1, "b": 4, "c": 14}, {"a": 1, "b": 4, "c": 3}, {"a": 4, "b": 1, "c": 3}]
|
||||
[{"a": 4, "b": 1, "c": 3}, {"a": 0, "b": 2, "c": 43}, {"a": 1, "b": 4, "c": 3}, {"a": 1, "b": 4, "c": 14}]
|
||||
[[{"a": 4, "b": 1, "c": 3}], [{"a": 0, "b": 2, "c": 43}], [{"a": 1, "b": 4, "c": 14}, {"a": 1, "b": 4, "c": 3}]]
|
||||
[[{"a": 1, "b": 4, "c": 14}, {"a": 0, "b": 2, "c": 43}], [{"a": 4, "b": 1, "c": 3}, {"a": 1, "b": 4, "c": 3}]]
|
||||
|
||||
unique
|
||||
[1,2,5,3,5,3,1,3]
|
||||
[1,2,3,5]
|
||||
|
||||
[min, max, min_by(.[1]), max_by(.[1]), min_by(.[2]), max_by(.[2])]
|
||||
[[4,2,"a"],[3,1,"a"],[2,4,"a"],[1,3,"a"]]
|
||||
[[1,3,"a"],[4,2,"a"],[3,1,"a"],[2,4,"a"],[4,2,"a"],[1,3,"a"]]
|
||||
|
||||
[min,max,min_by(.),max_by(.)]
|
||||
[]
|
||||
[null,null,null,null]
|
||||
|
||||
.foo[.baz]
|
||||
{"foo":{"bar":4},"baz":"bar"}
|
||||
4
|
||||
|
||||
.[] | .error = "no, it's OK"
|
||||
[{"error":true}]
|
||||
{"error": "no, it's OK"}
|
||||
|
||||
[{a:1}] | .[] | .a=999
|
||||
null
|
||||
{"a": 999}
|
||||
Reference in new issue
Block a user