mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-22 18:19:54 -04:00
cleanup: fix some selector vs symbol parsing issues
Add test coverage of selector parsing.
This commit is contained in:
parent
3ab5d27fc7
commit
e975dea201
@ -2,13 +2,13 @@ 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,
|
||||
conn_string = "@:@/@" % (host, port, db)
|
||||
};
|
||||
|
||||
let db_conn = mk_db_conn(host, 3306, dbame);
|
||||
let db_conn = mk_db_conn(dbhost, 3306, dbname);
|
||||
|
||||
let server_config = {
|
||||
dbconn = db_conn.conn_string,
|
||||
|
13
src/ast.rs
13
src/ast.rs
@ -241,24 +241,29 @@ impl MacroDef {
|
||||
-> HashSet<String> {
|
||||
let mut bad_symbols = HashSet::new();
|
||||
if let &Value::Symbol(ref name) = val {
|
||||
let mut ok = true;
|
||||
let mut ok = false;
|
||||
for arg in self.argdefs.iter() {
|
||||
ok &= arg.val == name.val
|
||||
if arg.val == name.val {
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
bad_symbols.insert(name.val.clone());
|
||||
}
|
||||
} else if let &Value::Selector(ref sel_node) = val {
|
||||
let list = &sel_node.val;
|
||||
let mut ok = true;
|
||||
let mut ok = false;
|
||||
if list.len() > 0 {
|
||||
// We only look to see if the first selector item exists.
|
||||
// This is because only the first one is a symbol all of the
|
||||
// rest of the items in the selector are fields in a tuple.
|
||||
// But we don't know at this time of the value passed into
|
||||
// this macro is a tuple since this isn't a callsite.
|
||||
println!("checking selector head {}", list[0].fragment);
|
||||
for arg in self.argdefs.iter() {
|
||||
ok &= arg.val == list[0].fragment
|
||||
if arg.val == list[0].fragment {
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
bad_symbols.insert(list[0].fragment.to_string());
|
||||
|
28
src/build.rs
28
src/build.rs
@ -231,7 +231,7 @@ pub struct Builder {
|
||||
/// out is our built output.
|
||||
out: ValueMap,
|
||||
/// last is the result of the last statement.
|
||||
last: Option<Rc<Val>>,
|
||||
pub last: Option<Rc<Val>>,
|
||||
}
|
||||
|
||||
macro_rules! eval_binary_expr {
|
||||
@ -294,6 +294,17 @@ impl Builder {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_out_by_name(&self, name: &str) -> Option<Rc<Val>> {
|
||||
let key = Positioned {
|
||||
pos: Position {
|
||||
line: 0,
|
||||
column: 0,
|
||||
},
|
||||
val: name.to_string(),
|
||||
};
|
||||
self.lookup_sym(&key)
|
||||
}
|
||||
|
||||
pub fn build(&mut self, ast: &Vec<Statement>) -> BuildResult {
|
||||
for stmt in ast.iter() {
|
||||
try!(self.build_stmt(stmt));
|
||||
@ -303,7 +314,7 @@ impl Builder {
|
||||
|
||||
pub fn build_file_string(&mut self, name: &str, input: String) -> BuildResult {
|
||||
match parse(Span::new(&input)) {
|
||||
nom::IResult::Done(_, stmts) => {
|
||||
nom::IResult::Done(_span, stmts) => {
|
||||
for stmt in stmts.iter() {
|
||||
try!(self.build_stmt(stmt));
|
||||
}
|
||||
@ -324,6 +335,7 @@ impl Builder {
|
||||
// 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)
|
||||
// TODO(jwall): Call an output converter.
|
||||
}
|
||||
|
||||
fn build_stmt(&mut self, stmt: &Statement) -> BuildResult {
|
||||
@ -923,8 +935,16 @@ mod test {
|
||||
}))
|
||||
.or_insert(Rc::new(Val::Int(2)));
|
||||
b.out
|
||||
.entry(Positioned::new("var3".to_string(), Position{line: 1, column: 0}))
|
||||
.or_insert(Rc::new(Val::Tuple(vec![(Positioned::new("lvl1".to_string(), Position{line: 1, column: 0}),
|
||||
.entry(Positioned::new("var3".to_string(),
|
||||
Position {
|
||||
line: 1,
|
||||
column: 0,
|
||||
}))
|
||||
.or_insert(Rc::new(Val::Tuple(vec![(Positioned::new("lvl1".to_string(),
|
||||
Position {
|
||||
line: 1,
|
||||
column: 0,
|
||||
}),
|
||||
Rc::new(Val::Int(4)))])));
|
||||
test_expr_to_val(vec![
|
||||
(Expression::Simple(Value::Selector(make_value_node(vec![Token::new("var1", Position{line: 1, column: 1})], 1, 1))), Val::Tuple(
|
||||
|
@ -24,6 +24,7 @@ pub mod ast;
|
||||
pub mod tokenizer;
|
||||
pub mod parse;
|
||||
pub mod build;
|
||||
pub mod convert;
|
||||
mod format;
|
||||
|
||||
pub use ast::Value;
|
||||
|
54
src/main.rs
54
src/main.rs
@ -13,11 +13,19 @@
|
||||
// limitations under the License.
|
||||
#[macro_use]
|
||||
extern crate clap;
|
||||
|
||||
extern crate ucglib;
|
||||
|
||||
use ucglib::build;
|
||||
use std::fs::File;
|
||||
use std::rc::Rc;
|
||||
use std::io;
|
||||
use std::process;
|
||||
|
||||
use ucglib::build::Val;
|
||||
use ucglib::build;
|
||||
//use ucglib::convert::get_converter;
|
||||
use ucglib::convert::ConverterRunner;
|
||||
|
||||
// TODO(jwall): List the target output types automatically.
|
||||
fn do_flags<'a>() -> clap::ArgMatches<'a> {
|
||||
clap_app!(
|
||||
ucg =>
|
||||
@ -26,6 +34,9 @@ fn do_flags<'a>() -> clap::ArgMatches<'a> {
|
||||
(about: "Universal Configuration Grammar compiler.")
|
||||
(@subcommand build =>
|
||||
(about: "Compile a specific ucg file.")
|
||||
(@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 out: --out -o +takes_value "Output file to write to.")
|
||||
(@arg INPUT: +required "Input ucg file to build.")
|
||||
)
|
||||
(@subcommand validate =>
|
||||
@ -36,18 +47,53 @@ fn do_flags<'a>() -> clap::ArgMatches<'a> {
|
||||
.get_matches()
|
||||
}
|
||||
|
||||
fn run_converter(c: ConverterRunner, v: Rc<Val>, f: &str) -> io::Result<()> {
|
||||
let file = File::create(f);
|
||||
c.convert(v, Box::new(file.unwrap()))
|
||||
}
|
||||
|
||||
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 out = matches.value_of("out").unwrap();
|
||||
let sym = matches.value_of("sym");
|
||||
let target = matches.value_of("target").unwrap();
|
||||
let mut builder = build::Builder::new();
|
||||
builder.build_file(file).unwrap();
|
||||
println!("Build successful");
|
||||
match ConverterRunner::new(target) {
|
||||
Ok(converter) => {
|
||||
let result = builder.build_file(file);
|
||||
if !result.is_ok() {
|
||||
eprintln!("{:?}", result.err());
|
||||
process::exit(1);
|
||||
}
|
||||
let val = match sym {
|
||||
Some(sym_name) => builder.get_out_by_name(sym_name),
|
||||
None => builder.last,
|
||||
};
|
||||
match val {
|
||||
Some(value) => {
|
||||
run_converter(converter, value, out).unwrap();
|
||||
println!("Build successful");
|
||||
process::exit(0);
|
||||
}
|
||||
None => {
|
||||
eprintln!("Build results in no value.");
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(msg) => {
|
||||
eprintln!("{}", msg);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
} 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");
|
||||
process::exit(0);
|
||||
}
|
||||
}
|
||||
|
143
src/parse.rs
143
src/parse.rs
@ -15,6 +15,9 @@ use std::str::FromStr;
|
||||
use std::error::Error;
|
||||
use std::borrow::Borrow;
|
||||
|
||||
use nom::IResult;
|
||||
use nom::InputLength;
|
||||
|
||||
use ast::*;
|
||||
use tokenizer::*;
|
||||
|
||||
@ -163,7 +166,27 @@ named!(
|
||||
)
|
||||
);
|
||||
|
||||
named!(value( Span ) -> Value, alt!(number | quoted_value | symbol | tuple));
|
||||
pub fn selector_or_symbol(input: Span) -> IResult<Span, Value> {
|
||||
let sym = do_parse!(input,
|
||||
sym: symbol >>
|
||||
not!(dottok) >>
|
||||
(sym)
|
||||
);
|
||||
match sym {
|
||||
IResult::Incomplete(i) => {
|
||||
return IResult::Incomplete(i);
|
||||
},
|
||||
IResult::Error(_) => {
|
||||
// TODO(jwall): Maybe be smarter about the error reporting here?
|
||||
return ws!(input, selector_value);
|
||||
},
|
||||
IResult::Done(rest, val) => {
|
||||
return IResult::Done(rest, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
named!(value( Span ) -> Value, alt!(number | quoted_value | tuple | selector_or_symbol ));
|
||||
|
||||
fn value_to_expression(v: Value) -> ParseResult<Expression> {
|
||||
Ok(Expression::Simple(v))
|
||||
@ -504,19 +527,90 @@ named!(statement( Span ) -> Statement,
|
||||
)
|
||||
);
|
||||
|
||||
named!(pub parse( Span ) -> Vec<Statement>, many1!(ws!(statement)));
|
||||
pub fn parse(input: Span) -> IResult<Span, Vec<Statement>> {
|
||||
let mut out = Vec::new();
|
||||
let mut i = input;
|
||||
loop {
|
||||
match ws!(i, statement) {
|
||||
IResult::Error(e) => {
|
||||
return IResult::Error(e);
|
||||
},
|
||||
IResult::Incomplete(i) => {
|
||||
return IResult::Incomplete(i);
|
||||
},
|
||||
IResult::Done(rest, stmt) => {
|
||||
out.push(stmt);
|
||||
i = rest;
|
||||
if i.input_len() == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return IResult::Done(i, out);
|
||||
}
|
||||
|
||||
//named!(pub parse( Span ) -> Vec<Statement>, many1!());
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
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, selector_value, selector_or_symbol, tuple, grouped_expression};
|
||||
use super::{copy_expression, macro_expression, select_expression};
|
||||
use super::{format_expression, call_expression, expression};
|
||||
use super::{expression_statement, let_statement, import_statement, statement};
|
||||
use ast::*;
|
||||
use nom_locate::LocatedSpan;
|
||||
|
||||
use nom::IResult;
|
||||
use nom::{Needed, IResult};
|
||||
|
||||
#[test]
|
||||
fn test_symbol_parsing() {
|
||||
assert_eq!(symbol(LocatedSpan::new("foo")),
|
||||
IResult::Done(LocatedSpan{fragment: "", offset: 3, line: 1},
|
||||
Value::Symbol(value_node!("foo".to_string(), Position{line: 1, column: 1}))) );
|
||||
assert_eq!(symbol(LocatedSpan::new("foo-bar")),
|
||||
IResult::Done(LocatedSpan{fragment: "", offset: 7, line: 1},
|
||||
Value::Symbol(value_node!("foo-bar".to_string(), Position{line: 1, column: 1}))) );
|
||||
assert_eq!(symbol(LocatedSpan::new("foo_bar")),
|
||||
IResult::Done(LocatedSpan{fragment: "", offset: 7, line: 1},
|
||||
Value::Symbol(value_node!("foo_bar".to_string(), Position{line: 1, column: 1}))) );
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_selector_parsing() {
|
||||
assert_eq!(selector_value(LocatedSpan::new("foo.")),
|
||||
IResult::Incomplete(Needed::Unknown)
|
||||
);
|
||||
assert_eq!(selector_value(LocatedSpan::new("foo.bar ")),
|
||||
IResult::Done(LocatedSpan{fragment: "", offset: 8, line: 1},
|
||||
Value::Selector(value_node!(vec![Token{fragment:"foo".to_string(), pos: Position{line: 1, column: 1}},
|
||||
Token{fragment:"bar".to_string(), pos: Position{line: 1, column: 5}}],
|
||||
Position{line: 1, column: 0})))
|
||||
);
|
||||
assert_eq!(selector_value(LocatedSpan::new("foo.bar;")),
|
||||
IResult::Done(LocatedSpan{fragment: ";", offset: 7, line: 1},
|
||||
Value::Selector(value_node!(vec![Token{fragment:"foo".to_string(), pos: Position{line: 1, column: 1}},
|
||||
Token{fragment:"bar".to_string(), pos: Position{line: 1, column: 5}}],
|
||||
Position{line: 1, column: 0})))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_selector_or_symbol_parsing() {
|
||||
assert_eq!(selector_or_symbol(LocatedSpan::new("foo.")),
|
||||
IResult::Incomplete(Needed::Unknown)
|
||||
);
|
||||
assert_eq!(selector_or_symbol(LocatedSpan::new("foo")),
|
||||
IResult::Done(LocatedSpan{fragment: "", offset: 3, line: 1},
|
||||
Value::Symbol(value_node!("foo".to_string(), Position{line: 1, column: 1}))) );
|
||||
assert_eq!(selector_or_symbol(LocatedSpan::new("foo.bar ")),
|
||||
IResult::Done(LocatedSpan{fragment: "", offset: 8, line: 1},
|
||||
Value::Selector(value_node!(vec![Token{fragment:"foo".to_string(), pos: Position{line: 1, column: 1}},
|
||||
Token{fragment:"bar".to_string(), pos: Position{line: 1, column: 5}}],
|
||||
Position{line: 1, column: 0})))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_statement_parse() {
|
||||
@ -744,19 +838,27 @@ mod test {
|
||||
#[test]
|
||||
fn test_expression_parse() {
|
||||
assert_eq!(expression(LocatedSpan::new("1")),
|
||||
IResult::Done(LocatedSpan {
|
||||
fragment: "",
|
||||
offset: 1,
|
||||
line: 1,
|
||||
},
|
||||
Expression::Simple(Value::Int(value_node!(1, Position{line: 1, column: 1})))));
|
||||
IResult::Done(LocatedSpan {
|
||||
fragment: "",
|
||||
offset: 1,
|
||||
line: 1,
|
||||
},
|
||||
Expression::Simple(Value::Int(value_node!(1, Position{line: 1, column: 1})))));
|
||||
assert_eq!(expression(LocatedSpan::new("foo")),
|
||||
IResult::Done(LocatedSpan {
|
||||
fragment: "",
|
||||
offset: 3,
|
||||
line: 1,
|
||||
},
|
||||
Expression::Simple(Value::Symbol(value_node!("foo".to_string(), Position{line: 1, column: 1})))));
|
||||
assert_eq!(expression(LocatedSpan::new("foo.bar ")),
|
||||
IResult::Done(LocatedSpan {
|
||||
fragment: "",
|
||||
offset: 3,
|
||||
offset: 8,
|
||||
line: 1,
|
||||
},
|
||||
Expression::Simple(Value::Symbol(value_node!("foo".to_string(), Position{line: 1, column: 1})))));
|
||||
Expression::Simple(Value::Selector(make_value_node(vec![Token::new("foo", Position{line: 1, column: 1}),
|
||||
Token::new("bar", Position{line: 1, column: 5})], 1, 0)))));
|
||||
assert_eq!(expression(LocatedSpan::new("1 + 1")),
|
||||
IResult::Done(LocatedSpan {
|
||||
fragment: "",
|
||||
@ -1237,6 +1339,10 @@ mod test {
|
||||
IResult::Done(LocatedSpan { offset: 10, line: 1, fragment: "" },
|
||||
(Token::new("foo", Position{line: 1, column: 1}),
|
||||
Expression::Simple(Value::Symbol(value_node!("bar".to_string(), Position{line: 1, column: 7}))))) );
|
||||
assert_eq!(field_value(LocatedSpan::new("foo = bar.baz ")),
|
||||
IResult::Done(LocatedSpan { offset: 14, line: 1, fragment: "" },
|
||||
(Token::new("foo", Position{line: 1, column: 1}),
|
||||
Expression::Simple(Value::Selector(make_value_node(vec![Token::new("bar", Position{line: 1, column: 7}), Token::new("baz", Position{line: 1, column: 11})], 1, 6))))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1257,19 +1363,6 @@ mod test {
|
||||
Value::Float(value_node!(0.1, Position{line: 1, column: 1}))) );
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_symbol_parsing() {
|
||||
assert_eq!(symbol(LocatedSpan::new("foo")),
|
||||
IResult::Done(LocatedSpan{fragment: "", offset: 3, line: 1},
|
||||
Value::Symbol(value_node!("foo".to_string(), Position{line: 1, column: 1}))) );
|
||||
assert_eq!(symbol(LocatedSpan::new("foo-bar")),
|
||||
IResult::Done(LocatedSpan{fragment: "", offset: 7, line: 1},
|
||||
Value::Symbol(value_node!("foo-bar".to_string(), Position{line: 1, column: 1}))) );
|
||||
assert_eq!(symbol(LocatedSpan::new("foo_bar")),
|
||||
IResult::Done(LocatedSpan{fragment: "", offset: 7, line: 1},
|
||||
Value::Symbol(value_node!("foo_bar".to_string(), Position{line: 1, column: 1}))) );
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse() {
|
||||
let bad_input = LocatedSpan::new("import mylib as lib;");
|
||||
|
Loading…
x
Reference in New Issue
Block a user