├── .gitignore ├── group.ebnf ├── shadow-cljs.edn ├── passwd.ebnf ├── src └── parseit │ ├── misc.cljs │ ├── build_hooks.clj │ ├── errors.cljs │ ├── slurp.cljs │ ├── parser.cljs │ ├── outputs.cljs │ ├── presets.cljs │ ├── transforms.cljs │ └── cli.cljs ├── deps.edn ├── csv.ebnf ├── package.json ├── ibdiagnet-lst.ebnf ├── hosts.ebnf ├── hpl.ebnf ├── ibnetdiscover.ebnf ├── README.org └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | pom.xml 2 | pom.xml.asc 3 | *.jar 4 | *.class 5 | /lib/ 6 | /classes/ 7 | /target/ 8 | /checkouts/ 9 | .lein-deps-sum 10 | .lein-repl-history 11 | .lein-plugins/ 12 | .lein-failures 13 | .nrepl-port 14 | .cpcache/ 15 | /.shadow-cljs 16 | /node_modules 17 | -------------------------------------------------------------------------------- /group.ebnf: -------------------------------------------------------------------------------- 1 | groups = (group )* 2 | group = name pw gid user_list? 3 | user_list = user (<','> user)* 4 | 5 | name = STRING 6 | pw = STRING 7 | gid = INTEGER 8 | user = STRING 9 | 10 | STRING = #'[^:\r\n,]+' 11 | INTEGER = #'[0-9]+' 12 | SEP = ':' 13 | EOL = #'(?:\r\n|\r|\n)' 14 | -------------------------------------------------------------------------------- /shadow-cljs.edn: -------------------------------------------------------------------------------- 1 | ;; shadow-cljs configuration 2 | {:deps true 3 | :builds {:cli {:target :node-script 4 | :output-to "target/parseit.js" 5 | :main parseit.cli/main 6 | :build-hooks [(parseit.build-hooks/nexe "target/parseit.js" "target/parseit")]}}} 7 | -------------------------------------------------------------------------------- /passwd.ebnf: -------------------------------------------------------------------------------- 1 | passwd = (user )* 2 | user = name pw uid gid gecos home shell 3 | 4 | name = STRING 5 | pw = STRING 6 | uid = INTEGER 7 | gid = INTEGER 8 | gecos = STRING? 9 | home = STRING 10 | shell = STRING 11 | STRING = #'[^:\r\n]+' 12 | INTEGER = #'[0-9]+' 13 | SEP = ':' 14 | EOL = #'(?:\r\n|\r|\n)' 15 | -------------------------------------------------------------------------------- /src/parseit/misc.cljs: -------------------------------------------------------------------------------- 1 | (ns parseit.misc 2 | (:require 3 | ["console" :as console] 4 | [goog.string] 5 | [goog.string.format])) 6 | 7 | (defn print-stderr [s] 8 | (console/error s)) 9 | 10 | (def fmt js/goog.string.format) 11 | 12 | (defn printf [f & args] 13 | (-> (apply fmt f args) 14 | (print))) 15 | 16 | (defn printf-stderr [f & args] 17 | (-> (apply fmt f args) 18 | (print-stderr))) 19 | 20 | -------------------------------------------------------------------------------- /deps.edn: -------------------------------------------------------------------------------- 1 | {:deps {com.taoensso/timbre {:mvn/version "4.10.0"} 2 | com.cognitect/transit-cljs {:mvn/version "0.8.256"} 3 | cider/cider-nrepl {:mvn/version "0.24.0"} 4 | thheller/shadow-cljs {:mvn/version "2.8.94"} 5 | instaparse {:mvn/version "1.4.10"} 6 | org.clojure/tools.cli {:mvn/version "1.0.194"}} 7 | :aliases {:outdated {:extra-deps {olical/depot {:mvn/version "1.8.4"}} 8 | :main-opts ["-m" "depot.outdated.main"]}}} 9 | -------------------------------------------------------------------------------- /csv.ebnf: -------------------------------------------------------------------------------- 1 | S = RECORD ( RECORD)* ( PARTIAL_RECORD)? ? 2 | RECORD = FIELD ( FIELD)* 3 | PARTIAL_RECORD = (FIELD )* PARTIAL_FIELD 4 | FIELD = ESCAPED | NON_ESCAPED 5 | PARTIAL_FIELD = PARTIAL_ESCAPED | NON_ESCAPED 6 | PARTIAL_ESCAPED = (TEXTDATA | | | ESCAPED_DQUOTE )* 7 | = (TEXTDATA | | | ESCAPED_DQUOTE )* 8 | = TEXTDATA* 9 | COMMA = #"," 10 | DQUOTE = #'"' 11 | ESCAPED_DQUOTE = #'""' 12 | TEXTDATA = #'[^,\r\n"]+' 13 | EOL = #"(?:\r\n|\r|\n)" 14 | -------------------------------------------------------------------------------- /src/parseit/build_hooks.clj: -------------------------------------------------------------------------------- 1 | (ns parseit.build-hooks 2 | (:require [clojure.java.shell :refer [sh]] 3 | [clojure.string :as str])) 4 | 5 | (defn exec [cmd] 6 | (let [{:keys [exit out err]} (apply sh cmd)] 7 | (if (zero? exit) 8 | (when-not (str/blank? out) 9 | (println out)) 10 | (println err)))) 11 | 12 | (defn nexe 13 | {:shadow.build/stage :flush} 14 | [state src dest] 15 | (case (:shadow.build/mode state) 16 | :release 17 | (let [cmd ["npx" "nexe"]] 18 | (println (format "Executing: %s" cmd)) 19 | (exec cmd)) 20 | 21 | true) 22 | state) 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "parseit", 3 | "version": "0.0.1", 4 | "description": "Parseit is command line tool to parse data using EBNF or ABNF using the excellent Instaparse library, and serializing the result into JSON, EDN, YAML or Transit format.", 5 | "main": "target/parseit.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/theasp/parseit.git" 12 | }, 13 | "keywords": [ 14 | "clojurescript", 15 | "instaparse", 16 | "parse", 17 | "cli", 18 | "ebnf", 19 | "abnf" 20 | ], 21 | "author": "Andrew Phillips", 22 | "license": "Apache-2.0", 23 | "bugs": { 24 | "url": "https://github.com/theasp/parseit/issues" 25 | }, 26 | "homepage": "https://github.com/theasp/parseit#readme", 27 | "dependencies": { 28 | "js-yaml": "^3.14.1", 29 | "source-map": "^0.7.3", 30 | "split2": "^3.2.2" 31 | }, 32 | "devDependencies": { 33 | "nexe": "^4.0.0-beta.18", 34 | "shadow-cljs": "^2.15.5" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/parseit/errors.cljs: -------------------------------------------------------------------------------- 1 | (ns parseit.errors 2 | (:require 3 | [parseit.misc :as misc :refer [printf printf-stderr print-stderr]] 4 | ["process" :as process :refer [exit]])) 5 | 6 | (defn invalid-preset [preset] 7 | (printf-stderr "ERROR: Unknown preset '%s'" preset) 8 | (exit 1)) 9 | 10 | (defn unknown-format [output] 11 | (printf-stderr "ERRROR: Unknown format '%s'" output) 12 | (exit 1)) 13 | 14 | (defn missing-argument-grammar-file [] 15 | (printf-stderr "ERROR: You must specify a grammar file or use --preset") 16 | (exit 1)) 17 | 18 | (defn invalid-style [style-name] 19 | (printf-stderr "ERROR: Unknown style '%s'" style-name) 20 | (exit 1)) 21 | 22 | (defn unable-to-parse [err] 23 | (print-stderr (pr-str err)) 24 | (exit 1)) 25 | 26 | (defn unknown-transform-type [type] 27 | (printf-stderr "Unknown transform type '%s'" type) 28 | (exit 1)) 29 | 30 | (defn parsing-options [errors summary] 31 | (print-stderr "ERROR: Unable to parse command line options:") 32 | (doseq [error errors] 33 | (printf-stderr " %s" error)) 34 | (exit 1)) 35 | 36 | (defn unable-to-read-file [err] 37 | (print-stderr (.-message err)) 38 | (exit 1)) 39 | -------------------------------------------------------------------------------- /src/parseit/slurp.cljs: -------------------------------------------------------------------------------- 1 | (ns parseit.slurp 2 | (:require 3 | ["fs" :as fs] 4 | ["process" :as process] 5 | ["split2" :as split2] 6 | [parseit.errors :as errors])) 7 | 8 | (defn slurp-file [file encoding] 9 | (let [encoding (when (and encoding (not= :raw (keyword encoding))) 10 | (name encoding))] 11 | (try 12 | (if (= file "-") 13 | (fs/readFileSync 0 encoding) 14 | (fs/readFileSync file encoding)) 15 | (catch js/Object e 16 | (errors/unable-to-read-file e))))) 17 | 18 | (defn read-stream-split [^js stream regex line-fn done-fn] 19 | (-> stream 20 | (.pipe (split2 regex nil nil)) 21 | (.on "data" line-fn) 22 | (.on "end" done-fn))) 23 | 24 | (defn read-stream [^js stream data-fn done-fn] 25 | (-> stream 26 | (.on "data" data-fn) 27 | (.on "end" done-fn))) 28 | 29 | (defn create-read-stream [file encoding] 30 | (let [encoding (when (and encoding (not= :raw (keyword encoding))) 31 | (name encoding))] 32 | (if (= file "-") 33 | (.setEncoding process/stdin encoding) 34 | (fs/createReadStream file #js {:encoding encoding 35 | :emitClose true})))) 36 | 37 | (defn read-file-split [file regex encoding line-fn done-fn] 38 | (try 39 | (-> (create-read-stream file encoding) 40 | (read-stream-split regex line-fn done-fn)) 41 | (catch js/Object e 42 | (errors/unable-to-read-file e)))) 43 | 44 | 45 | (defn read-file [file encoding data-fn done-fn] 46 | (try 47 | ;; Use slurp-file, streams will be split at 64k by default 48 | (-> (slurp-file file encoding) 49 | (data-fn)) 50 | (done-fn) 51 | (catch js/Object e 52 | (errors/unable-to-read-file e)))) 53 | -------------------------------------------------------------------------------- /ibdiagnet-lst.ebnf: -------------------------------------------------------------------------------- 1 | ibdiagnet_lst = (header )? links 2 | 3 | header = 4 | version 5 | command 6 | timestamp 7 | 8 | version = <'# Running version: '> versions 9 | versions = (version_item <','?>)+ 10 | version_item = <'"'> version_name version_value <'"'> 11 | version_name = #'[^ \"]+' 12 | version_value = #'[^ \"]+' 13 | 14 | command = <'# Running command: '> STRING 15 | timestamp = <'# Timestamp: '> STRING 16 | 17 | links = link+ 18 | link = from to conn 19 | 20 | from = node 21 | to = node 22 | 23 | node = <'{'> 24 | type 25 | ports 26 | system_guid 27 | guid 28 | port_guid 29 | vendor_id 30 | device_id 31 | revision 32 | name 33 | lid 34 | port 35 | <'}'> 36 | 37 | type = WORD 38 | ports = <'Ports:'> HEX 39 | system_guid = <'SystemGUID:'> HEX 40 | guid = <'NodeGUID:'> HEX 41 | port_guid = <'PortGUID:'> HEX 42 | vendor_id = <'VenID:'> HEX 43 | device_id = <'DevID:'> HEX 44 | revision = <'Rev:'> HEX 45 | lid = <'LID:'> HEX 46 | port = <'PN:'> HEX 47 | name = <'{'> #'[^\}]+' <'}'> 48 | 49 | conn = phy 50 | log 51 | speed 52 | 53 | phy = <'PHY='> (WORD | '') 54 | log = <'LOG='> WORD 55 | speed = <'SPD='> NUMBER 56 | 57 | QUOTED_TUPLE = <'"'> #'[^ \"]+' #'[^ \"]+' <'"'> 58 | 59 | COMMENT = <'#'> STRING? 60 | HEX = #'(0x)?[0-9a-fA-F]+' 61 | 62 | SPACE = #'[ \t]+' 63 | WORD = #'[^ \t]+' 64 | 65 | NUMBER = #'[0-9]+(\.[0-9]+)?' 66 | 67 | STRING = #'[^\r\n]*[^\r\n \t]' 68 | 69 | = #'[^\r\n]+' 70 | = #'(?:\r\n|\r|\n)' 71 | 72 | -------------------------------------------------------------------------------- /hosts.ebnf: -------------------------------------------------------------------------------- 1 | hosts = (host? )* 2 | host = address name ( aliases)? 3 | aliases = hostname ( hostname)* 4 | comment = '#' STRING? 5 | name = hostname 6 | address = ipv4 | ipv6 7 | 8 | hostname = hostname_segment ('.' hostname_segment)* 9 | hostname_segment = #'[a-zA-Z0-9][a-zA-Z0-9-]*' 10 | 11 | (* IP parsing from https://github.com/lunatic-cat/iproute/blob/master/resources/route.ebnf *) 12 | 13 | (* https://stackoverflow.com/a/14639569/423551 *) 14 | ipv6 = h16-colon-6 ls32 | 15 | "::" h16-colon-5 ls32 | 16 | h16? "::" h16-colon-4 ls32 | 17 | h16-colon-upto-1? "::" h16-colon-3 ls32 | 18 | h16-colon-upto-2? "::" h16-colon-2 ls32 | 19 | h16-colon-upto-3? "::" h16 ":" ls32 | 20 | h16-colon-upto-4? "::" ls32 | 21 | h16-colon-upto-5? "::" h16 | 22 | h16-colon-upto-6? "::" 23 | (* https://github.com/Engelberg/instaparse/issues/187 *) 24 | = #"([0-9a-fA-F]{1,4}:){6}" 25 | = #"([0-9a-fA-F]{1,4}:){5}" 26 | = #"([0-9a-fA-F]{1,4}:){4}" 27 | = #"([0-9a-fA-F]{1,4}:){3}" 28 | = #"([0-9a-fA-F]{1,4}:){2}" 29 | = #"([0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4}" 30 | = #"([0-9a-fA-F]{1,4}:){0,5}[0-9a-fA-F]{1,4}" 31 | = #"([0-9a-fA-F]{1,4}:){0,4}[0-9a-fA-F]{1,4}" 32 | = #"([0-9a-fA-F]{1,4}:){0,3}[0-9a-fA-F]{1,4}" 33 | = #"([0-9a-fA-F]{1,4}:){0,2}[0-9a-fA-F]{1,4}" 34 | = #"([0-9a-fA-F]{1,4}:){0,1}[0-9a-fA-F]{1,4}" 35 | = h16 ":" h16 | ipv4 36 | = #"[0-9a-fA-F]{1,4}" 37 | ipv4 = dec-octet "." dec-octet "." dec-octet "." dec-octet 38 | = digit | (#"[0-9]" digit) | ("1" digit digit) | ("2" #"[0-4]" digit) | ("25" #"[0-5]") 39 | = #"[0-9]" 40 | 41 | SPACE = #'[ \t]' 42 | STRING = #'[^\r\n]+' 43 | EOL = #'(?:\r\n|\r|\n)' 44 | -------------------------------------------------------------------------------- /src/parseit/parser.cljs: -------------------------------------------------------------------------------- 1 | (ns parseit.parser 2 | (:require 3 | [parseit.misc :as misc] 4 | [parseit.slurp :as slurp] 5 | [parseit.errors :as errors] 6 | [instaparse.core :as insta :refer-macros [defparser]])) 7 | 8 | (def styles 9 | {nil :hiccup 10 | "" :hiccup 11 | "hiccup" :hiccup 12 | "tree" :hiccup 13 | "enlive" :enlive 14 | "list" :enlive}) 15 | 16 | 17 | 18 | (defn some-second? [d] 19 | (some? (second d))) 20 | 21 | (defn build-parser [{:keys [grammar options parser] :as state}] 22 | (let [style-name (get-in state [:options :style])] 23 | (if-let [style (get styles style-name)] 24 | (let [parser (or parser (insta/parser grammar :output-format style)) 25 | parse-opts (->> [:start :partial :total :unhide :optimize] 26 | (select-keys options) 27 | (filter some-second?) 28 | (apply concat)) 29 | parse (if (:all? options) 30 | insta/parses 31 | insta/parse) 32 | parse-fn (fn [input] 33 | (let [parsed (apply parse parser input :optimize :memory parse-opts)] 34 | (if (insta/failure? parsed) 35 | (errors/unable-to-parse parsed) 36 | parsed)))] 37 | (assoc state :parse-fn parse-fn)) 38 | (errors/invalid-style style-name)))) 39 | 40 | (defn load-grammar-file [state] 41 | (if-let [file (-> state :arguments first)] 42 | (-> state 43 | (assoc :grammar-file file) 44 | (assoc :grammar (slurp/slurp-file file "utf8")) 45 | (update :arguments rest) 46 | (build-parser)) 47 | (errors/missing-argument-grammar-file))) 48 | 49 | (defn load-grammar [state] 50 | (if (or (:grammar state) (:parser state)) 51 | state 52 | (load-grammar-file state))) 53 | -------------------------------------------------------------------------------- /hpl.ebnf: -------------------------------------------------------------------------------- 1 | hpl = header settings results 2 | 3 | header = version authors 4 | version = <"HPLinpack"> FLOAT 5 | authors = author_line+ 6 | = (<'Written by'> | <'Modified by'>) NOT_EOL 7 | 8 | settings = settings_body 9 | 10 | settings_head = <'An explanation of the input/output parameters follows:'> ( )+ 11 | settings_body = <'The following parameter values will be used:'> kv_item+ 12 | 13 | kv_item = kv_key <':'> kv_value 14 | kv_key = #'[^:\r\n]*[^:\r\n \t]' 15 | kv_value = (INTEGER / FLOAT / STRING) 16 | 17 | settings_foot = ( )+ 18 | 19 | results = test_results result_timings ( )* 20 | test_results = result* 21 | 22 | result = result_tv result_n result_nb result_p result_q result_time result_gflops 23 | result_tv = WORD 24 | result_n = INTEGER 25 | result_nb = INTEGER 26 | result_p = INTEGER 27 | result_q = INTEGER 28 | result_time = FLOAT 29 | result_gflops = FLOAT 30 | 31 | result_hpl_pdgesv = 'HPL_pdgesv() start time' NOT_EOL EOL EOL 'HPL_pdgesv() end time' NOT_EOL EOL EOL 32 | result_timings = kv_item+ 33 | 34 | V_LINE = "--VVV--VVV--VVV--VVV--VVV--VVV--VVV--VVV--VVV--VVV--VVV--VVV--VVV--VVV--VVV-" 35 | DOUBLE_LINE = "================================================================================" 36 | SINGLE_LINE = "--------------------------------------------------------------------------------" 37 | 38 | SPACE = #'[ \t]+' 39 | WORD = #'[^ \t]+' 40 | INTEGER = SIGN? DIGITS 41 | FLOAT = SIGN? DIGITS '.' DIGITS? EXPONENT? 42 | EXPONENT = 'e' INTEGER 43 | 44 | SIGN = '+' | '-' 45 | = #'[0-9]+' 46 | STRING = #'[^\r\n]*[^\r\n \t]' 47 | 48 | = #'[^\r\n]+' 49 | = #'(?:\r\n|\r|\n)' 50 | -------------------------------------------------------------------------------- /src/parseit/outputs.cljs: -------------------------------------------------------------------------------- 1 | (ns parseit.outputs 2 | (:require 3 | ["js-yaml" :as yaml] 4 | [cljs.pprint :as pprint] 5 | [parseit.errors :as errors] 6 | [cognitect.transit :as transit])) 7 | 8 | (defn edn-output [data] 9 | (pr data)) 10 | 11 | (defn edn-pretty-output [data] 12 | (pprint/pprint data)) 13 | 14 | (defn json-output [data] 15 | (-> (clj->js data) 16 | (js/JSON.stringify) 17 | (print))) 18 | 19 | (defn json-pretty-output [data] 20 | (-> (clj->js data) 21 | (js/JSON.stringify nil 2) 22 | (print))) 23 | 24 | (defn transit-output [data] 25 | (-> (transit/writer :json) 26 | (transit/write data) 27 | (print))) 28 | 29 | (defn transit-verbose-output [data] 30 | (-> (transit/writer :json-verbose) 31 | (transit/write data) 32 | (print))) 33 | 34 | (defn yaml-output [data] 35 | (print "---") 36 | (-> (clj->js data) 37 | (yaml/safeDump) 38 | (print))) 39 | 40 | (def outputs 41 | {:edn {:desc "Extensible Data Format" 42 | :fn edn-output} 43 | :edn-pretty {:desc "Extensible Data Format (pretty)" 44 | :fn edn-pretty-output} 45 | :json {:desc "JavaScript Object Notation" 46 | :fn json-output} 47 | :json-pretty {:desc "JavaScript Object Notation (pretty)" 48 | :fn json-pretty-output} 49 | :transit {:desc "Transit JSON" 50 | :fn transit-output} 51 | :transit-verbose {:desc "Transit JSON Verbose" 52 | :fn transit-verbose-output} 53 | :yaml {:desc "YAML Ain't Markup Language" 54 | :fn yaml-output}}) 55 | 56 | (def output-fns 57 | (-> (fn [acc [type value]] 58 | (let [types (conj (:aliases value) type) 59 | f (:fn value)] 60 | (-> (fn [acc type] 61 | (assoc acc type f)) 62 | (reduce acc types)))) 63 | (reduce {} outputs))) 64 | 65 | (defn output-parsed [{:keys [output-fn parsed] :as state}] 66 | (output-fn parsed)) 67 | 68 | (defn build-output [{:keys [options] :as state}] 69 | (let [format (get options :format) 70 | output-fn (get output-fns format)] 71 | (if output-fn 72 | (assoc state :output-fn output-fn) 73 | (errors/unknown-format format)))) 74 | -------------------------------------------------------------------------------- /ibnetdiscover.ebnf: -------------------------------------------------------------------------------- 1 | ibnetdiscover = header nodes 2 | 3 | header = generated initiated 4 | generated = <'#'> <'Topology file: generated on '> generated_date 5 | generated_date = STRING 6 | 7 | initiated = <'#'> initiated_node_guid initiated_port_guid 8 | initiated_node_guid = <'Initiated from node '> GUID 9 | initiated_port_guid = <'port '> GUID 10 | 11 | nodes = node+ 12 | node = vend_id dev_id sysimg_guid (ca_guid | switch_guid) node_info ports 13 | 14 | vend_id = <'vendid='> HEX 15 | dev_id = <'devid='> HEX 16 | sysimg_guid = <'sysimgguid='> GUID 17 | ca_guid = <'caguid='> GUID 18 | switch_guid = <'switchguid='> GUID (<'('> <')'>)? 19 | 20 | = <#'(S|H)-'?> HEX 21 | 22 | = node_type 23 | node_ports 24 | <'"'> <'"'> 25 | <'#'> 26 | node_name 27 | ( 28 | node_class 29 | 'port 0' 30 | node_lid 31 | node_lmc 32 | )? EOL 33 | 34 | 35 | node_type = #'(Ca|Switch)' 36 | node_name = <'"'> #'[^"\r\n]+' <'"'> 37 | node_ports = DIGITS 38 | node_lid = LID 39 | node_lmc = LMC 40 | node_class = #'(base|enhanced)' 41 | 42 | ports = port* 43 | 44 | port = port_num port_local_guid? 45 | port_remote_guid port_remote_port 46 | <'#'> 47 | ( port_local_lid)? 48 | ( port_local_lmc)? 49 | 50 | node_name 51 | port_remote_lid 52 | port_speed 53 | 54 | port_local_guid = <'('> GUID <')'> 55 | port_num = <'['> DIGITS <']'> 56 | port_remote_guid = <'"'> GUID <'"'> 57 | port_remote_port = <'['> DIGITS <']'> 58 | port_local_lid = LID 59 | port_local_lmc = LMC 60 | port_remote_lid = LID 61 | port_speed = #'\d+x[A-Z]+' 62 | 63 | 64 | = <'lid '> DIGITS 65 | = <'lmc '> DIGITS 66 | 67 | COMMENT = <'#'> STRING? 68 | 69 | HEX = #'(0x)?[0-9a-f]+' 70 | 71 | SPACE = #'[ \t]+' 72 | WORD = #'[^ \t]+' 73 | 74 | = SIGN? DIGITS 75 | = SIGN? DIGITS '.' DIGITS? EXPONENT? 76 | = 'e' INTEGER 77 | 78 | SIGN = '+' | '-' 79 | DIGITS = #'[0-9]+' 80 | STRING = #'[^\r\n]*[^\r\n \t]' 81 | 82 | = #'[^\r\n]+' 83 | = #'(?:\r\n|\r|\n)' 84 | 85 | -------------------------------------------------------------------------------- /src/parseit/presets.cljs: -------------------------------------------------------------------------------- 1 | (ns parseit.presets 2 | (:require 3 | [parseit.errors :as errors] 4 | [parseit.transforms 5 | :as transforms 6 | :refer [wrap-type 7 | transform-map 8 | transform-list 9 | transform-merge 10 | transform-map-kv 11 | transform-first 12 | transform-integer 13 | transform-hex-to-int 14 | transform-int-to-hex]] 15 | [instaparse.core :as insta :refer-macros [defparser]] 16 | [clojure.string :as str])) 17 | 18 | (defparser csv-parser "csv.ebnf") 19 | (def csv-preset 20 | {:parser csv-parser 21 | :transform transforms/standard 22 | :desc "Comma Seperated Value"}) 23 | 24 | 25 | (defparser passwd-parser "passwd.ebnf") 26 | (def passwd-preset 27 | {:desc "NSS passwd(5), i.e. /etc/passwd" 28 | :parser passwd-parser 29 | :transform (merge transforms/standard 30 | {:user transform-map 31 | :passwd transform-list})}) 32 | 33 | (defparser group-parser "group.ebnf") 34 | (def group-preset 35 | {:desc "NSS group(5), i.e. /etc/group" 36 | :parser group-parser 37 | :transform (merge transforms/standard 38 | {:group transform-map 39 | :groups transform-list 40 | :user str})}) 41 | 42 | (defparser hosts-parser "hosts.ebnf") 43 | (def hosts-preset 44 | {:desc "NSS hosts(5), i.e. /etc/hosts" 45 | :parser hosts-parser 46 | :transform (merge transforms/standard 47 | {:hosts transform-list 48 | :host transform-map 49 | :hostname_segment str 50 | :hostname str 51 | :ipv4 str 52 | :ipv6 str})}) 53 | 54 | (defparser hpl-parser "hpl.ebnf") 55 | (def hpl-preset 56 | {:desc "High Performance Linpack benchmark results" 57 | :parser hpl-parser 58 | :transform (merge transforms/standard 59 | {:hpl transform-map 60 | :authors (wrap-type transform-list :authors) 61 | :header (wrap-type transform-map :header) 62 | :result_timings (wrap-type transform-merge :result_timings) 63 | :results (wrap-type transform-map :results) 64 | :result transform-map 65 | :WORD str 66 | :settings_body transform-merge 67 | :kv_item transform-map-kv 68 | :kv_key transform-first 69 | :kv_value transform-first})}) 70 | 71 | (defparser ibnetdiscover-parser "ibnetdiscover.ebnf") 72 | (def ibnetdiscover-preset 73 | {:desc "Infiniband ibnetdiscover output" 74 | :parser ibnetdiscover-parser 75 | :transform (merge transforms/standard 76 | {:ibnetdiscover transform-map 77 | :header (wrap-type transform-merge :header) 78 | :initiated transform-map 79 | :generated transform-map 80 | :node transform-map 81 | :port transform-map})}) 82 | 83 | (defparser ibdiagnet-lst-parser "ibdiagnet-lst.ebnf") 84 | 85 | (def ibdiagnet-lst-preset 86 | {:desc "Infiniband ibdiagnet.lst" 87 | :parser ibdiagnet-lst-parser 88 | :transform (merge transforms/standard 89 | {:port (wrap-type transform-hex-to-int :port) 90 | :lid (wrap-type transform-hex-to-int :lid) 91 | :ports (wrap-type transform-hex-to-int :ports) 92 | :versions transform-merge 93 | :version_item transform-map-kv 94 | :version_name str 95 | :version_value str 96 | :header (wrap-type transform-map :header) 97 | :conn (wrap-type transform-map :conn) 98 | :links (wrap-type transform-list :links) 99 | :link transform-map 100 | :node transform-map 101 | :ibdiagnet_lst transform-map})}) 102 | 103 | (def presets 104 | {:csv csv-preset 105 | :passwd passwd-preset 106 | :group group-preset 107 | :hosts hosts-preset 108 | :hpl hpl-preset 109 | :ibnetdiscover ibnetdiscover-preset 110 | :ibdiagnet-lst ibdiagnet-lst-preset}) 111 | 112 | (defn load-preset [{:keys [options] :as state}] 113 | (if-let [preset-name (-> state :options :preset)] 114 | (let [preset (get presets preset-name false)] 115 | (if preset 116 | (merge state preset) 117 | (errors/invalid-preset (name preset-name)))) 118 | (merge state {:transform (when-not (:no-standard-tx? options) transforms/standard)}))) 119 | 120 | (defn preset-valid? [name] 121 | (-> (get presets name) 122 | (some?))) 123 | -------------------------------------------------------------------------------- /src/parseit/transforms.cljs: -------------------------------------------------------------------------------- 1 | (ns parseit.transforms 2 | (:require 3 | [parseit.errors :as errors] 4 | [goog.string] 5 | [goog.string.format] 6 | [clojure.string :as str] 7 | [instaparse.core :as insta :refer-macros [defparser]])) 8 | 9 | 10 | (defn transform-integer [& s] 11 | (-> (apply str s) 12 | (js/parseInt))) 13 | 14 | (defn transform-float [& s] 15 | (-> (apply str s) 16 | (js/parseFloat))) 17 | 18 | (defn transform-uuid [& s] 19 | (-> (apply str s) 20 | (uuid))) 21 | 22 | (defn transform-hex-to-int [& s] 23 | (-> (apply str s) 24 | (str/replace #"^0x" "") 25 | (js/parseInt 16))) 26 | 27 | (defn transform-int-to-hex [& s] 28 | (str "0x" (-> (apply str s) 29 | (int) 30 | (.toString 16) 31 | (str/upper-case)))) 32 | 33 | (defn transform-hex [& s] 34 | (str "0x" (-> (apply str s) 35 | (str/replace #"^0x" "") 36 | (str/upper-case)))) 37 | 38 | (defn transform-nil [& s] 39 | nil) 40 | 41 | (defn add-child-to-map [m child] 42 | (if (vector? child) 43 | (case (count child) 44 | 0 m 45 | 1 (assoc m (first child) nil) 46 | 2 (assoc m (first child) (second child)) 47 | (assoc m (first child) (rest child))) 48 | m)) 49 | 50 | (defn transform-map [& children] 51 | (reduce add-child-to-map {} children)) 52 | 53 | (defn transform-map-kv [& children] 54 | {(first children) (second children)}) 55 | 56 | (defn transform-merge [& children] 57 | (apply merge children)) 58 | 59 | (defn transform-list [& children] 60 | children) 61 | 62 | (defn transform-first [& children] 63 | (first children)) 64 | 65 | (defn transform-keyword [& s] 66 | (-> (apply str s) 67 | (keyword))) 68 | 69 | (def transforms 70 | {:integer {:aliases [:int] 71 | :desc "Convert to integer" 72 | :fn transform-integer} 73 | :float {:aliases [:decimal :number :double] 74 | :desc "Convert to floating point" 75 | :fn transform-float} 76 | :string {:aliases [:text :str] 77 | :desc "Convert to string" 78 | :fn str} 79 | :hex {:aliases [] 80 | :desc "Ensure that the value starts with 0x" 81 | :fn transform-hex} 82 | :hex-to-int {:aliases [:hex2int :hex-int] 83 | :desc "Convert hex to integer" 84 | :fn transform-hex-to-int} 85 | :int-to-hex {:aliases [:int2hex :int-hex] 86 | :desc "Convert integer to hex" 87 | :fn transform-int-to-hex} 88 | :map {:aliases [:dict :object] 89 | :desc "Create map from children" 90 | :fn transform-map} 91 | :list {:aliases [:array :vec :unwrap] 92 | :desc "Create list from children" 93 | :fn transform-list} 94 | :first {:aliases [] 95 | :desc "No conversion, just the first item" 96 | :fn transform-first} 97 | :keyword {:aliases [] 98 | :desc "Create a keyword (only useful for EDN or Transit)" 99 | :fn transform-keyword} 100 | :map-kv {:aliases [] 101 | :desc "Transform a list of key value pairs into a map" 102 | :fn transform-map-kv} 103 | :merge {:aliases [] 104 | :desc "Merge multiple maps into a single map" 105 | :fn transform-merge} 106 | :nil {:aliases [:null] 107 | :desc "Convert to nil" 108 | :fn transform-nil}}) 109 | 110 | (def transform-fns 111 | (-> (fn [acc [type value]] 112 | (let [types (conj (:aliases value) type) 113 | f (:fn value)] 114 | (-> (fn [acc type] 115 | (let [type+ (-> (name type) (str "+") (keyword))] 116 | (-> acc 117 | (assoc type f) 118 | (assoc type+ f)))) 119 | (reduce acc types)))) 120 | (reduce {} transforms))) 121 | 122 | (def standard 123 | {:DIGITS transform-integer 124 | :INTEGER transform-integer 125 | :NUMBER transform-float 126 | :FLOAT transform-float 127 | :HEX transform-hex 128 | :WORD str 129 | :STRING str}) 130 | 131 | (defn type-valid? [type] 132 | (contains? transform-fns (keyword type))) 133 | 134 | (defn type-wrap? [type] 135 | (-> (name type) 136 | (last) 137 | (= "+"))) 138 | 139 | (defn wrap-type [f name] 140 | (fn [& s] 141 | [name (apply f s)])) 142 | 143 | (defn add-transform [transforms [rule type]] 144 | (if-let [transform-fn (get transform-fns (keyword type))] 145 | (if (type-wrap? type) 146 | (assoc transforms (keyword rule) (wrap-type transform-fn rule)) 147 | (assoc transforms (keyword rule) transform-fn)) 148 | (errors/unknown-transform-type type))) 149 | 150 | (defn build-transform [{:keys [options transform] :as state}] 151 | (let [standard? (not (:no-standard-tx? options)) 152 | transform (reduce add-transform transform (:tx options)) 153 | transform-fn (if (empty? transform) 154 | identity 155 | #(insta/transform transform %))] 156 | (assoc state :transform-fn transform-fn))) 157 | 158 | (defn transform-parsed [{:keys [transform-fn parsed] :as state}] 159 | (assoc state :parsed (transform-fn parsed))) 160 | -------------------------------------------------------------------------------- /src/parseit/cli.cljs: -------------------------------------------------------------------------------- 1 | (ns parseit.cli 2 | (:require 3 | [parseit.misc :as misc :refer [printf printf-stderr print-stderr fmt]] 4 | [parseit.slurp :as slurp] 5 | [parseit.parser :as parser] 6 | [parseit.errors :as errors] 7 | [parseit.transforms :as transforms] 8 | [parseit.presets :as presets] 9 | [parseit.outputs :as outputs] 10 | ["process" :as process] 11 | [clojure.string :as str] 12 | [clojure.tools.cli :refer [parse-opts]])) 13 | 14 | 15 | (def cli-options 16 | ;; An option with a required argument 17 | [["-p" "--preset PRESET" "Preset grammar to use" 18 | :id :preset 19 | :default nil 20 | :parse-fn #(when-not (str/blank? %) 21 | (keyword %)) 22 | :default-desc "none" 23 | :validate [presets/preset-valid? 24 | "Unknown preset name"]] 25 | ["-f" "--format FORMAT" "Select the output format" 26 | :id :format 27 | :default :json-pretty 28 | :default-desc "json-pretty" 29 | :parse-fn #(when-not (str/blank? %) 30 | (keyword %))] 31 | ["-S" "--start RULE" "Start processing at this rule" 32 | :parse-fn #(when-not (str/blank? %) 33 | (keyword %))] 34 | ["-t" "--transform RULE:TYPE[+]" "Transform a rule into a type, and keep wrapped with + suffix" 35 | :id :tx 36 | :default [] 37 | :parse-fn #(str/split % #":" 2) 38 | :validate [#(not (str/blank? (first %))) 39 | "Transform rule is empty" 40 | 41 | #(not (str/blank? (second %))) 42 | "Transform target empty" 43 | 44 | #(transforms/type-valid? (second %)) 45 | "Unknown transform type"] 46 | :default-desc "" 47 | :assoc-fn (fn [options id value] 48 | (-> options 49 | (update :tx conj value)))] 50 | ["-T" "--no-standard-tx" "Do use the standard transformations" 51 | :id :no-standard-tx?] 52 | ["-a" "--all" "Return all parses rather than the best match" 53 | :id :all?] 54 | ["-s" "--split REGEX" "Process input as a stream, parsing each chunk seperated by REGEX" 55 | :id :split] 56 | ["-l" "--split-lines" "Split on newlines, same as --split '\\r?\\n'" 57 | :id :split-lines? 58 | :assoc-fn (fn [options id] 59 | (assoc options :split "\\r?\\n"))] 60 | ["-L" "--split-lines-keep" "Split on newlines, keeping the newline, same as --split '(?<=\\r?\\n)'" 61 | :id :split-lines-keep? 62 | :assoc-fn (fn [options id] 63 | (assoc options :split "(?<=\\r?\\n)"))] 64 | ["-e" "--encoding ENCODING" "Use the specified encoding when reading the input, or raw" 65 | :id :encoding 66 | :default "utf8" 67 | :validate [#(not (str/blank? %)) 68 | "Encoding is empty"]] 69 | ["-X" "--style TYPE" "Build the parsed tree in the style of hiccup or enlive" 70 | :default "hiccup" 71 | :parse-fn #(when-not (str/blank? %) (keyword %)) 72 | :validate [#(not (str/blank? %)) 73 | "Style is empty" 74 | 75 | #(contains? parser/styles %) 76 | "Not a valid style"]] 77 | ["-h" "--help" "Help"]]) 78 | 79 | 80 | (defn item->help [item-name item] 81 | (let [names (concat [item-name] (:aliases item)) 82 | item-name (->> names 83 | (map name) 84 | (sort) 85 | (str/join ", "))] 86 | [item-name (:desc item)])) 87 | 88 | 89 | (defn print-help-table [items] 90 | (let [items (map #(apply item->help %) items) 91 | longest (->> (map #(-> % first count) items) 92 | (apply max)) 93 | fmt (str " %-" longest "s %s")] 94 | (doseq [item (sort items)] 95 | (printf fmt (first item) (second item))))) 96 | 97 | 98 | (defn transform-types-help [] 99 | (print "Transformation Types") 100 | (print-help-table transforms/transforms)) 101 | 102 | (defn output-formats-help [] 103 | (print "Output Formats") 104 | (print-help-table outputs/outputs)) 105 | 106 | (defn presets-help [] 107 | (print "Presets") 108 | (print-help-table presets/presets)) 109 | 110 | (defn handle-opts-help [summary] 111 | (printf "Usage: %s [options] [input]" "parseit") 112 | (printf " %s [options] --preset [input]" "parseit") 113 | (printf " %s --help" "parseit") 114 | (print "") 115 | (print "Options") 116 | (print summary) 117 | (print "") 118 | (output-formats-help) 119 | (print "") 120 | (transform-types-help) 121 | (print "") 122 | (presets-help) 123 | (process/exit 0)) 124 | 125 | (defn handle-opts-normal [options arguments] 126 | (let [{:keys [parse-fn transform-fn output-fn options arguments] :as state} 127 | (-> {:options options :arguments arguments} 128 | (presets/load-preset) 129 | (parser/load-grammar) 130 | (parser/build-parser) 131 | (transforms/build-transform) 132 | (outputs/build-output)) 133 | 134 | process-fn #(-> % parse-fn transform-fn output-fn) 135 | file (or (first arguments) "-") 136 | encoding (:encoding options) 137 | done-fn #(process/exit 0) 138 | split (some-> (:split options) 139 | (re-pattern))] 140 | (if split 141 | (slurp/read-file-split file split encoding process-fn done-fn) 142 | (slurp/read-file file encoding process-fn done-fn)))) 143 | 144 | (defn handle-opts [{:keys [options arguments summary errors]}] 145 | (cond 146 | errors (errors/parsing-options errors summary) 147 | (:help options) (handle-opts-help summary) 148 | :else (handle-opts-normal options arguments))) 149 | 150 | (defn main [& args] 151 | (-> (parse-opts args cli-options) 152 | (handle-opts))) 153 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | #+TITLE: Parseit 2 | 3 | * Introduction 4 | Parseit is command line tool to parse data using [[https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form][EBNF]] or [[https://en.wikipedia.org/wiki/Augmented_Backus%E2%80%93Naur_form][ABNF]] using the excellent [[https://github.com/Engelberg/instaparse][Instaparse]] library, and serializing the result into [[https://www.json.org/json-en.html][JSON]], [[https://github.com/edn-format/edn][EDN]], [[https://yaml.org/][YAML]] or [[https://github.com/cognitect/transit-format][Transit]] format. 5 | 6 | * Usage 7 | #+begin_example 8 | Usage: parseit [options] [input] 9 | parseit [options] --preset [input] 10 | parseit --help 11 | 12 | Options 13 | -p, --preset PRESET none Preset grammar to use 14 | -f, --format FORMAT json-pretty Select the output format 15 | -S, --start RULE Start processing at this rule 16 | -t, --transform RULE:TYPE[+] Transform a rule into a type, and keep wrapped with + suffix 17 | -T, --no-standard-tx Do use the standard transformations 18 | -a, --all Return all parses rather than the best match 19 | -s, --split REGEX Process input as a stream, parsing each chunk seperated by REGEX 20 | -l, --split-lines Split on newlines, same as --split '(?<=\r?\n)' 21 | -e, --encoding ENCODING utf8 Use the specified encoding when reading the input, or raw 22 | -X, --style TYPE hiccup Build the parsed tree in the style of hiccup or enlive 23 | -h, --help Help 24 | 25 | Output Formats 26 | edn Extensible Data Format 27 | edn-pretty Extensible Data Format (pretty) 28 | json JavaScript Object Notation 29 | json-pretty JavaScript Object Notation (pretty) 30 | transit Transit JSON 31 | transit-verbose Transit JSON Verbose 32 | yaml YAML Ain't Markup Language 33 | 34 | Transformation Types 35 | array, list, unwrap, vec Create list from children 36 | decimal, double, float, number Convert to floating point 37 | dict, map, object Create map from children 38 | first No conversion, just the first item 39 | int, integer Convert to integer 40 | keyword Create a keyword (only useful for EDN or Transit) 41 | map-kv Transform a list of key value pairs into a map 42 | merge Merge multiple maps into a single map 43 | nil, null Convert to nil 44 | str, string, text Convert to string 45 | 46 | Presets 47 | csv Comma Seperated Value 48 | group NSS group(5), i.e. /etc/group 49 | hosts NSS hosts(5), i.e. /etc/hosts 50 | hpl High Performance Linpack benchmark results 51 | passwd NSS passwd(5), i.e. /etc/passwd 52 | #+end_example 53 | 54 | * Example 55 | ** Parsing ~passwd~ 56 | 57 | You can parse ~passwd~ entries using the following as ~passwd.ebnf~: 58 | #+NAME: passwd-ebnf 59 | #+HEADER: :exports code 60 | #+HEADER: :results silent 61 | #+HEADER: :tangle passwd.ebnf 62 | #+BEGIN_SRC conf 63 | passwd = (user )* 64 | user = name pw uid gid gecos home shell 65 | 66 | name = STRING 67 | pw = STRING 68 | uid = INTEGER 69 | gid = INTEGER 70 | gecos = STRING? 71 | home = STRING 72 | shell = STRING 73 | STRING = #'[^:\r\n]+' 74 | INTEGER = #'[0-9]+' 75 | SEP = ':' 76 | EOL = #'(?:\r\n|\r|\n)' 77 | #+END_SRC 78 | 79 | We will use ~getent passwd root~ to get the entry for the root user: 80 | #+begin_example 81 | $ getent passwd root 82 | root:x:0:0:root:/root:/bin/bash 83 | #+end_example 84 | 85 | We can then pipe that into Parseit and see the result: 86 | #+begin_example 87 | $ getent passwd root | parseit passwd.ebnf 88 | ["passwd",["user",["name","root"],["pw","x"],["uid",0],["gid",0],["gecos","root"],["home","/root"],["shell","/bin/bash"]]] 89 | #+end_example 90 | 91 | Not a bad start! You will notice that the UID and GID values were converted into integers. There is a library of standard transformations and the ~INTEGER~ rule will be transformed into an integer, and the ~STRING~ rule will be turned into a string. You can avoid this by using ~--no-standard-tx~: 92 | #+begin_example 93 | $ getent passwd root | node target/parseit.js --no-standard-tx passwd.ebnf 94 | ["passwd",["user",["name",["STRING","root"]],["pw",["STRING","x"]],["uid",["INTEGER","0"]],["gid",["INTEGER","0"]],["gecos",["STRING","root"]],["home",["STRING","/root"]],["shell",["STRING","/bin/bash"]]]] 95 | #+end_example 96 | 97 | If you use the options ~--transform user:map~ and ~--transform passwd:list~, Parseit will turn the ~user~ rule into a map (dictionary) and ~passwd~ rule into a list of users: 98 | #+BEGIN_EXAMPLE 99 | $ getent passwd root | parseit --transform user:map --transform passwd:list passwd.ebnf 100 | [{"name":"root","pw":"x","uid":0,"gid":0,"gecos":"root","home":"/root","shell":"/bin/bash"}] 101 | #+END_EXAMPLE 102 | 103 | You don't actually need the EBNF file or any of those options though! There is a ~passwd~ preset which does it all for your: 104 | #+begin_example 105 | $ getent passwd root | parseit --preset passwd 106 | [{"name":"root","pw":"x","uid":0,"gid":0,"gecos":"root","home":"/root","shell":"/bin/bash"}] 107 | #+end_example 108 | 109 | We are parsing a single user, but it's currently being returned in an array. We can tell Parseit to start parsing at a specific rule using the argument ~--start RULE~: 110 | #+begin_example 111 | $ getent passwd root | parseit --preset passwd --start user 112 | Parse error at line 1, column 23: 113 | root:x:0:0:root:/root:/bin/bash 114 | ^ 115 | Expected: 116 | #"[^:\r\n]+" (followed by end-of-string) 117 | #+end_example 118 | 119 | Hey, that didn't work! The problem is that the input ends with a newline and the user rule does not allow that. We can use the ~--split REGEX~ argument to split the input on newlines. 120 | #+begin_example 121 | $ getent passwd root | parseit --preset passwd --start user --split '\n' 122 | {"name":"root","pw":"x","uid":0,"gid":0,"gecos":"root","home":"/root","shell":"/bin/bash"} 123 | #+end_example 124 | 125 | With ~--split~, each chunk will be processed as they are read. You can see this by doing: 126 | #+begin_example 127 | $ (getent passwd root; sleep 10; getent passwd bin) | parseit --preset passwd --start user --split '\n' 128 | {"name":"root","pw":"x","uid":0,"gid":0,"gecos":"root","home":"/root","shell":"/bin/bash"} 129 | {"name":"bin","pw":"x","uid":2,"gid":2,"gecos":"bin","home":"/bin","shell":"/usr/sbin/nologin"} 130 | #+end_example 131 | 132 | Maybe you don't like reading JSON? You can use the YAML output format to make it more readable: 133 | #+begin_example 134 | $ getent passwd root | parseit --preset passwd --format yaml 135 | --- 136 | - name: root 137 | pw: x 138 | uid: 0 139 | gid: 0 140 | gecos: root 141 | home: /root 142 | shell: /bin/bash 143 | #+end_example 144 | 145 | * Grammar 146 | 147 | Parseit uses [[https://github.com/Engelberg/instaparse][Instaparse]], so the [[https://github.com/Engelberg/instaparse#notation][notation section of the tutorial]] has a good description of the grammar syntax. Keep in mind that you will not need to escape strings as you would in Clojure as the grammar will be read out of a text file. 148 | 149 | * Building 150 | This will install Shadow CLJS and then build the JavaScript as ~target/parseit.js~ and a native executable (using nexe) as ~parseit~: 151 | #+begin_example 152 | $ npm install -g shadow-cljs 153 | $ npm install --save-dev shadow-cljs 154 | $ shadow-cljs release cli 155 | #+end_example 156 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------