2017-07-11 20:29:54 -05:00
|
|
|
// Copyright 2017 Jeremy Wall <jeremy@marzhillstudios.com>
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
2018-02-02 15:27:33 -06:00
|
|
|
// #![feature(trace_macros,log_syntax)]
|
2017-12-24 15:24:06 -05:00
|
|
|
|
2018-02-05 08:44:14 -06:00
|
|
|
//! # ucg, A universal configuration grammar.
|
|
|
|
//!
|
|
|
|
//! Ucg defines a common grammar for describing a collection of configuration values.
|
|
|
|
//! ucg allows you to specify configuration values with a syntax that that is immutable,
|
|
|
|
//! comoposable with copy-on-write semantics, and safe.
|
|
|
|
//!
|
|
|
|
//! ## Example
|
|
|
|
//!
|
|
|
|
//! ```ucg
|
|
|
|
//! // named bindings
|
|
|
|
//! let host = "mysql.internal.net";
|
|
|
|
//! let port = 8080
|
|
|
|
//!
|
|
|
|
//! // format strings
|
|
|
|
//! let connstr = "mysql://@:@" % (host, port);
|
|
|
|
//!
|
|
|
|
//! // tuples
|
|
|
|
//! let dbconf = {
|
|
|
|
//! connstr = connstr,
|
|
|
|
//! database = "mydb",
|
|
|
|
//! // lists
|
|
|
|
//! tables = ["posts", "comments", "users"],
|
|
|
|
//! };
|
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! ## Syntax
|
|
|
|
//!
|
|
|
|
//! ucg is a safe language with type inference that tries to guarantee that it will halt.
|
|
|
|
//! A valid ucg file is composesed of a series of statements. Statements are any valid
|
|
|
|
//! ucg expression terminated by a semicolon.
|
|
|
|
//!
|
|
|
|
//! ### Reserved words
|
|
|
|
//!
|
|
|
|
//! The following words are reserved in ucg and can't be used as named bindings.
|
|
|
|
//!
|
|
|
|
//! * let
|
|
|
|
//! * import
|
|
|
|
//! * as
|
|
|
|
//! * select
|
|
|
|
//! * macro
|
|
|
|
//!
|
|
|
|
//! ### Primitive types
|
|
|
|
//!
|
|
|
|
//! ucg has a relatively simple syntax with 3 primitive types, Int, Float, and String.
|
|
|
|
//!
|
|
|
|
//! * Int, is any integer number.
|
|
|
|
//! * Float is any number with a decimal point.
|
|
|
|
//!
|
|
|
|
//! ```ucg
|
|
|
|
//! 1.0; // A typical float.
|
|
|
|
//! 1. // You can leave off the 0 after the decimal point.
|
|
|
|
//! .1 // the leading 0 is also optional.
|
|
|
|
//! ```
|
|
|
|
//!
|
2018-02-05 19:35:17 -06:00
|
|
|
//! * String is any quoted text. backslashes within a string escape the next preceding
|
|
|
|
//! character.
|
2018-02-05 08:44:14 -06:00
|
|
|
//!
|
|
|
|
//! ``` ucg
|
|
|
|
//! "foo"; // a smiple string
|
|
|
|
//! "I'm a \"fine\" looking string"; // escaped quotes in a string.
|
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! ### Complex types
|
|
|
|
//!
|
|
|
|
//! ucg has two complex data types, Lists and Tuples.
|
|
|
|
//!
|
|
|
|
//! * List's start are surrounded with square brackets `[ ]` and have comma separated elements.
|
|
|
|
//!
|
|
|
|
//! ```ucg
|
|
|
|
//! [1, 2, 3]; // A simple list of numbers.
|
|
|
|
//!
|
|
|
|
//! [[1, 2], [3, 4]] // A deep list with embedded lists inside.
|
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! Lists are 0 indexed and you can index into them using the dotted selector syntax.
|
|
|
|
//!
|
|
|
|
//! ```ucg
|
|
|
|
//! let mylist = [0, 1, 2, 3];
|
|
|
|
//!
|
|
|
|
//! let zero = mylist.0;
|
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! * Tuple's are an ordered collection of name, value pairs. They are bounded by curly braces `{ }`
|
|
|
|
//! and contain name = value pairs separated by commas. Trailing commas are permitted. The name must
|
|
|
|
//! be a bareword without quotes.
|
|
|
|
//!
|
|
|
|
//! ```ucg
|
|
|
|
//! let mytuple = {
|
|
|
|
//! field1 = "value1",
|
|
|
|
//! field2 = "value2",
|
|
|
|
//! };
|
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! Tuples can be indexed using dotted selector syntax.
|
|
|
|
//!
|
|
|
|
//! ```ucg
|
|
|
|
//! let field = mytuple.fields1;
|
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! ### Expressions
|
|
|
|
//!
|
|
|
|
//! #### Selectors
|
|
|
|
//!
|
|
|
|
//! Selectors are references to a bound value in ucg. They can index arbitrarily deep into either tuples or lists.
|
|
|
|
//! The head of a selector can be any expression that resolves to a tuple or list. Optionally a selector can also be
|
|
|
|
//! followed by either a bareword to index a tuple field or an integer to index a list position.
|
|
|
|
//!
|
|
|
|
//! The simplest selector is just a reference to a bound value by name.
|
|
|
|
//!
|
|
|
|
//! ```ucg
|
|
|
|
//! let mytuple = {
|
|
|
|
//! field1 = "a string",
|
|
|
|
//! field2 = [{
|
|
|
|
//! subfield1 = 1,
|
|
|
|
//! }];
|
|
|
|
//! };
|
|
|
|
//!
|
|
|
|
//! mytuple.field2.0; // descend into a deeply nested tuple and array.
|
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! #### Binary operators
|
|
|
|
//!
|
|
|
|
//! ucg supports the following operators, +, -, *, /; Each one is type safe and infers the types from the values they operate on.
|
|
|
|
//! The operators expect both the left and right operands to be of the same type. All of the operators are valid on integers and floats.
|
|
|
|
//! The + operator can additionally concatenate strings or arrays.
|
|
|
|
//!
|
|
|
|
//! ```ucg
|
|
|
|
//! 1 + 1; // result is 2
|
|
|
|
//! "foo " + "bar" // result is "foo bar"
|
|
|
|
//! [1,2] + [3,4]; // result is [1,2,3,4]
|
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! #### Conditional data.
|
|
|
|
//!
|
|
|
|
//! ucg supports a limited form of conditional selection of data using the select expression. A select expression starts with the select
|
|
|
|
//! keyword and is followed by a an expression resolving to a string naming the field to select, an expression resolving to the default value,
|
|
|
|
//! and a tuple to select the field from. If the field selected is not in the tuple then the default value will be used.
|
|
|
|
//!
|
|
|
|
//! ```ucg
|
|
|
|
//! let want = "baz";
|
|
|
|
//!
|
|
|
|
//! // field default
|
|
|
|
//! select want, "quux", {
|
|
|
|
//! baz = "foo",
|
|
|
|
//! fuzz = "bang",
|
|
|
|
//! }; // result will be "foo"
|
|
|
|
//!
|
|
|
|
//! // field default
|
|
|
|
//! select "quack", "quux", {
|
|
|
|
//! baz = "foo",
|
|
|
|
//! fuzz = "bang",
|
|
|
|
//! }; // result will be "quux"
|
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! #### Macros
|
|
|
|
//!
|
|
|
|
//! Macros look like functions but they are resolved at compile time and configurations don't execute so they never appear in output.
|
|
|
|
//! They are useful for constructing tuples of a certain shape or otherwise promoting data reuse. You define a macro with the macro
|
|
|
|
//! keyword followed by the arguments in parentheses and then a tuple.
|
|
|
|
//!
|
|
|
|
//! ```ucg
|
|
|
|
//! let myfunc = macro (arg1, arg2) {
|
|
|
|
//! host = arg1,
|
|
|
|
//! port = arg2,
|
|
|
|
//! connstr = "couchdb://@:@" % (arg1, arg2),
|
|
|
|
//! }
|
|
|
|
//!
|
|
|
|
//! let my dbconf = myfunc("couchdb.example.org", "9090");
|
|
|
|
//!
|
|
|
|
//! let my dbhost = dbconf.host;
|
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! macros always resolve to a tuple. If you want to get a single value out you can use selector syntax to retrieve it.
|
|
|
|
//!
|
|
|
|
//! ### Statements
|
|
|
|
//!
|
|
|
|
//! There are 3 kinds of statements in a ucg configuration file. expression statements, let statements, and import statements.
|
|
|
|
//!
|
|
|
|
//! * expression statements
|
|
|
|
//! The simplest and least useful is the expression statements. It is any valid expression followed by a semicolon.
|
|
|
|
//!
|
|
|
|
//! ```ucg
|
|
|
|
//! 1;
|
|
|
|
//! 4 / 2;
|
|
|
|
//! "foo";
|
|
|
|
//! "foo" + "bar";
|
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! Despite the fact that these are valid the results are thrown away and can essentially be considered a noop. If we
|
|
|
|
//! ever create a repl for ucg statements they may prove more useful.
|
|
|
|
//!
|
|
|
|
//! * Let statements
|
|
|
|
//! The let expression binds the result of any valid expression to a name. It starts with the let keyword and is followed by
|
|
|
|
//! the name of the binding, =, and a valid ucg expression.
|
|
|
|
//!
|
|
|
|
//! ```ucg
|
|
|
|
//! let name = "foo";
|
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! * Import statement
|
|
|
|
//! The import statement imports the contents of another ucg file into the current file with a name. The imported files bound
|
|
|
|
//! values are exposed as a tuple in the referencing file. It starts with the import keyword and is followed by a quoted path
|
|
|
|
//! to the ucg file, they keyword as, and a name for the imported values.
|
|
|
|
//!
|
|
|
|
//! ```ucg
|
|
|
|
//! import "dbconfigs.ucg" as dbconfigs;
|
|
|
|
//!
|
|
|
|
//! let mysqlconf = dbconfigs.mysql;
|
|
|
|
//! ```
|
2017-05-05 22:33:25 -05:00
|
|
|
#[macro_use]
|
|
|
|
extern crate nom;
|
2017-09-23 11:19:45 -05:00
|
|
|
#[macro_use]
|
|
|
|
extern crate nom_locate;
|
2018-02-04 16:08:30 -06:00
|
|
|
extern crate serde_json;
|
2017-05-05 22:33:25 -05:00
|
|
|
|
2017-09-23 11:19:45 -05:00
|
|
|
#[macro_use]
|
2017-08-12 14:48:28 -05:00
|
|
|
pub mod ast;
|
2018-02-02 15:27:33 -06:00
|
|
|
#[macro_use]
|
2017-10-02 21:32:06 -05:00
|
|
|
pub mod tokenizer;
|
2017-06-07 20:14:40 -05:00
|
|
|
pub mod parse;
|
2017-06-08 22:15:48 -05:00
|
|
|
pub mod build;
|
2017-11-15 22:41:55 -06:00
|
|
|
pub mod convert;
|
2017-12-09 10:02:45 -06:00
|
|
|
pub mod error;
|
|
|
|
|
2017-08-08 21:02:54 -05:00
|
|
|
mod format;
|
|
|
|
|
2017-08-12 14:48:28 -05:00
|
|
|
pub use ast::Value;
|
|
|
|
pub use ast::Expression;
|
|
|
|
pub use ast::Statement;
|
2017-05-05 22:33:25 -05:00
|
|
|
|
2017-06-07 20:14:40 -05:00
|
|
|
pub use parse::parse;
|
2017-06-08 22:15:48 -05:00
|
|
|
pub use build::Builder;
|