From 45b971238071dcd38bb4087d5e1fce072c638c79 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Sat, 12 Aug 2017 14:48:28 -0500 Subject: [PATCH] Some Refactoring and cleanups. * Moved the AST datastructures into their own module. * Collapsed the Expression Enum to just wrappers around structs instead of having actual structs in the definitions. * Added a few more unit tests to ensure nothing got broken. * Added documentation for the new structs. * Added a unifying BinaryExpression Tuple type. --- Cargo.toml | 17 +-- examples/test.ucg | 16 +++ src/ast.rs | 139 ++++++++++++++++++++++ src/build.rs | 297 +++++++++++++++++++++------------------------- src/lib.rs | 8 +- src/main.rs | 39 +++++- src/parse.rs | 224 ++++++++++------------------------ 7 files changed, 407 insertions(+), 333 deletions(-) create mode 100644 examples/test.ucg create mode 100644 src/ast.rs diff --git a/Cargo.toml b/Cargo.toml index 3877e4d..7474783 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,16 +1,17 @@ [package] -name = "ucg" -version = "0.0.1" -authors = ["Jeremy Wall "] +name = "ucg" +version = "0.0.1" +authors = ["Jeremy Wall "] description = "Runs a command on user specified triggers." -repository = "https://github.com/zaphar/ucg" -readme = "README.md" -keywords = ["compiler", "config"] -license = "Apache-2.0" +repository = "https://github.com/zaphar/ucg" +readme = "README.md" +keywords = ["compiler", "config"] +license = "Apache-2.0" [dependencies] -nom = "^2.2" +nom = "^2.2" quick-error = "^1.2.0" +clap = "~2.26.0" [lib] name = "ucglib" diff --git a/examples/test.ucg b/examples/test.ucg new file mode 100644 index 0000000..4c72f43 --- /dev/null +++ b/examples/test.ucg @@ -0,0 +1,16 @@ +let dbhost = "localhost"; +let dbname = "testdb"; + +let mk_db_conn = macro (host, port, db) => { + conn_string = "@:@/@" % (host, port, db) + host = host, + port = port, + db = db, +}; + +let db_conn = mk_db_conn(host, 3306, dbame); + +let server_config = { + dbconn = db_conn.conn_string, + tmpldir = "./templates" +}; \ No newline at end of file diff --git a/src/ast.rs b/src/ast.rs new file mode 100644 index 0000000..76b994e --- /dev/null +++ b/src/ast.rs @@ -0,0 +1,139 @@ +// Copyright 2017 Jeremy Wall +// +// 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. +pub type FieldList = Vec<(String, Expression)>; // str is expected to be a symbol +pub type SelectorList = Vec; // str is expected to always be a symbol. + +/// Value represents a Value in the UCG parsed AST. +#[derive(Debug,PartialEq,Clone)] +pub enum Value { + // Constant Values + Int(i64), + Float(f64), + String(String), + Symbol(String), + // Complex Values + Tuple(FieldList), + Selector(SelectorList), +} + +impl Value { + pub fn type_name(&self) -> String { + match self { + &Value::Int(_) => "Integer".to_string(), + &Value::Float(_) => "Float".to_string(), + &Value::String(_) => "String".to_string(), + &Value::Symbol(_) => "Symbol".to_string(), + &Value::Tuple(_) => "Tuple".to_string(), + &Value::Selector(_) => "Selector".to_string(), + } + } + + fn fields_to_string(v: &FieldList) -> String { + let mut buf = String::new(); + buf.push_str("{\n"); + for ref t in v.iter() { + buf.push_str("\t"); + buf.push_str(&t.0); + buf.push_str("\n"); + } + buf.push_str("}"); + return buf; + } + + pub fn to_string(&self) -> String { + match self { + &Value::Int(ref i) => format!("{}", i), + &Value::Float(ref f) => format!("{}", f), + &Value::String(ref s) => format!("{}", s), + &Value::Symbol(ref s) => format!("{}", s), + &Value::Tuple(ref fs) => format!("{}", Self::fields_to_string(fs)), + &Value::Selector(ref v) => v.join("."), + } + } +} + +/// CallDef represents a call to a Macro that is expected to already have been +/// defined. +#[derive(PartialEq,Debug,Clone)] +pub struct CallDef { + pub macroref: SelectorList, + pub arglist: Vec, +} + +/// SelectDef selects a value from a tuple with a default if the value doesn't +/// exist. +#[derive(PartialEq,Debug,Clone)] +pub struct SelectDef { + pub val: Box, + pub default: Box, + pub tuple: FieldList, +} + +/// MacroDef is a pure function that always returns a Tuple. +/// +/// MacroDef's are not closures. They can not reference +/// any values except what is defined in their arguments. +#[derive(PartialEq,Debug,Clone)] +pub struct MacroDef { + pub argdefs: Vec, + pub fields: FieldList, +} + +/// BinaryExpression represents an expression with a left and a right side. +#[derive(Debug,PartialEq,Clone)] +pub struct BinaryExpression(pub Value, pub Box); + +/// Expression encodes an expression. Expressions compute a value from operands. +#[derive(Debug,PartialEq,Clone)] +pub enum Expression { + // Base Expression + Simple(Value), + + // TODO(jwall): This should probably be all one type :-p + // Binary Expressions + Add(BinaryExpression), + Sub(BinaryExpression), + Mul(BinaryExpression), + Div(BinaryExpression), + + // Complex Expressions + Copy(SelectorList, FieldList), + Grouped(Box), + + Format(String, Vec), + + Call(CallDef), + + Macro(MacroDef), + Select(SelectDef), +} + +/// Statement encodes a parsed Statement in the UCG AST. +#[derive(Debug,PartialEq)] +pub enum Statement { + // simple expression + Expression(Expression), + + // Named bindings + Let { + name: String, + value: Expression, + }, + + // Include a file. + Import { + path: String, + name: String, + }, +} diff --git a/src/build.rs b/src/build.rs index a41be50..c6bce9e 100644 --- a/src/build.rs +++ b/src/build.rs @@ -11,8 +11,6 @@ // 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 parse::{parse,Statement,Expression,Value,FieldList,SelectorList}; - use std::fs::File; use std::io::Read; use std::error::Error; @@ -25,7 +23,38 @@ use std::rc::Rc; use nom; +use ast::*; use format; +use parse::parse; + +impl MacroDef { + pub fn eval(&self, mut args: Vec>) -> Result)>, Box> { + // Error conditions. If the args don't match the length and types of the argdefs then this is + // macro call error. + if args.len() > self.argdefs.len() { + return Err(Box::new( + BuildError::BadArgLen( + "Macro called with too many args".to_string()))); + } + // If the args don't match the types required by the expressions then that is a TypeFail. + // If the expressions reference Symbols not defined in the MacroDef that is also an error. + // TODO(jwall): We should probably enforce that the Expression Symbols must be in argdefs rules + // at Macro definition time not evaluation time. + let mut scope = HashMap::new(); + for (i, arg) in args.drain(0..).enumerate() { + scope.entry(self.argdefs[i].clone()).or_insert(arg.clone()); + } + let b = Builder::new_with_scope(scope); + let mut result: Vec<(String, Rc)> = Vec::new(); + for &(ref key, ref expr) in self.fields.iter() { + // We clone the expressions here because this macro may be consumed + // multiple times in the future. + let val = try!(b.eval_expr(expr.clone())); + result.push((key.clone(), val.clone())); + } + Ok(result) + } +} quick_error! { #[derive(Debug,PartialEq)] @@ -68,46 +97,7 @@ quick_error! { /// BuildResult is the result of a build. type BuildResult = Result<(), Box>; -/// MacroDef is a pure function that always returns a Tuple. -/// -/// MacroDef's are not closures. They can not reference -/// any values except what is defined in their arguments. -#[derive(PartialEq,Debug,Clone)] -pub struct MacroDef { - argdefs: Vec, - fields: FieldList, -} - -impl MacroDef { - fn eval(&self, mut args: Vec>) -> Result)>, Box> { - // Error conditions. If the args don't match the length and types of the argdefs then this is - // macro call error. - if args.len() > self.argdefs.len() { - return Err(Box::new( - BuildError::BadArgLen( - "Macro called with too many args".to_string()))); - } - // If the args don't match the types required by the expressions then that is a TypeFail. - // If the expressions reference Symbols not defined in the MacroDef that is also an error. - // TODO(jwall): We should probably enforce that the Expression Symbols must be in argdefs rules - // at Macro definition time not evaluation time. - let mut scope = HashMap::new(); - for (i, arg) in args.drain(0..).enumerate() { - scope.entry(self.argdefs[i].clone()).or_insert(arg.clone()); - } - let b = Builder::new_with_scope(scope); - let mut result: Vec<(String, Rc)> = Vec::new(); - for &(ref key, ref expr) in self.fields.iter() { - // We clone the expressions here because this macro may be consumed - // multiple times in the future. - let val = try!(b.eval_expr(expr.clone())); - result.push((key.clone(), val.clone())); - } - Ok(result) - } -} - -/// Val is the type of a value for a field in a Tuple. +/// Val is the Intermediate representation of a compiled UCG AST. #[derive(PartialEq,Debug,Clone)] pub enum Val { Int(i64), @@ -277,6 +267,64 @@ impl Builder { Ok(()) } + pub fn build_file_string(&mut self, name: &str, input: String) -> BuildResult { + match parse((&input[..]).as_bytes()) { + nom::IResult::Done(_, mut stmts) => { + for stmt in stmts.drain(0..) { + try!(self.build_stmt(stmt)); + } + Ok(()) + }, + nom::IResult::Error(err) => Err(Box::new(err)), + nom::IResult::Incomplete(_) => Err(Box::new( + BuildError::IncompleteParse(format!("Could not parse input from file: {}", name)))), + } + } + + pub fn build_file(&mut self, name: &str) -> BuildResult { + let mut f = try!(File::open(name)); + let mut s = String::new(); + // TODO(jwall): It would be nice to be able to do this with streaming + try!(f.read_to_string(&mut s)); + self.build_file_string(name, s) + } + + fn build_stmt(&mut self, stmt: Statement) -> BuildResult { + match stmt { + Statement::Let { name: sym, value: expr } => { + let val = try!(self.eval_expr(expr)); + self.last = Some(val.clone()); + match self.out.entry(sym) { + Entry::Occupied(e) => { + return Err(Box::new( + BuildError::DuplicateBinding( + format!("Let binding for {} already exists", e.key())))); + }, + Entry::Vacant(e) => { + e.insert(val); + }, + } + } + Statement::Import { path: val, name: sym } => { + if !self.files.contains(&val) { // Only parse the file once on import. + if self.assets.get(&sym).is_none() { + let mut b = Self::new(); + try!(b.build_file(&val)); + let fields: Vec<(String, Rc)> = b.out.drain().collect(); + let result = Rc::new(Val::Tuple(fields)); + self.assets.entry(sym).or_insert(result.clone()); + self.files.insert(val); + self.last = Some(result); + } + } + } + Statement::Expression(expr) => { + self.last = Some(try!(self.eval_expr(expr))); + } + }; + Ok(()) + } + fn lookup_sym(&self, sym: &str) -> Option> { if self.out.contains_key(sym) { return Some(self.out[sym].clone()); @@ -358,9 +406,9 @@ impl Builder { Expression::Simple(val) => { self.value_to_val(val) }, - Expression::Add(v, expr) => { + Expression::Add(BinaryExpression(v, expr)) => { let expr_result = try!(self.eval_expr(*expr)); - let v = try!(self.value_to_val(*v)); + let v = try!(self.value_to_val(v)); match *v { Val::Int(i) => { eval_binary_expr!(&Val::Int(ii), expr_result, @@ -390,9 +438,9 @@ impl Builder { } } }, - Expression::Sub(v, expr) => { + Expression::Sub(BinaryExpression(v, expr)) => { let expr_result = try!(self.eval_expr(*expr)); - let v = try!(self.value_to_val(*v)); + let v = try!(self.value_to_val(v)); match *v { Val::Int(i) => { eval_binary_expr!(&Val::Int(ii), expr_result, @@ -409,9 +457,9 @@ impl Builder { } } }, - Expression::Mul(v, expr) => { + Expression::Mul(BinaryExpression(v, expr)) => { let expr_result = try!(self.eval_expr(*expr)); - let v = try!(self.value_to_val(*v)); + let v = try!(self.value_to_val(v)); match *v { Val::Int(i) => { eval_binary_expr!(&Val::Int(ii), expr_result, @@ -428,9 +476,9 @@ impl Builder { } } }, - Expression::Div(v, expr) => { + Expression::Div(BinaryExpression(v, expr)) => { let expr_result = try!(self.eval_expr(*expr)); - let v = try!(self.value_to_val(*v)); + let v = try!(self.value_to_val(v)); match *v { Val::Int(i) => { eval_binary_expr!(&Val::Int(ii), expr_result, @@ -505,7 +553,7 @@ impl Builder { let formatter = format::Formatter::new(tmpl, vals); Ok(Rc::new(Val::String(try!(formatter.render())))) }, - Expression::Call{macroref: sel, arglist: mut args} => { + Expression::Call(CallDef{macroref: sel, arglist: mut args}) => { let v = try!(self.lookup_selector(sel)); if let &Val::Macro(ref m) = v.deref() { // Congratulations this is actually a macro. @@ -521,16 +569,12 @@ impl Builder { // We should pretty print the selectors here. format!("{} is not a Macro", v)))) }, - Expression::Macro{arglist: args, tuple: fields} => { + Expression::Macro(def) => { // TODO(jwall): Walk the AST and verify that the symbols all // exist as names in the arglist. - let md = MacroDef{ - argdefs: args, - fields: fields, - }; - Ok(Rc::new(Val::Macro(md))) + Ok(Rc::new(Val::Macro(def))) }, - Expression::Select{val: target, default: def_expr, tuple: mut fields} => { + Expression::Select(SelectDef{val: target, default: def_expr, tuple: mut fields}) => { // First resolve the target expression. let v = try!(self.eval_expr(*target)); // Second ensure that the expression resolves to a string. @@ -554,69 +598,12 @@ impl Builder { } } - pub fn build_file_string(&mut self, name: &str, input: String) -> BuildResult { - match parse((&input[..]).as_bytes()) { - nom::IResult::Done(_, mut stmts) => { - for stmt in stmts.drain(0..) { - try!(self.build_stmt(stmt)); - } - Ok(()) - }, - nom::IResult::Error(err) => Err(Box::new(err)), - nom::IResult::Incomplete(_) => Err(Box::new( - BuildError::IncompleteParse(format!("Could not parse input from file: {}", name)))), - } - } - - pub fn build_file(&mut self, name: &str) -> BuildResult { - let mut f = try!(File::open(name)); - let mut s = String::new(); - // TODO(jwall): It would be nice to be able to do this with streaming - try!(f.read_to_string(&mut s)); - self.build_file_string(name, s) - } - - fn build_stmt(&mut self, stmt: Statement) -> BuildResult { - match stmt { - Statement::Let { name: sym, value: expr } => { - let val = try!(self.eval_expr(expr)); - self.last = Some(val.clone()); - match self.out.entry(sym) { - Entry::Occupied(e) => { - return Err(Box::new( - BuildError::DuplicateBinding( - format!("Let binding for {} already exists", e.key())))); - }, - Entry::Vacant(e) => { - e.insert(val); - }, - } - } - Statement::Import { path: val, name: sym } => { - if !self.files.contains(&val) { // Only parse the file once on import. - if self.assets.get(&sym).is_none() { - let mut b = Self::new(); - try!(b.build_file(&val)); - let fields: Vec<(String, Rc)> = b.out.drain().collect(); - let result = Rc::new(Val::Tuple(fields)); - self.assets.entry(sym).or_insert(result.clone()); - self.files.insert(val); - self.last = Some(result); - } - } - } - Statement::Expression(expr) => { - self.last = Some(try!(self.eval_expr(expr))); - } - }; - Ok(()) - } } #[cfg(test)] mod test { - use super::{Builder,Val,MacroDef}; - use parse::{Statement,Expression,Value}; + use super::{Builder, Val, MacroDef, SelectDef, CallDef}; + use ast::*; use std::rc::Rc; fn test_expr_to_val(mut cases: Vec<(Expression,Val)>, b: Builder) { @@ -627,11 +614,11 @@ mod test { #[test] fn test_eval_div_expr() { - let mut b = Builder::new(); + let b = Builder::new(); test_expr_to_val(vec![ - (Expression::Div(Box::new(Value::Int(2)), Box::new(Expression::Simple(Value::Int(2)))), + (Expression::Div(BinaryExpression(Value::Int(2), Box::new(Expression::Simple(Value::Int(2))))), Val::Int(1)), - (Expression::Div(Box::new(Value::Float(2.0)), Box::new(Expression::Simple(Value::Float(2.0)))), + (Expression::Div(BinaryExpression(Value::Float(2.0), Box::new(Expression::Simple(Value::Float(2.0))))), Val::Float(1.0)), ], b); } @@ -639,20 +626,20 @@ mod test { #[test] #[should_panic(expected = "Expected Float")] fn test_eval_div_expr_fail() { - let mut b = Builder::new(); + let b = Builder::new(); test_expr_to_val(vec![ - (Expression::Div(Box::new(Value::Float(2.0)), Box::new(Expression::Simple(Value::Int(2)))), + (Expression::Div(BinaryExpression(Value::Float(2.0), Box::new(Expression::Simple(Value::Int(2))))), Val::Float(1.0)), ], b); } #[test] fn test_eval_mul_expr() { - let mut b = Builder::new(); + let b = Builder::new(); test_expr_to_val(vec![ - (Expression::Mul(Box::new(Value::Int(2)), Box::new(Expression::Simple(Value::Int(2)))), + (Expression::Mul(BinaryExpression(Value::Int(2), Box::new(Expression::Simple(Value::Int(2))))), Val::Int(4)), - (Expression::Mul(Box::new(Value::Float(2.0)), Box::new(Expression::Simple(Value::Float(2.0)))), + (Expression::Mul(BinaryExpression(Value::Float(2.0), Box::new(Expression::Simple(Value::Float(2.0))))), Val::Float(4.0)), ], b); } @@ -660,23 +647,20 @@ mod test { #[test] #[should_panic(expected = "Expected Float")] fn test_eval_mul_expr_fail() { - let mut b = Builder::new(); + let b = Builder::new(); test_expr_to_val(vec![ - (Expression::Mul(Box::new(Value::Float(2.0)), - Box::new(Expression::Simple(Value::Int(2)))), + (Expression::Mul(BinaryExpression(Value::Float(2.0), Box::new(Expression::Simple(Value::Int(2))))), Val::Float(1.0)), ], b); } #[test] fn test_eval_subtract_expr() { - let mut b = Builder::new(); + let b = Builder::new(); test_expr_to_val(vec![ - (Expression::Sub(Box::new(Value::Int(2)), - Box::new(Expression::Simple(Value::Int(1)))), + (Expression::Sub(BinaryExpression(Value::Int(2), Box::new(Expression::Simple(Value::Int(1))))), Val::Int(1)), - (Expression::Sub(Box::new(Value::Float(2.0)), - Box::new(Expression::Simple(Value::Float(1.0)))), + (Expression::Sub(BinaryExpression(Value::Float(2.0), Box::new(Expression::Simple(Value::Float(1.0))))), Val::Float(1.0)), ], b); } @@ -684,26 +668,22 @@ mod test { #[test] #[should_panic(expected = "Expected Float")] fn test_eval_subtract_expr_fail() { - let mut b = Builder::new(); + let b = Builder::new(); test_expr_to_val(vec![ - (Expression::Sub(Box::new(Value::Float(2.0)), - Box::new(Expression::Simple(Value::Int(2)))), + (Expression::Sub(BinaryExpression(Value::Float(2.0), Box::new(Expression::Simple(Value::Int(2))))), Val::Float(1.0)), ], b); } #[test] fn test_eval_add_expr() { - let mut b = Builder::new(); + let b = Builder::new(); test_expr_to_val(vec![ - (Expression::Add(Box::new(Value::Int(1)), - Box::new(Expression::Simple(Value::Int(1)))), + (Expression::Add(BinaryExpression(Value::Int(1), Box::new(Expression::Simple(Value::Int(1))))), Val::Int(2)), - (Expression::Add(Box::new(Value::Float(1.0)), - Box::new(Expression::Simple(Value::Float(1.0)))), + (Expression::Add(BinaryExpression(Value::Float(1.0), Box::new(Expression::Simple(Value::Float(1.0))))), Val::Float(2.0)), - (Expression::Add(Box::new(Value::String("foo".to_string())), - Box::new(Expression::Simple(Value::String("bar".to_string())))), + (Expression::Add(BinaryExpression(Value::String("foo".to_string()), Box::new(Expression::Simple(Value::String("bar".to_string()))))), Val::String("foobar".to_string())), ], b); } @@ -711,10 +691,9 @@ mod test { #[test] #[should_panic(expected = "Expected Float")] fn test_eval_add_expr_fail() { - let mut b = Builder::new(); + let b = Builder::new(); test_expr_to_val(vec![ - (Expression::Add(Box::new(Value::Float(2.0)), - Box::new(Expression::Simple(Value::Int(2)))), + (Expression::Add(BinaryExpression(Value::Float(2.0), Box::new(Expression::Simple(Value::Int(2))))), Val::Float(1.0)), ], b); } @@ -795,7 +774,7 @@ mod test { #[test] #[should_panic(expected = "Unable to find Symbol tpl1")] fn test_expr_copy_no_such_tuple() { - let mut b = Builder::new(); + let b = Builder::new(); test_expr_to_val(vec![ (Expression::Copy(vec!["tpl1".to_string()], Vec::new()), Val::Tuple(Vec::new())), @@ -889,10 +868,10 @@ mod test { ], }))); test_expr_to_val(vec![ - (Expression::Call{ + (Expression::Call(CallDef{ macroref: vec!["tstmac".to_string()], arglist: vec![Expression::Simple(Value::String("bar".to_string()))], - }, + }), Val::Tuple(vec![ ("foo".to_string(), Rc::new(Val::String("bar".to_string()))), ])), @@ -911,10 +890,10 @@ mod test { ], }))); test_expr_to_val(vec![ - (Expression::Call{ + (Expression::Call(CallDef{ macroref: vec!["tstmac".to_string()], arglist: vec![Expression::Simple(Value::String("bar".to_string()))], - }, + }), Val::Tuple(vec![ ("foo".to_string(), Rc::new(Val::String("bar".to_string()))), ])), @@ -927,23 +906,23 @@ mod test { b.out.entry("foo".to_string()).or_insert(Rc::new(Val::String("bar".to_string()))); b.out.entry("baz".to_string()).or_insert(Rc::new(Val::String("boo".to_string()))); test_expr_to_val(vec![ - (Expression::Select{ + (Expression::Select(SelectDef{ val: Box::new(Expression::Simple(Value::Symbol("foo".to_string()))), default: Box::new(Expression::Simple(Value::Int(1))), tuple: vec![ ("bar".to_string(), Expression::Simple(Value::Int(2))), ("quux".to_string(), Expression::Simple(Value::String("2".to_string()))), ], - }, + }), Val::Int(2)), - (Expression::Select{ + (Expression::Select(SelectDef{ val: Box::new(Expression::Simple(Value::Symbol("baz".to_string()))), default: Box::new(Expression::Simple(Value::Int(1))), tuple: vec![ ("bar".to_string(), Expression::Simple(Value::Int(2))), ("quux".to_string(), Expression::Simple(Value::String("2".to_string()))), ], - }, + }), // If the field doesn't exist then we get the default. Val::Int(1)), ], b); @@ -955,14 +934,14 @@ mod test { let mut b = Builder::new(); b.out.entry("foo".to_string()).or_insert(Rc::new(Val::Int(4))); test_expr_to_val(vec![ - (Expression::Select{ + (Expression::Select(SelectDef{ val: Box::new(Expression::Simple(Value::Symbol("foo".to_string()))), default: Box::new(Expression::Simple(Value::Int(1))), tuple: vec![ ("bar".to_string(), Expression::Simple(Value::Int(2))), ("quux".to_string(), Expression::Simple(Value::String("2".to_string()))), ], - }, + }), Val::Int(2)), ], b); } @@ -973,7 +952,7 @@ mod test { b.build_stmt(Statement::Let{ name:"foo".to_string(), value: Expression::Simple(Value::String("bar".to_string())), - }); + }).unwrap(); test_expr_to_val(vec![ (Expression::Simple(Value::Symbol("foo".to_string())), Val::String("bar".to_string())), @@ -983,7 +962,7 @@ mod test { #[test] fn test_build_file_string() { let mut b = Builder::new(); - b.build_file_string("foo.ucg", "let foo = 1;".to_string()); + b.build_file_string("foo.ucg", "let foo = 1;".to_string()).unwrap(); assert!(b.out.contains_key("foo")); } diff --git a/src/lib.rs b/src/lib.rs index 679cc99..fddc0ab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,14 +17,14 @@ extern crate nom; #[macro_use] extern crate quick_error; +pub mod ast; pub mod parse; pub mod build; - mod format; -pub use parse::Value; -pub use parse::Expression; -pub use parse::Statement; +pub use ast::Value; +pub use ast::Expression; +pub use ast::Statement; pub use parse::parse; pub use build::Builder; diff --git a/src/main.rs b/src/main.rs index 7c9aaa4..5b5b000 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,8 +11,43 @@ // 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. +#[macro_use] +extern crate clap; + extern crate ucglib; -fn main() { - println!("hello world"); +use ucglib::build; + +fn do_flags<'a>() -> clap::ArgMatches<'a> { + clap_app!( + ucg => + (version: crate_version!()) + (author: crate_authors!()) + (about: "Universal Configuration Grammar compiler.") + (@subcommand build => + (about: "Compile a specific ucg file.") + (@arg INPUT: +required "Input ucg file to build.") + ) + (@subcommand validate => + (about: "Check a specific ucg file for errors.") + (@arg INPUT: +required "Input ucg file to validate.") + ) + ) + .get_matches() +} + +fn main() { + // TODO(jwall): Read and build an actual file. + let app = do_flags(); + if let Some(matches) = app.subcommand_matches("build") { + let file = matches.value_of("INPUT").unwrap(); + let mut builder = build::Builder::new(); + builder.build_file(file).unwrap(); + println!("Build successful"); + } else if let Some(matches) = app.subcommand_matches("validate") { + let file = matches.value_of("INPUT").unwrap(); + let mut builder = build::Builder::new(); + builder.build_file(file).unwrap(); + println!("File Validates"); + } } diff --git a/src/parse.rs b/src/parse.rs index 3c9c64d..73810e9 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -30,114 +30,10 @@ use std::error::Error; use nom::{alpha, is_alphanumeric, digit}; +use ast::*; + type ParseResult = Result>; -pub type FieldList = Vec<(String, Expression)>; // str is expected to be a symbol -pub type SelectorList = Vec; // str is expected to always be a symbol. - -/// Value represents a Value in the UCG parsed AST. -#[derive(Debug,PartialEq,Clone)] -pub enum Value { - // Constant Values - Int(i64), - Float(f64), - String(String), - Symbol(String), - // Complex Values - Tuple(FieldList), - Selector(SelectorList), -} - -impl Value { - pub fn type_name(&self) -> String { - match self { - &Value::Int(_) => "Integer".to_string(), - &Value::Float(_) => "Float".to_string(), - &Value::String(_) => "String".to_string(), - &Value::Symbol(_) => "Symbol".to_string(), - &Value::Tuple(_) => "Tuple".to_string(), - &Value::Selector(_) => "Selector".to_string(), - } - } - - fn fields_to_string(v: &FieldList) -> String { - let mut buf = String::new(); - buf.push_str("{\n"); - for ref t in v.iter() { - buf.push_str("\t"); - buf.push_str(&t.0); - buf.push_str("\n"); - } - buf.push_str("}"); - return buf; - } - - pub fn to_string(&self) -> String { - match self { - &Value::Int(ref i) => format!("{}", i), - &Value::Float(ref f) => format!("{}", f), - &Value::String(ref s) => format!("{}", s), - &Value::Symbol(ref s) => format!("{}", s), - &Value::Tuple(ref fs) => format!("{}", Self::fields_to_string(fs)), - &Value::Selector(ref v) => v.join("."), - } - } -} - -/// Expression encodes an expression. Expressions compute a value from operands. -#[derive(Debug,PartialEq,Clone)] -pub enum Expression { - // Base Expression - Simple(Value), - - // Binary Expressions - Add(Box, Box), - Sub(Box, Box), - Mul(Box, Box), - Div(Box, Box), - - // Complex Expressions - Copy(SelectorList, FieldList), - Grouped(Box), - - Format(String, Vec), - - Call { - macroref: SelectorList, - arglist: Vec, - }, - - Macro { - arglist: Vec, - tuple: FieldList, - }, - - Select { - val: Box, - default: Box, - tuple: FieldList, - }, -} - -/// Statement encodes a parsed Statement in the UCG AST. -#[derive(Debug,PartialEq)] -pub enum Statement { - // simple expression - Expression(Expression), - - // Named bindings - Let { - name: String, - value: Expression, - }, - - // Include a file. - Import { - path: String, - name: String, - }, -} - // sentinels and punctuation named!(doublequote, tag!("\"")); named!(comma, tag!(",")); @@ -305,7 +201,7 @@ named!(simple_expression, ); fn tuple_to_add_expression(tpl: (Value, Expression)) -> ParseResult { - Ok(Expression::Add(Box::new(tpl.0), Box::new(tpl.1))) + Ok(Expression::Add(BinaryExpression(tpl.0, Box::new(tpl.1)))) } named!(add_expression, @@ -321,7 +217,7 @@ named!(add_expression, ); fn tuple_to_sub_expression(tpl: (Value, Expression)) -> ParseResult { - Ok(Expression::Sub(Box::new(tpl.0), Box::new(tpl.1))) + Ok(Expression::Sub(BinaryExpression(tpl.0, Box::new(tpl.1)))) } named!(sub_expression, @@ -337,7 +233,7 @@ named!(sub_expression, ); fn tuple_to_mul_expression(tpl: (Value, Expression)) -> ParseResult { - Ok(Expression::Mul(Box::new(tpl.0), Box::new(tpl.1))) + Ok(Expression::Mul(BinaryExpression(tpl.0, Box::new(tpl.1)))) } named!(mul_expression, @@ -353,7 +249,7 @@ named!(mul_expression, ); fn tuple_to_div_expression(tpl: (Value, Expression)) -> ParseResult { - Ok(Expression::Div(Box::new(tpl.0), Box::new(tpl.1))) + Ok(Expression::Div(BinaryExpression(tpl.0, Box::new(tpl.1)))) } named!(div_expression, @@ -401,10 +297,10 @@ named!(copy_expression, fn tuple_to_macro(mut t: (Vec, Value)) -> ParseResult { match t.1 { Value::Tuple(v) => { - Ok(Expression::Macro { - arglist: t.0.drain(0..).map(|s| s.to_string()).collect(), - tuple: v, - }) + Ok(Expression::Macro(MacroDef { + argdefs: t.0.drain(0..).map(|s| s.to_string()).collect(), + fields: v, + })) } // TODO(jwall): Show a better version of the unexpected parsed value. val => { @@ -434,11 +330,11 @@ fn tuple_to_select(t: (Expression, Expression, Value)) -> ParseResult { match t.2 { Value::Tuple(v) => { - Ok(Expression::Select { + Ok(Expression::Select(SelectDef{ val: Box::new(t.0), default: Box::new(t.1), tuple: v, - }) + })) } // TODO(jwall): Show a better version of the unexpected parsed value. val => { @@ -480,10 +376,10 @@ named!(format_expression, fn tuple_to_call(t: (Value, Vec)) -> ParseResult { if let Value::Selector(sl) = t.0 { - Ok(Expression::Call { + Ok(Expression::Call(CallDef{ macroref: sl, arglist: t.1, - }) + })) } else { Err(Box::new(ParseError::UnexpectedToken("Selector".to_string(), format!("{:?}", t.0)))) } @@ -605,12 +501,13 @@ named!(pub parse >, many1!(ws!(statement))); #[cfg(test)] mod test { use std::str::from_utf8; - use super::{Statement, Expression, Value}; + use super::{Statement, Expression, Value, MacroDef, SelectDef, CallDef}; use super::{number, symbol, parse, field_value, tuple, grouped_expression}; use super::{arglist, copy_expression, macro_expression, select_expression}; use super::{format_expression, call_expression, expression}; use super::{expression_statement, let_statement, import_statement, statement}; use nom::IResult; + use ast::*; #[test] fn test_statement_parse() { @@ -725,70 +622,70 @@ mod test { IResult::Done(&b""[..], Expression::Simple(Value::Symbol("foo".to_string())))); assert_eq!(expression(&b"1 + 1"[..]), IResult::Done(&b""[..], - Expression::Add(Box::new(Value::Int(1)), - Box::new(Expression::Simple(Value::Int(1)))))); + Expression::Add(BinaryExpression(Value::Int(1), + Box::new(Expression::Simple(Value::Int(1))))))); assert_eq!(expression(&b"1 - 1"[..]), IResult::Done(&b""[..], - Expression::Sub(Box::new(Value::Int(1)), - Box::new(Expression::Simple(Value::Int(1)))))); + Expression::Sub(BinaryExpression(Value::Int(1), + Box::new(Expression::Simple(Value::Int(1))))))); assert_eq!(expression(&b"1 * 1"[..]), IResult::Done(&b""[..], - Expression::Mul(Box::new(Value::Int(1)), - Box::new(Expression::Simple(Value::Int(1)))))); + Expression::Mul(BinaryExpression(Value::Int(1), + Box::new(Expression::Simple(Value::Int(1))))))); assert_eq!(expression(&b"1 / 1"[..]), IResult::Done(&b""[..], - Expression::Div(Box::new(Value::Int(1)), - Box::new(Expression::Simple(Value::Int(1)))))); + Expression::Div(BinaryExpression(Value::Int(1), + Box::new(Expression::Simple(Value::Int(1))))))); assert_eq!(expression(&b"1+1"[..]), IResult::Done(&b""[..], - Expression::Add(Box::new(Value::Int(1)), - Box::new(Expression::Simple(Value::Int(1)))))); + Expression::Add(BinaryExpression(Value::Int(1), + Box::new(Expression::Simple(Value::Int(1))))))); assert_eq!(expression(&b"1-1"[..]), IResult::Done(&b""[..], - Expression::Sub(Box::new(Value::Int(1)), - Box::new(Expression::Simple(Value::Int(1)))))); + Expression::Sub(BinaryExpression(Value::Int(1), + Box::new(Expression::Simple(Value::Int(1))))))); assert_eq!(expression(&b"1*1"[..]), IResult::Done(&b""[..], - Expression::Mul(Box::new(Value::Int(1)), - Box::new(Expression::Simple(Value::Int(1)))))); + Expression::Mul(BinaryExpression(Value::Int(1), + Box::new(Expression::Simple(Value::Int(1))))))); assert_eq!(expression(&b"1/1"[..]), IResult::Done(&b""[..], - Expression::Div(Box::new(Value::Int(1)), - Box::new(Expression::Simple(Value::Int(1)))))); + Expression::Div(BinaryExpression(Value::Int(1), + Box::new(Expression::Simple(Value::Int(1))))))); assert_eq!(expression(&b"macro (arg1, arg2) => { foo = arg1 }"[..]), IResult::Done(&b""[..], - Expression::Macro{ - arglist: vec![ + Expression::Macro(MacroDef{ + argdefs: vec![ "arg1".to_string(), "arg2".to_string(), ], - tuple: vec![ + fields: vec![ ("foo".to_string(), Expression::Simple(Value::Symbol("arg1".to_string()))), ], - } + }) ) ); assert_eq!(expression(&b"select foo, 1, { foo = 2 };"[..]), IResult::Done(&b""[..], - Expression::Select{ + Expression::Select(SelectDef{ val: Box::new(Expression::Simple(Value::Symbol("foo".to_string()))), default: Box::new(Expression::Simple(Value::Int(1))), tuple: vec![ ("foo".to_string(), Expression::Simple(Value::Int(2))) ] - } + }) ) ); assert_eq!(expression(&b"foo.bar (1, \"foo\")"[..]), IResult::Done(&b""[..], - Expression::Call{ + Expression::Call(CallDef{ macroref: vec!["foo".to_string(),"bar".to_string()], arglist: vec![ Expression::Simple(Value::Int(1)), Expression::Simple(Value::String("foo".to_string())), ], - } + }) ) ); assert_eq!(expression(&b"(1 + 1)"[..]), @@ -796,8 +693,8 @@ mod test { Expression::Grouped( Box::new( Expression::Add( - Box::new(Value::Int(1)), - Box::new(Expression::Simple(Value::Int(1))) + BinaryExpression(Value::Int(1), + Box::new(Expression::Simple(Value::Int(1)))) ) ) ) @@ -819,6 +716,13 @@ mod test { Expression::Simple(Value::Int(2))]) ) ); + assert_eq!(format_expression(&b"\"foo @ @\"%(1, 2)"[..]), + IResult::Done(&b""[..], + Expression::Format("foo @ @".to_string(), + vec![Expression::Simple(Value::Int(1)), + Expression::Simple(Value::Int(2))]) + ) + ); } #[test] @@ -831,25 +735,25 @@ mod test { assert_eq!(call_expression(&b"foo (1, \"foo\")"[..]), IResult::Done(&b""[..], - Expression::Call{ + Expression::Call(CallDef{ macroref: vec!["foo".to_string()], arglist: vec![ Expression::Simple(Value::Int(1)), Expression::Simple(Value::String("foo".to_string())), ], - } + }) ) ); assert_eq!(call_expression(&b"foo.bar (1, \"foo\")"[..]), IResult::Done(&b""[..], - Expression::Call{ + Expression::Call(CallDef{ macroref: vec!["foo".to_string(),"bar".to_string()], arglist: vec![ Expression::Simple(Value::Int(1)), Expression::Simple(Value::String("foo".to_string())), ], - } + }) ) ); } @@ -863,13 +767,13 @@ mod test { assert_eq!(select_expression(&b"select foo, 1, { foo = 2 };"[..]), IResult::Done(&b""[..], - Expression::Select{ + Expression::Select(SelectDef{ val: Box::new(Expression::Simple(Value::Symbol("foo".to_string()))), default: Box::new(Expression::Simple(Value::Int(1))), tuple: vec![ ("foo".to_string(), Expression::Simple(Value::Int(2))) ] - } + }) ) ); } @@ -890,13 +794,13 @@ mod test { assert_eq!(macro_expression(&b"macro (arg1, arg2) => {foo=1,bar=2}"[..]), IResult::Done(&b""[..], - Expression::Macro{ - arglist: vec!["arg1".to_string(), + Expression::Macro(MacroDef{ + argdefs: vec!["arg1".to_string(), "arg2".to_string()], - tuple: vec![("foo".to_string(), Expression::Simple(Value::Int(1))), + fields: vec![("foo".to_string(), Expression::Simple(Value::Int(1))), ("bar".to_string(), Expression::Simple(Value::Int(2))) ] - } + }) ) ); } @@ -947,9 +851,9 @@ mod test { Expression::Grouped( Box::new( Expression::Add( - Box::new(Value::Int(1)), - Box::new(Expression::Simple( - Value::Int(1))) + BinaryExpression(Value::Int(1), + Box::new(Expression::Simple( + Value::Int(1)))) ) ) ) @@ -1058,8 +962,8 @@ mod test { value: Expression::Simple(Value::Int(1)) }, Statement::Expression( - Expression::Add(Box::new(Value::Int(1)), - Box::new(Expression::Simple(Value::Int(1)))) + Expression::Add(BinaryExpression(Value::Int(1), + Box::new(Expression::Simple(Value::Int(1))))) ) ]); }