mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-22 18:19:54 -04:00
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.
This commit is contained in:
parent
9b367fa2d2
commit
45b9712380
17
Cargo.toml
17
Cargo.toml
@ -1,16 +1,17 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ucg"
|
name = "ucg"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
authors = ["Jeremy Wall <jeremy@marzhillstudios.com>"]
|
authors = ["Jeremy Wall <jeremy@marzhillstudios.com>"]
|
||||||
description = "Runs a command on user specified triggers."
|
description = "Runs a command on user specified triggers."
|
||||||
repository = "https://github.com/zaphar/ucg"
|
repository = "https://github.com/zaphar/ucg"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
keywords = ["compiler", "config"]
|
keywords = ["compiler", "config"]
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nom = "^2.2"
|
nom = "^2.2"
|
||||||
quick-error = "^1.2.0"
|
quick-error = "^1.2.0"
|
||||||
|
clap = "~2.26.0"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "ucglib"
|
name = "ucglib"
|
||||||
|
16
examples/test.ucg
Normal file
16
examples/test.ucg
Normal file
@ -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"
|
||||||
|
};
|
139
src/ast.rs
Normal file
139
src/ast.rs
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
// 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.
|
||||||
|
pub type FieldList = Vec<(String, Expression)>; // str is expected to be a symbol
|
||||||
|
pub type SelectorList = Vec<String>; // 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<Expression>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<Expression>,
|
||||||
|
pub default: Box<Expression>,
|
||||||
|
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<String>,
|
||||||
|
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>);
|
||||||
|
|
||||||
|
/// 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<Expression>),
|
||||||
|
|
||||||
|
Format(String, Vec<Expression>),
|
||||||
|
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
}
|
297
src/build.rs
297
src/build.rs
@ -11,8 +11,6 @@
|
|||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// 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.
|
||||||
use parse::{parse,Statement,Expression,Value,FieldList,SelectorList};
|
|
||||||
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
@ -25,7 +23,38 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
use nom;
|
use nom;
|
||||||
|
|
||||||
|
use ast::*;
|
||||||
use format;
|
use format;
|
||||||
|
use parse::parse;
|
||||||
|
|
||||||
|
impl MacroDef {
|
||||||
|
pub fn eval(&self, mut args: Vec<Rc<Val>>) -> Result<Vec<(String, Rc<Val>)>, Box<Error>> {
|
||||||
|
// 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<Val>)> = 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! {
|
quick_error! {
|
||||||
#[derive(Debug,PartialEq)]
|
#[derive(Debug,PartialEq)]
|
||||||
@ -68,46 +97,7 @@ quick_error! {
|
|||||||
/// BuildResult is the result of a build.
|
/// BuildResult is the result of a build.
|
||||||
type BuildResult = Result<(), Box<Error>>;
|
type BuildResult = Result<(), Box<Error>>;
|
||||||
|
|
||||||
/// MacroDef is a pure function that always returns a Tuple.
|
/// Val is the Intermediate representation of a compiled UCG AST.
|
||||||
///
|
|
||||||
/// 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<String>,
|
|
||||||
fields: FieldList,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MacroDef {
|
|
||||||
fn eval(&self, mut args: Vec<Rc<Val>>) -> Result<Vec<(String, Rc<Val>)>, Box<Error>> {
|
|
||||||
// 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<Val>)> = 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.
|
|
||||||
#[derive(PartialEq,Debug,Clone)]
|
#[derive(PartialEq,Debug,Clone)]
|
||||||
pub enum Val {
|
pub enum Val {
|
||||||
Int(i64),
|
Int(i64),
|
||||||
@ -277,6 +267,64 @@ impl Builder {
|
|||||||
Ok(())
|
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<Val>)> = 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<Rc<Val>> {
|
fn lookup_sym(&self, sym: &str) -> Option<Rc<Val>> {
|
||||||
if self.out.contains_key(sym) {
|
if self.out.contains_key(sym) {
|
||||||
return Some(self.out[sym].clone());
|
return Some(self.out[sym].clone());
|
||||||
@ -358,9 +406,9 @@ impl Builder {
|
|||||||
Expression::Simple(val) => {
|
Expression::Simple(val) => {
|
||||||
self.value_to_val(val)
|
self.value_to_val(val)
|
||||||
},
|
},
|
||||||
Expression::Add(v, expr) => {
|
Expression::Add(BinaryExpression(v, expr)) => {
|
||||||
let expr_result = try!(self.eval_expr(*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 {
|
match *v {
|
||||||
Val::Int(i) => {
|
Val::Int(i) => {
|
||||||
eval_binary_expr!(&Val::Int(ii), expr_result,
|
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 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 {
|
match *v {
|
||||||
Val::Int(i) => {
|
Val::Int(i) => {
|
||||||
eval_binary_expr!(&Val::Int(ii), expr_result,
|
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 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 {
|
match *v {
|
||||||
Val::Int(i) => {
|
Val::Int(i) => {
|
||||||
eval_binary_expr!(&Val::Int(ii), expr_result,
|
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 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 {
|
match *v {
|
||||||
Val::Int(i) => {
|
Val::Int(i) => {
|
||||||
eval_binary_expr!(&Val::Int(ii), expr_result,
|
eval_binary_expr!(&Val::Int(ii), expr_result,
|
||||||
@ -505,7 +553,7 @@ impl Builder {
|
|||||||
let formatter = format::Formatter::new(tmpl, vals);
|
let formatter = format::Formatter::new(tmpl, vals);
|
||||||
Ok(Rc::new(Val::String(try!(formatter.render()))))
|
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));
|
let v = try!(self.lookup_selector(sel));
|
||||||
if let &Val::Macro(ref m) = v.deref() {
|
if let &Val::Macro(ref m) = v.deref() {
|
||||||
// Congratulations this is actually a macro.
|
// Congratulations this is actually a macro.
|
||||||
@ -521,16 +569,12 @@ impl Builder {
|
|||||||
// We should pretty print the selectors here.
|
// We should pretty print the selectors here.
|
||||||
format!("{} is not a Macro", v))))
|
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
|
// TODO(jwall): Walk the AST and verify that the symbols all
|
||||||
// exist as names in the arglist.
|
// exist as names in the arglist.
|
||||||
let md = MacroDef{
|
Ok(Rc::new(Val::Macro(def)))
|
||||||
argdefs: args,
|
|
||||||
fields: fields,
|
|
||||||
};
|
|
||||||
Ok(Rc::new(Val::Macro(md)))
|
|
||||||
},
|
},
|
||||||
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.
|
// First resolve the target expression.
|
||||||
let v = try!(self.eval_expr(*target));
|
let v = try!(self.eval_expr(*target));
|
||||||
// Second ensure that the expression resolves to a string.
|
// 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<Val>)> = 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)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::{Builder,Val,MacroDef};
|
use super::{Builder, Val, MacroDef, SelectDef, CallDef};
|
||||||
use parse::{Statement,Expression,Value};
|
use ast::*;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
fn test_expr_to_val(mut cases: Vec<(Expression,Val)>, b: Builder) {
|
fn test_expr_to_val(mut cases: Vec<(Expression,Val)>, b: Builder) {
|
||||||
@ -627,11 +614,11 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_eval_div_expr() {
|
fn test_eval_div_expr() {
|
||||||
let mut b = Builder::new();
|
let b = Builder::new();
|
||||||
test_expr_to_val(vec![
|
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)),
|
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)),
|
Val::Float(1.0)),
|
||||||
], b);
|
], b);
|
||||||
}
|
}
|
||||||
@ -639,20 +626,20 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "Expected Float")]
|
#[should_panic(expected = "Expected Float")]
|
||||||
fn test_eval_div_expr_fail() {
|
fn test_eval_div_expr_fail() {
|
||||||
let mut b = Builder::new();
|
let b = Builder::new();
|
||||||
test_expr_to_val(vec![
|
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)),
|
Val::Float(1.0)),
|
||||||
], b);
|
], b);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_eval_mul_expr() {
|
fn test_eval_mul_expr() {
|
||||||
let mut b = Builder::new();
|
let b = Builder::new();
|
||||||
test_expr_to_val(vec![
|
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)),
|
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)),
|
Val::Float(4.0)),
|
||||||
], b);
|
], b);
|
||||||
}
|
}
|
||||||
@ -660,23 +647,20 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "Expected Float")]
|
#[should_panic(expected = "Expected Float")]
|
||||||
fn test_eval_mul_expr_fail() {
|
fn test_eval_mul_expr_fail() {
|
||||||
let mut b = Builder::new();
|
let b = Builder::new();
|
||||||
test_expr_to_val(vec![
|
test_expr_to_val(vec![
|
||||||
(Expression::Mul(Box::new(Value::Float(2.0)),
|
(Expression::Mul(BinaryExpression(Value::Float(2.0), Box::new(Expression::Simple(Value::Int(2))))),
|
||||||
Box::new(Expression::Simple(Value::Int(2)))),
|
|
||||||
Val::Float(1.0)),
|
Val::Float(1.0)),
|
||||||
], b);
|
], b);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_eval_subtract_expr() {
|
fn test_eval_subtract_expr() {
|
||||||
let mut b = Builder::new();
|
let b = Builder::new();
|
||||||
test_expr_to_val(vec![
|
test_expr_to_val(vec![
|
||||||
(Expression::Sub(Box::new(Value::Int(2)),
|
(Expression::Sub(BinaryExpression(Value::Int(2), Box::new(Expression::Simple(Value::Int(1))))),
|
||||||
Box::new(Expression::Simple(Value::Int(1)))),
|
|
||||||
Val::Int(1)),
|
Val::Int(1)),
|
||||||
(Expression::Sub(Box::new(Value::Float(2.0)),
|
(Expression::Sub(BinaryExpression(Value::Float(2.0), Box::new(Expression::Simple(Value::Float(1.0))))),
|
||||||
Box::new(Expression::Simple(Value::Float(1.0)))),
|
|
||||||
Val::Float(1.0)),
|
Val::Float(1.0)),
|
||||||
], b);
|
], b);
|
||||||
}
|
}
|
||||||
@ -684,26 +668,22 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "Expected Float")]
|
#[should_panic(expected = "Expected Float")]
|
||||||
fn test_eval_subtract_expr_fail() {
|
fn test_eval_subtract_expr_fail() {
|
||||||
let mut b = Builder::new();
|
let b = Builder::new();
|
||||||
test_expr_to_val(vec![
|
test_expr_to_val(vec![
|
||||||
(Expression::Sub(Box::new(Value::Float(2.0)),
|
(Expression::Sub(BinaryExpression(Value::Float(2.0), Box::new(Expression::Simple(Value::Int(2))))),
|
||||||
Box::new(Expression::Simple(Value::Int(2)))),
|
|
||||||
Val::Float(1.0)),
|
Val::Float(1.0)),
|
||||||
], b);
|
], b);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_eval_add_expr() {
|
fn test_eval_add_expr() {
|
||||||
let mut b = Builder::new();
|
let b = Builder::new();
|
||||||
test_expr_to_val(vec![
|
test_expr_to_val(vec![
|
||||||
(Expression::Add(Box::new(Value::Int(1)),
|
(Expression::Add(BinaryExpression(Value::Int(1), Box::new(Expression::Simple(Value::Int(1))))),
|
||||||
Box::new(Expression::Simple(Value::Int(1)))),
|
|
||||||
Val::Int(2)),
|
Val::Int(2)),
|
||||||
(Expression::Add(Box::new(Value::Float(1.0)),
|
(Expression::Add(BinaryExpression(Value::Float(1.0), Box::new(Expression::Simple(Value::Float(1.0))))),
|
||||||
Box::new(Expression::Simple(Value::Float(1.0)))),
|
|
||||||
Val::Float(2.0)),
|
Val::Float(2.0)),
|
||||||
(Expression::Add(Box::new(Value::String("foo".to_string())),
|
(Expression::Add(BinaryExpression(Value::String("foo".to_string()), Box::new(Expression::Simple(Value::String("bar".to_string()))))),
|
||||||
Box::new(Expression::Simple(Value::String("bar".to_string())))),
|
|
||||||
Val::String("foobar".to_string())),
|
Val::String("foobar".to_string())),
|
||||||
], b);
|
], b);
|
||||||
}
|
}
|
||||||
@ -711,10 +691,9 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "Expected Float")]
|
#[should_panic(expected = "Expected Float")]
|
||||||
fn test_eval_add_expr_fail() {
|
fn test_eval_add_expr_fail() {
|
||||||
let mut b = Builder::new();
|
let b = Builder::new();
|
||||||
test_expr_to_val(vec![
|
test_expr_to_val(vec![
|
||||||
(Expression::Add(Box::new(Value::Float(2.0)),
|
(Expression::Add(BinaryExpression(Value::Float(2.0), Box::new(Expression::Simple(Value::Int(2))))),
|
||||||
Box::new(Expression::Simple(Value::Int(2)))),
|
|
||||||
Val::Float(1.0)),
|
Val::Float(1.0)),
|
||||||
], b);
|
], b);
|
||||||
}
|
}
|
||||||
@ -795,7 +774,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "Unable to find Symbol tpl1")]
|
#[should_panic(expected = "Unable to find Symbol tpl1")]
|
||||||
fn test_expr_copy_no_such_tuple() {
|
fn test_expr_copy_no_such_tuple() {
|
||||||
let mut b = Builder::new();
|
let b = Builder::new();
|
||||||
test_expr_to_val(vec![
|
test_expr_to_val(vec![
|
||||||
(Expression::Copy(vec!["tpl1".to_string()], Vec::new()),
|
(Expression::Copy(vec!["tpl1".to_string()], Vec::new()),
|
||||||
Val::Tuple(Vec::new())),
|
Val::Tuple(Vec::new())),
|
||||||
@ -889,10 +868,10 @@ mod test {
|
|||||||
],
|
],
|
||||||
})));
|
})));
|
||||||
test_expr_to_val(vec![
|
test_expr_to_val(vec![
|
||||||
(Expression::Call{
|
(Expression::Call(CallDef{
|
||||||
macroref: vec!["tstmac".to_string()],
|
macroref: vec!["tstmac".to_string()],
|
||||||
arglist: vec![Expression::Simple(Value::String("bar".to_string()))],
|
arglist: vec![Expression::Simple(Value::String("bar".to_string()))],
|
||||||
},
|
}),
|
||||||
Val::Tuple(vec![
|
Val::Tuple(vec![
|
||||||
("foo".to_string(), Rc::new(Val::String("bar".to_string()))),
|
("foo".to_string(), Rc::new(Val::String("bar".to_string()))),
|
||||||
])),
|
])),
|
||||||
@ -911,10 +890,10 @@ mod test {
|
|||||||
],
|
],
|
||||||
})));
|
})));
|
||||||
test_expr_to_val(vec![
|
test_expr_to_val(vec![
|
||||||
(Expression::Call{
|
(Expression::Call(CallDef{
|
||||||
macroref: vec!["tstmac".to_string()],
|
macroref: vec!["tstmac".to_string()],
|
||||||
arglist: vec![Expression::Simple(Value::String("bar".to_string()))],
|
arglist: vec![Expression::Simple(Value::String("bar".to_string()))],
|
||||||
},
|
}),
|
||||||
Val::Tuple(vec![
|
Val::Tuple(vec![
|
||||||
("foo".to_string(), Rc::new(Val::String("bar".to_string()))),
|
("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("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())));
|
b.out.entry("baz".to_string()).or_insert(Rc::new(Val::String("boo".to_string())));
|
||||||
test_expr_to_val(vec![
|
test_expr_to_val(vec![
|
||||||
(Expression::Select{
|
(Expression::Select(SelectDef{
|
||||||
val: Box::new(Expression::Simple(Value::Symbol("foo".to_string()))),
|
val: Box::new(Expression::Simple(Value::Symbol("foo".to_string()))),
|
||||||
default: Box::new(Expression::Simple(Value::Int(1))),
|
default: Box::new(Expression::Simple(Value::Int(1))),
|
||||||
tuple: vec![
|
tuple: vec![
|
||||||
("bar".to_string(), Expression::Simple(Value::Int(2))),
|
("bar".to_string(), Expression::Simple(Value::Int(2))),
|
||||||
("quux".to_string(), Expression::Simple(Value::String("2".to_string()))),
|
("quux".to_string(), Expression::Simple(Value::String("2".to_string()))),
|
||||||
],
|
],
|
||||||
},
|
}),
|
||||||
Val::Int(2)),
|
Val::Int(2)),
|
||||||
(Expression::Select{
|
(Expression::Select(SelectDef{
|
||||||
val: Box::new(Expression::Simple(Value::Symbol("baz".to_string()))),
|
val: Box::new(Expression::Simple(Value::Symbol("baz".to_string()))),
|
||||||
default: Box::new(Expression::Simple(Value::Int(1))),
|
default: Box::new(Expression::Simple(Value::Int(1))),
|
||||||
tuple: vec![
|
tuple: vec![
|
||||||
("bar".to_string(), Expression::Simple(Value::Int(2))),
|
("bar".to_string(), Expression::Simple(Value::Int(2))),
|
||||||
("quux".to_string(), Expression::Simple(Value::String("2".to_string()))),
|
("quux".to_string(), Expression::Simple(Value::String("2".to_string()))),
|
||||||
],
|
],
|
||||||
},
|
}),
|
||||||
// If the field doesn't exist then we get the default.
|
// If the field doesn't exist then we get the default.
|
||||||
Val::Int(1)),
|
Val::Int(1)),
|
||||||
], b);
|
], b);
|
||||||
@ -955,14 +934,14 @@ mod test {
|
|||||||
let mut b = Builder::new();
|
let mut b = Builder::new();
|
||||||
b.out.entry("foo".to_string()).or_insert(Rc::new(Val::Int(4)));
|
b.out.entry("foo".to_string()).or_insert(Rc::new(Val::Int(4)));
|
||||||
test_expr_to_val(vec![
|
test_expr_to_val(vec![
|
||||||
(Expression::Select{
|
(Expression::Select(SelectDef{
|
||||||
val: Box::new(Expression::Simple(Value::Symbol("foo".to_string()))),
|
val: Box::new(Expression::Simple(Value::Symbol("foo".to_string()))),
|
||||||
default: Box::new(Expression::Simple(Value::Int(1))),
|
default: Box::new(Expression::Simple(Value::Int(1))),
|
||||||
tuple: vec![
|
tuple: vec![
|
||||||
("bar".to_string(), Expression::Simple(Value::Int(2))),
|
("bar".to_string(), Expression::Simple(Value::Int(2))),
|
||||||
("quux".to_string(), Expression::Simple(Value::String("2".to_string()))),
|
("quux".to_string(), Expression::Simple(Value::String("2".to_string()))),
|
||||||
],
|
],
|
||||||
},
|
}),
|
||||||
Val::Int(2)),
|
Val::Int(2)),
|
||||||
], b);
|
], b);
|
||||||
}
|
}
|
||||||
@ -973,7 +952,7 @@ mod test {
|
|||||||
b.build_stmt(Statement::Let{
|
b.build_stmt(Statement::Let{
|
||||||
name:"foo".to_string(),
|
name:"foo".to_string(),
|
||||||
value: Expression::Simple(Value::String("bar".to_string())),
|
value: Expression::Simple(Value::String("bar".to_string())),
|
||||||
});
|
}).unwrap();
|
||||||
test_expr_to_val(vec![
|
test_expr_to_val(vec![
|
||||||
(Expression::Simple(Value::Symbol("foo".to_string())),
|
(Expression::Simple(Value::Symbol("foo".to_string())),
|
||||||
Val::String("bar".to_string())),
|
Val::String("bar".to_string())),
|
||||||
@ -983,7 +962,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_build_file_string() {
|
fn test_build_file_string() {
|
||||||
let mut b = Builder::new();
|
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"));
|
assert!(b.out.contains_key("foo"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,14 +17,14 @@ extern crate nom;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate quick_error;
|
extern crate quick_error;
|
||||||
|
|
||||||
|
pub mod ast;
|
||||||
pub mod parse;
|
pub mod parse;
|
||||||
pub mod build;
|
pub mod build;
|
||||||
|
|
||||||
mod format;
|
mod format;
|
||||||
|
|
||||||
pub use parse::Value;
|
pub use ast::Value;
|
||||||
pub use parse::Expression;
|
pub use ast::Expression;
|
||||||
pub use parse::Statement;
|
pub use ast::Statement;
|
||||||
|
|
||||||
pub use parse::parse;
|
pub use parse::parse;
|
||||||
pub use build::Builder;
|
pub use build::Builder;
|
||||||
|
39
src/main.rs
39
src/main.rs
@ -11,8 +11,43 @@
|
|||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// 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.
|
||||||
|
#[macro_use]
|
||||||
|
extern crate clap;
|
||||||
|
|
||||||
extern crate ucglib;
|
extern crate ucglib;
|
||||||
|
|
||||||
fn main() {
|
use ucglib::build;
|
||||||
println!("hello world");
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
224
src/parse.rs
224
src/parse.rs
@ -30,114 +30,10 @@ use std::error::Error;
|
|||||||
|
|
||||||
use nom::{alpha, is_alphanumeric, digit};
|
use nom::{alpha, is_alphanumeric, digit};
|
||||||
|
|
||||||
|
use ast::*;
|
||||||
|
|
||||||
type ParseResult<O> = Result<O, Box<Error>>;
|
type ParseResult<O> = Result<O, Box<Error>>;
|
||||||
|
|
||||||
pub type FieldList = Vec<(String, Expression)>; // str is expected to be a symbol
|
|
||||||
pub type SelectorList = Vec<String>; // 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<Value>, Box<Expression>),
|
|
||||||
Sub(Box<Value>, Box<Expression>),
|
|
||||||
Mul(Box<Value>, Box<Expression>),
|
|
||||||
Div(Box<Value>, Box<Expression>),
|
|
||||||
|
|
||||||
// Complex Expressions
|
|
||||||
Copy(SelectorList, FieldList),
|
|
||||||
Grouped(Box<Expression>),
|
|
||||||
|
|
||||||
Format(String, Vec<Expression>),
|
|
||||||
|
|
||||||
Call {
|
|
||||||
macroref: SelectorList,
|
|
||||||
arglist: Vec<Expression>,
|
|
||||||
},
|
|
||||||
|
|
||||||
Macro {
|
|
||||||
arglist: Vec<String>,
|
|
||||||
tuple: FieldList,
|
|
||||||
},
|
|
||||||
|
|
||||||
Select {
|
|
||||||
val: Box<Expression>,
|
|
||||||
default: Box<Expression>,
|
|
||||||
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
|
// sentinels and punctuation
|
||||||
named!(doublequote, tag!("\""));
|
named!(doublequote, tag!("\""));
|
||||||
named!(comma, tag!(","));
|
named!(comma, tag!(","));
|
||||||
@ -305,7 +201,7 @@ named!(simple_expression<Expression>,
|
|||||||
);
|
);
|
||||||
|
|
||||||
fn tuple_to_add_expression(tpl: (Value, Expression)) -> ParseResult<Expression> {
|
fn tuple_to_add_expression(tpl: (Value, Expression)) -> ParseResult<Expression> {
|
||||||
Ok(Expression::Add(Box::new(tpl.0), Box::new(tpl.1)))
|
Ok(Expression::Add(BinaryExpression(tpl.0, Box::new(tpl.1))))
|
||||||
}
|
}
|
||||||
|
|
||||||
named!(add_expression<Expression>,
|
named!(add_expression<Expression>,
|
||||||
@ -321,7 +217,7 @@ named!(add_expression<Expression>,
|
|||||||
);
|
);
|
||||||
|
|
||||||
fn tuple_to_sub_expression(tpl: (Value, Expression)) -> ParseResult<Expression> {
|
fn tuple_to_sub_expression(tpl: (Value, Expression)) -> ParseResult<Expression> {
|
||||||
Ok(Expression::Sub(Box::new(tpl.0), Box::new(tpl.1)))
|
Ok(Expression::Sub(BinaryExpression(tpl.0, Box::new(tpl.1))))
|
||||||
}
|
}
|
||||||
|
|
||||||
named!(sub_expression<Expression>,
|
named!(sub_expression<Expression>,
|
||||||
@ -337,7 +233,7 @@ named!(sub_expression<Expression>,
|
|||||||
);
|
);
|
||||||
|
|
||||||
fn tuple_to_mul_expression(tpl: (Value, Expression)) -> ParseResult<Expression> {
|
fn tuple_to_mul_expression(tpl: (Value, Expression)) -> ParseResult<Expression> {
|
||||||
Ok(Expression::Mul(Box::new(tpl.0), Box::new(tpl.1)))
|
Ok(Expression::Mul(BinaryExpression(tpl.0, Box::new(tpl.1))))
|
||||||
}
|
}
|
||||||
|
|
||||||
named!(mul_expression<Expression>,
|
named!(mul_expression<Expression>,
|
||||||
@ -353,7 +249,7 @@ named!(mul_expression<Expression>,
|
|||||||
);
|
);
|
||||||
|
|
||||||
fn tuple_to_div_expression(tpl: (Value, Expression)) -> ParseResult<Expression> {
|
fn tuple_to_div_expression(tpl: (Value, Expression)) -> ParseResult<Expression> {
|
||||||
Ok(Expression::Div(Box::new(tpl.0), Box::new(tpl.1)))
|
Ok(Expression::Div(BinaryExpression(tpl.0, Box::new(tpl.1))))
|
||||||
}
|
}
|
||||||
|
|
||||||
named!(div_expression<Expression>,
|
named!(div_expression<Expression>,
|
||||||
@ -401,10 +297,10 @@ named!(copy_expression<Expression>,
|
|||||||
fn tuple_to_macro(mut t: (Vec<Value>, Value)) -> ParseResult<Expression> {
|
fn tuple_to_macro(mut t: (Vec<Value>, Value)) -> ParseResult<Expression> {
|
||||||
match t.1 {
|
match t.1 {
|
||||||
Value::Tuple(v) => {
|
Value::Tuple(v) => {
|
||||||
Ok(Expression::Macro {
|
Ok(Expression::Macro(MacroDef {
|
||||||
arglist: t.0.drain(0..).map(|s| s.to_string()).collect(),
|
argdefs: t.0.drain(0..).map(|s| s.to_string()).collect(),
|
||||||
tuple: v,
|
fields: v,
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
// TODO(jwall): Show a better version of the unexpected parsed value.
|
// TODO(jwall): Show a better version of the unexpected parsed value.
|
||||||
val => {
|
val => {
|
||||||
@ -434,11 +330,11 @@ fn tuple_to_select(t: (Expression, Expression, Value))
|
|||||||
-> ParseResult<Expression> {
|
-> ParseResult<Expression> {
|
||||||
match t.2 {
|
match t.2 {
|
||||||
Value::Tuple(v) => {
|
Value::Tuple(v) => {
|
||||||
Ok(Expression::Select {
|
Ok(Expression::Select(SelectDef{
|
||||||
val: Box::new(t.0),
|
val: Box::new(t.0),
|
||||||
default: Box::new(t.1),
|
default: Box::new(t.1),
|
||||||
tuple: v,
|
tuple: v,
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
// TODO(jwall): Show a better version of the unexpected parsed value.
|
// TODO(jwall): Show a better version of the unexpected parsed value.
|
||||||
val => {
|
val => {
|
||||||
@ -480,10 +376,10 @@ named!(format_expression<Expression>,
|
|||||||
|
|
||||||
fn tuple_to_call(t: (Value, Vec<Expression>)) -> ParseResult<Expression> {
|
fn tuple_to_call(t: (Value, Vec<Expression>)) -> ParseResult<Expression> {
|
||||||
if let Value::Selector(sl) = t.0 {
|
if let Value::Selector(sl) = t.0 {
|
||||||
Ok(Expression::Call {
|
Ok(Expression::Call(CallDef{
|
||||||
macroref: sl,
|
macroref: sl,
|
||||||
arglist: t.1,
|
arglist: t.1,
|
||||||
})
|
}))
|
||||||
} else {
|
} else {
|
||||||
Err(Box::new(ParseError::UnexpectedToken("Selector".to_string(), format!("{:?}", t.0))))
|
Err(Box::new(ParseError::UnexpectedToken("Selector".to_string(), format!("{:?}", t.0))))
|
||||||
}
|
}
|
||||||
@ -605,12 +501,13 @@ named!(pub parse<Vec<Statement> >, many1!(ws!(statement)));
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use std::str::from_utf8;
|
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::{number, symbol, parse, field_value, tuple, grouped_expression};
|
||||||
use super::{arglist, copy_expression, macro_expression, select_expression};
|
use super::{arglist, copy_expression, macro_expression, select_expression};
|
||||||
use super::{format_expression, call_expression, expression};
|
use super::{format_expression, call_expression, expression};
|
||||||
use super::{expression_statement, let_statement, import_statement, statement};
|
use super::{expression_statement, let_statement, import_statement, statement};
|
||||||
use nom::IResult;
|
use nom::IResult;
|
||||||
|
use ast::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_statement_parse() {
|
fn test_statement_parse() {
|
||||||
@ -725,70 +622,70 @@ mod test {
|
|||||||
IResult::Done(&b""[..], Expression::Simple(Value::Symbol("foo".to_string()))));
|
IResult::Done(&b""[..], Expression::Simple(Value::Symbol("foo".to_string()))));
|
||||||
assert_eq!(expression(&b"1 + 1"[..]),
|
assert_eq!(expression(&b"1 + 1"[..]),
|
||||||
IResult::Done(&b""[..],
|
IResult::Done(&b""[..],
|
||||||
Expression::Add(Box::new(Value::Int(1)),
|
Expression::Add(BinaryExpression(Value::Int(1),
|
||||||
Box::new(Expression::Simple(Value::Int(1))))));
|
Box::new(Expression::Simple(Value::Int(1)))))));
|
||||||
assert_eq!(expression(&b"1 - 1"[..]),
|
assert_eq!(expression(&b"1 - 1"[..]),
|
||||||
IResult::Done(&b""[..],
|
IResult::Done(&b""[..],
|
||||||
Expression::Sub(Box::new(Value::Int(1)),
|
Expression::Sub(BinaryExpression(Value::Int(1),
|
||||||
Box::new(Expression::Simple(Value::Int(1))))));
|
Box::new(Expression::Simple(Value::Int(1)))))));
|
||||||
assert_eq!(expression(&b"1 * 1"[..]),
|
assert_eq!(expression(&b"1 * 1"[..]),
|
||||||
IResult::Done(&b""[..],
|
IResult::Done(&b""[..],
|
||||||
Expression::Mul(Box::new(Value::Int(1)),
|
Expression::Mul(BinaryExpression(Value::Int(1),
|
||||||
Box::new(Expression::Simple(Value::Int(1))))));
|
Box::new(Expression::Simple(Value::Int(1)))))));
|
||||||
assert_eq!(expression(&b"1 / 1"[..]),
|
assert_eq!(expression(&b"1 / 1"[..]),
|
||||||
IResult::Done(&b""[..],
|
IResult::Done(&b""[..],
|
||||||
Expression::Div(Box::new(Value::Int(1)),
|
Expression::Div(BinaryExpression(Value::Int(1),
|
||||||
Box::new(Expression::Simple(Value::Int(1))))));
|
Box::new(Expression::Simple(Value::Int(1)))))));
|
||||||
|
|
||||||
assert_eq!(expression(&b"1+1"[..]),
|
assert_eq!(expression(&b"1+1"[..]),
|
||||||
IResult::Done(&b""[..],
|
IResult::Done(&b""[..],
|
||||||
Expression::Add(Box::new(Value::Int(1)),
|
Expression::Add(BinaryExpression(Value::Int(1),
|
||||||
Box::new(Expression::Simple(Value::Int(1))))));
|
Box::new(Expression::Simple(Value::Int(1)))))));
|
||||||
assert_eq!(expression(&b"1-1"[..]),
|
assert_eq!(expression(&b"1-1"[..]),
|
||||||
IResult::Done(&b""[..],
|
IResult::Done(&b""[..],
|
||||||
Expression::Sub(Box::new(Value::Int(1)),
|
Expression::Sub(BinaryExpression(Value::Int(1),
|
||||||
Box::new(Expression::Simple(Value::Int(1))))));
|
Box::new(Expression::Simple(Value::Int(1)))))));
|
||||||
assert_eq!(expression(&b"1*1"[..]),
|
assert_eq!(expression(&b"1*1"[..]),
|
||||||
IResult::Done(&b""[..],
|
IResult::Done(&b""[..],
|
||||||
Expression::Mul(Box::new(Value::Int(1)),
|
Expression::Mul(BinaryExpression(Value::Int(1),
|
||||||
Box::new(Expression::Simple(Value::Int(1))))));
|
Box::new(Expression::Simple(Value::Int(1)))))));
|
||||||
assert_eq!(expression(&b"1/1"[..]),
|
assert_eq!(expression(&b"1/1"[..]),
|
||||||
IResult::Done(&b""[..],
|
IResult::Done(&b""[..],
|
||||||
Expression::Div(Box::new(Value::Int(1)),
|
Expression::Div(BinaryExpression(Value::Int(1),
|
||||||
Box::new(Expression::Simple(Value::Int(1))))));
|
Box::new(Expression::Simple(Value::Int(1)))))));
|
||||||
assert_eq!(expression(&b"macro (arg1, arg2) => { foo = arg1 }"[..]),
|
assert_eq!(expression(&b"macro (arg1, arg2) => { foo = arg1 }"[..]),
|
||||||
IResult::Done(&b""[..],
|
IResult::Done(&b""[..],
|
||||||
Expression::Macro{
|
Expression::Macro(MacroDef{
|
||||||
arglist: vec![
|
argdefs: vec![
|
||||||
"arg1".to_string(),
|
"arg1".to_string(),
|
||||||
"arg2".to_string(),
|
"arg2".to_string(),
|
||||||
],
|
],
|
||||||
tuple: vec![
|
fields: vec![
|
||||||
("foo".to_string(), Expression::Simple(Value::Symbol("arg1".to_string()))),
|
("foo".to_string(), Expression::Simple(Value::Symbol("arg1".to_string()))),
|
||||||
],
|
],
|
||||||
}
|
})
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(expression(&b"select foo, 1, { foo = 2 };"[..]),
|
assert_eq!(expression(&b"select foo, 1, { foo = 2 };"[..]),
|
||||||
IResult::Done(&b""[..],
|
IResult::Done(&b""[..],
|
||||||
Expression::Select{
|
Expression::Select(SelectDef{
|
||||||
val: Box::new(Expression::Simple(Value::Symbol("foo".to_string()))),
|
val: Box::new(Expression::Simple(Value::Symbol("foo".to_string()))),
|
||||||
default: Box::new(Expression::Simple(Value::Int(1))),
|
default: Box::new(Expression::Simple(Value::Int(1))),
|
||||||
tuple: vec![
|
tuple: vec![
|
||||||
("foo".to_string(), Expression::Simple(Value::Int(2)))
|
("foo".to_string(), Expression::Simple(Value::Int(2)))
|
||||||
]
|
]
|
||||||
}
|
})
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(expression(&b"foo.bar (1, \"foo\")"[..]),
|
assert_eq!(expression(&b"foo.bar (1, \"foo\")"[..]),
|
||||||
IResult::Done(&b""[..],
|
IResult::Done(&b""[..],
|
||||||
Expression::Call{
|
Expression::Call(CallDef{
|
||||||
macroref: vec!["foo".to_string(),"bar".to_string()],
|
macroref: vec!["foo".to_string(),"bar".to_string()],
|
||||||
arglist: vec![
|
arglist: vec![
|
||||||
Expression::Simple(Value::Int(1)),
|
Expression::Simple(Value::Int(1)),
|
||||||
Expression::Simple(Value::String("foo".to_string())),
|
Expression::Simple(Value::String("foo".to_string())),
|
||||||
],
|
],
|
||||||
}
|
})
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(expression(&b"(1 + 1)"[..]),
|
assert_eq!(expression(&b"(1 + 1)"[..]),
|
||||||
@ -796,8 +693,8 @@ mod test {
|
|||||||
Expression::Grouped(
|
Expression::Grouped(
|
||||||
Box::new(
|
Box::new(
|
||||||
Expression::Add(
|
Expression::Add(
|
||||||
Box::new(Value::Int(1)),
|
BinaryExpression(Value::Int(1),
|
||||||
Box::new(Expression::Simple(Value::Int(1)))
|
Box::new(Expression::Simple(Value::Int(1))))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -819,6 +716,13 @@ mod test {
|
|||||||
Expression::Simple(Value::Int(2))])
|
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]
|
#[test]
|
||||||
@ -831,25 +735,25 @@ mod test {
|
|||||||
|
|
||||||
assert_eq!(call_expression(&b"foo (1, \"foo\")"[..]),
|
assert_eq!(call_expression(&b"foo (1, \"foo\")"[..]),
|
||||||
IResult::Done(&b""[..],
|
IResult::Done(&b""[..],
|
||||||
Expression::Call{
|
Expression::Call(CallDef{
|
||||||
macroref: vec!["foo".to_string()],
|
macroref: vec!["foo".to_string()],
|
||||||
arglist: vec![
|
arglist: vec![
|
||||||
Expression::Simple(Value::Int(1)),
|
Expression::Simple(Value::Int(1)),
|
||||||
Expression::Simple(Value::String("foo".to_string())),
|
Expression::Simple(Value::String("foo".to_string())),
|
||||||
],
|
],
|
||||||
}
|
})
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(call_expression(&b"foo.bar (1, \"foo\")"[..]),
|
assert_eq!(call_expression(&b"foo.bar (1, \"foo\")"[..]),
|
||||||
IResult::Done(&b""[..],
|
IResult::Done(&b""[..],
|
||||||
Expression::Call{
|
Expression::Call(CallDef{
|
||||||
macroref: vec!["foo".to_string(),"bar".to_string()],
|
macroref: vec!["foo".to_string(),"bar".to_string()],
|
||||||
arglist: vec![
|
arglist: vec![
|
||||||
Expression::Simple(Value::Int(1)),
|
Expression::Simple(Value::Int(1)),
|
||||||
Expression::Simple(Value::String("foo".to_string())),
|
Expression::Simple(Value::String("foo".to_string())),
|
||||||
],
|
],
|
||||||
}
|
})
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -863,13 +767,13 @@ mod test {
|
|||||||
|
|
||||||
assert_eq!(select_expression(&b"select foo, 1, { foo = 2 };"[..]),
|
assert_eq!(select_expression(&b"select foo, 1, { foo = 2 };"[..]),
|
||||||
IResult::Done(&b""[..],
|
IResult::Done(&b""[..],
|
||||||
Expression::Select{
|
Expression::Select(SelectDef{
|
||||||
val: Box::new(Expression::Simple(Value::Symbol("foo".to_string()))),
|
val: Box::new(Expression::Simple(Value::Symbol("foo".to_string()))),
|
||||||
default: Box::new(Expression::Simple(Value::Int(1))),
|
default: Box::new(Expression::Simple(Value::Int(1))),
|
||||||
tuple: vec![
|
tuple: vec![
|
||||||
("foo".to_string(), Expression::Simple(Value::Int(2)))
|
("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}"[..]),
|
assert_eq!(macro_expression(&b"macro (arg1, arg2) => {foo=1,bar=2}"[..]),
|
||||||
IResult::Done(&b""[..],
|
IResult::Done(&b""[..],
|
||||||
Expression::Macro{
|
Expression::Macro(MacroDef{
|
||||||
arglist: vec!["arg1".to_string(),
|
argdefs: vec!["arg1".to_string(),
|
||||||
"arg2".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)))
|
("bar".to_string(), Expression::Simple(Value::Int(2)))
|
||||||
]
|
]
|
||||||
}
|
})
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -947,9 +851,9 @@ mod test {
|
|||||||
Expression::Grouped(
|
Expression::Grouped(
|
||||||
Box::new(
|
Box::new(
|
||||||
Expression::Add(
|
Expression::Add(
|
||||||
Box::new(Value::Int(1)),
|
BinaryExpression(Value::Int(1),
|
||||||
Box::new(Expression::Simple(
|
Box::new(Expression::Simple(
|
||||||
Value::Int(1)))
|
Value::Int(1))))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -1058,8 +962,8 @@ mod test {
|
|||||||
value: Expression::Simple(Value::Int(1))
|
value: Expression::Simple(Value::Int(1))
|
||||||
},
|
},
|
||||||
Statement::Expression(
|
Statement::Expression(
|
||||||
Expression::Add(Box::new(Value::Int(1)),
|
Expression::Add(BinaryExpression(Value::Int(1),
|
||||||
Box::new(Expression::Simple(Value::Int(1))))
|
Box::new(Expression::Simple(Value::Int(1)))))
|
||||||
)
|
)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user