Add json as an output type.

* Uses serde_json
* Doesn't handle macro values very well.
* Handles maps and lists just fine.
* doesn't pretty print the values though.
This commit is contained in:
Jeremy Wall 2018-02-04 16:08:30 -06:00
parent 97c97ced55
commit 5fba06d71f
7 changed files with 101 additions and 6 deletions

View File

@ -14,6 +14,7 @@ version = "^3.2"
[dependencies] [dependencies]
nom_locate = "^0.1.1" nom_locate = "^0.1.1"
clap = "~2.26.0" clap = "~2.26.0"
serde_json = "~1.0.9"
[lib] [lib]
name = "ucglib" name = "ucglib"

View File

@ -5,11 +5,12 @@
You should be able to ask the compiler to tell you any value or set of values in the You should be able to ask the compiler to tell you any value or set of values in the
compiled configuration. compiled configuration.
## Translation Language (Experiemental) ## Translation Language (Experimental)
For some configuration file formats we need a way to specify a particular For some configuration file formats we need a way to specify a particular
organiztion for a given configuration structure. Some options here could be organiztion for a given configuration structure. Some options here could be
* Simple data export (json)
* A Functional Transform similar to xslt or css transforms. * A Functional Transform similar to xslt or css transforms.
* A Templating language * A Templating language
* Annotations. * Annotations.

View File

@ -25,9 +25,7 @@ impl FlagConverter {
pub fn new() -> Self { pub fn new() -> Self {
FlagConverter {} FlagConverter {}
} }
}
impl FlagConverter {
fn write(&self, v: &Val, w: &mut Write) -> Result<()> { fn write(&self, v: &Val, w: &mut Write) -> Result<()> {
match v { match v {
&Val::Float(ref f) => { &Val::Float(ref f) => {
@ -45,6 +43,10 @@ impl FlagConverter {
} }
&Val::Tuple(ref flds) => { &Val::Tuple(ref flds) => {
for &(ref name, ref val) in flds.iter() { for &(ref name, ref val) in flds.iter() {
if val.is_tuple() {
eprintln!("Skipping embedded tuple...");
return Ok(());
}
try!(write!(w, "--{} ", name.val)); try!(write!(w, "--{} ", name.val));
// TODO(jwall): What if the value is a tuple? // TODO(jwall): What if the value is a tuple?
try!(self.write(&val, w)); try!(self.write(&val, w));
@ -61,6 +63,6 @@ impl FlagConverter {
impl Converter for FlagConverter { impl Converter for FlagConverter {
fn convert(&self, v: Rc<Val>, mut w: Box<Write>) -> Result<()> { fn convert(&self, v: Rc<Val>, mut w: Box<Write>) -> Result<()> {
return self.write(&v, &mut w); self.write(&v, &mut w)
} }
} }

86
src/convert/json.rs Normal file
View File

@ -0,0 +1,86 @@
// 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.
use std::rc::Rc;
use std::io::Write;
use std::io::Result;
use serde_json;
use ast;
use build::Val;
use convert::traits::Converter;
pub struct JsonConverter {}
impl JsonConverter {
pub fn new() -> Self {
JsonConverter {}
}
fn convert_list(&self, items: &Vec<Rc<Val>>) -> Result<serde_json::Value> {
let mut v = Vec::new();
for val in items.iter() {
v.push(try!(self.convert_value(val)));
}
Ok(serde_json::Value::Array(v))
}
fn convert_tuple(&self, items: &Vec<(ast::Positioned<String>, Rc<Val>)>) -> Result<serde_json::Value> {
let mut mp = serde_json::Map::new();
for &(ref k, ref v) in items.iter() {
mp.entry(k.val.clone()).or_insert(try!(self.convert_value(v)));
}
Ok(serde_json::Value::Object(mp))
}
fn convert_value(&self, v: &Val) -> Result<serde_json::Value> {
let jsn_val = match v {
&Val::Float(f) => {
let n = match serde_json::Number::from_f64(f) {
Some(n) => n,
// In theory this should never happen. But on the off chance that it does...
None => panic!("Float is too large or Not a Number {}", f),
};
serde_json::Value::Number(n)
},
&Val::Int(i) => {
let n = match serde_json::Number::from_f64(i as f64) {
Some(n) => n,
// In theory this should never happen. But on the off chance that it does...
None => panic!("Float is too large or Not a Number {}", i),
};
serde_json::Value::Number(n)
},
&Val::String(ref s) => serde_json::Value::String(s.clone()),
&Val::Macro(_) => {
// TODO(jwall): We probably want to actually skip this but for now
// we'll use null
eprintln!("Skipping macro encoding as null...");
serde_json::Value::Null
},
&Val::List(ref l) => try!(self.convert_list(l)),
&Val::Tuple(ref t) => try!(self.convert_tuple(t)),
};
Ok(jsn_val)
}
fn write(&self, v: &Val, w: &mut Write) -> Result<()> {
let jsn_val = try!(self.convert_value(v));
try!(serde_json::to_writer(w, &jsn_val));
Ok(())
}
}
impl Converter for JsonConverter {
fn convert(&self, v: Rc<Val>, mut w: Box<Write>) -> Result<()> {
self.write(&v, &mut w)
}
}

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
pub mod flags; pub mod flags;
pub mod json;
pub mod traits; pub mod traits;
use std::io; use std::io;
@ -29,6 +30,9 @@ impl ConverterRunner {
if typ == "flags" { if typ == "flags" {
return Ok(ConverterRunner { converter: Box::new(flags::FlagConverter::new()) }); return Ok(ConverterRunner { converter: Box::new(flags::FlagConverter::new()) });
} }
if typ == "json" {
return Ok(ConverterRunner { converter: Box::new(json::JsonConverter::new()) });
}
return Err(format!("Unknown Target output type: {}", typ)); return Err(format!("Unknown Target output type: {}", typ));
} }

View File

@ -17,6 +17,7 @@
extern crate nom; extern crate nom;
#[macro_use] #[macro_use]
extern crate nom_locate; extern crate nom_locate;
extern crate serde_json;
#[macro_use] #[macro_use]
pub mod ast; pub mod ast;

View File

@ -34,7 +34,7 @@ fn do_flags<'a>() -> clap::ArgMatches<'a> {
(@subcommand build => (@subcommand build =>
(about: "Compile a specific ucg file.") (about: "Compile a specific ucg file.")
(@arg sym: --sym +takes_value "Specify a specific let binding in the ucg file to output.") (@arg sym: --sym +takes_value "Specify a specific let binding in the ucg file to output.")
(@arg target: --target -t +required +takes_value "Target output type.") (@arg target: --target -t +required +takes_value "Target output type. (flags, json)")
(@arg out: --out -o +takes_value "Output file to write to.") (@arg out: --out -o +takes_value "Output file to write to.")
(@arg INPUT: +required "Input ucg file to build.") (@arg INPUT: +required "Input ucg file to build.")
) )