diff --git a/doc/grammar.bnf b/doc/grammar.bnf index e51e5fb..c787924 100644 --- a/doc/grammar.bnf +++ b/doc/grammar.bnf @@ -4,9 +4,15 @@ INSTR ::= | ASSIGN semicolon | ASSIGN_IF semicolon | ASSIGN_ADD semicolon +| CMD_CALL semicolon +| CMD_DEF + ASSIGN ::= ident assign EXPR ASSIGN_IF ::= ident assign_if EXPR ASSIGN_ADD ::= ident assign_add EXPR +CMD_CALL ::= ident EXPR* +CMD_DEF ::= command ident colon EXPR* rarrow EXPR* BLOCK +BLOCK ::= obrace INSTR* cbrace EXPR ::= LITERAL LITERAL ::= | BUILTIN diff --git a/src/ast/lexer.rs b/src/ast/lexer.rs index 947f668..05b9be0 100644 --- a/src/ast/lexer.rs +++ b/src/ast/lexer.rs @@ -21,7 +21,9 @@ impl Lexer { text: String::from(""), separators: vec![ ';', '[', ']', - '=', '?', '+' + '=', '?', '+', ':', + '(', ')', '{', '}', + ] } } @@ -87,6 +89,9 @@ impl Lexer { } if !c.is_ascii_alphabetic() && c != '_' + && c != '%' + && c != '@' + && c != '$' && !c.is_digit(10) { break; } @@ -334,6 +339,11 @@ impl Iterator for Lexer { self.skip_spaces(); self.skip_comments(); + if let Some(token) = self.scan_text("->") { + self.cursor = token.position; + return Some(Contextual(Node::RArrow, Context(self.line))); + } + if let Some(token) = self.scan_text("+=") { self.cursor = token.position; return Some(Contextual(Node::AssignAdd, Context(self.line))); @@ -349,6 +359,26 @@ impl Iterator for Lexer { return Some(Contextual(Node::Assign, Context(self.line))); } + if let Some(token) = self.scan_text("(") { + self.cursor = token.position; + return Some(Contextual(Node::OPar, Context(self.line))) + } + + if let Some(token) = self.scan_text(")") { + self.cursor = token.position; + return Some(Contextual(Node::CPar, Context(self.line))) + } + + if let Some(token) = self.scan_text("{") { + self.cursor = token.position; + return Some(Contextual(Node::OBrace, Context(self.line))) + } + + if let Some(token) = self.scan_text("}") { + self.cursor = token.position; + return Some(Contextual(Node::CBrace, Context(self.line))) + } + if let Some(token) = self.scan_text("[") { self.cursor = token.position; return Some(Contextual(Node::OSquare, Context(self.line))) @@ -359,6 +389,11 @@ impl Iterator for Lexer { return Some(Contextual(Node::CSquare, Context(self.line))) } + if let Some(token) = self.scan_keyword("command") { + self.cursor = token.position; + return Some(Contextual(Node::Command, Context(self.line))) + } + if let Some(token) = self.scan_keyword("true") { self.cursor = token.position; return Some(Contextual(Node::Bool(token.value == "true"), @@ -376,6 +411,11 @@ impl Iterator for Lexer { return Some(Contextual(Node::Semicolon, Context(self.line))) } + if let Some(token) = self.scan_text(":") { + self.cursor = token.position; + return Some(Contextual(Node::Colon, Context(self.line))) + } + if let Some(token) = self.scan_string() { self.cursor = token.position; return Some(Contextual(Node::String(token.value.clone()), @@ -441,6 +481,17 @@ mod test { Ok(result) } + + #[test] + fn test_cmd_call() { + let mut lex = Lexer::new(); + let res = lexer_run(&mut lex, " (true) ").unwrap(); + assert_eq!(3, res.len()); + assert_eq!(Node::OPar, res.get(0).unwrap().0); + assert_eq!(Node::Bool(true), res.get(1).unwrap().0); + assert_eq!(Node::CPar, res.get(2).unwrap().0); + } + #[test] fn test_booleans() { let mut lex = Lexer::new(); @@ -561,13 +612,13 @@ mod test { assert_eq!(4, res.len()); - assert_eq!(Node::Symbol(String::from("abc")), + assert_eq!(Node::Symbol(String::from("abc")), res.get(0).unwrap().0); - assert_eq!(Node::Symbol(String::from("+-*/")), + assert_eq!(Node::Symbol(String::from("+-*/")), res.get(1).unwrap().0); - assert_eq!(Node::Symbol(String::from("a")), + assert_eq!(Node::Symbol(String::from("a")), res.get(2).unwrap().0); assert_eq!(Node::Semicolon, res.get(3).unwrap().0); @@ -590,4 +641,45 @@ mod test { Ok(()) } + + #[test] + fn test_command() -> Result<(), error::AstError> { + let mut lex = Lexer::new(); + + let res = lexer_run(&mut lex, + " command: ->{ } %4 % $4 $ @47 @")?; + + assert_eq!(11, res.len()); + + assert_eq!(Node::Command, res.get(0).unwrap().0); + + assert_eq!(Node::Colon, res.get(1).unwrap().0); + + assert_eq!(Node::RArrow, res.get(2).unwrap().0); + + assert_eq!(Node::OBrace, res.get(3).unwrap().0); + + assert_eq!(Node::CBrace, res.get(4).unwrap().0); + + assert_eq!(Node::Ident(String::from("%4")), + res.get(5).unwrap().0); + + assert_eq!(Node::Ident(String::from("%")), + res.get(6).unwrap().0); + + assert_eq!(Node::Ident(String::from("$4")), + res.get(7).unwrap().0); + + assert_eq!(Node::Ident(String::from("$")), + res.get(8).unwrap().0); + + assert_eq!(Node::Ident(String::from("@47")), + res.get(9).unwrap().0); + + assert_eq!(Node::Ident(String::from("@")), + res.get(10).unwrap().0); + + Ok(()) + } + } diff --git a/src/ast/node.rs b/src/ast/node.rs index 52fa226..5409cd5 100644 --- a/src/ast/node.rs +++ b/src/ast/node.rs @@ -1,6 +1,7 @@ use crate::context::Contextual; #[derive(Debug)] +#[derive(Clone)] #[derive(PartialEq)] pub enum Node { Module(String, Vec>), @@ -9,8 +10,8 @@ pub enum Node { Float(f64), String(String), Symbol(String), - OSquare, - CSquare, + OSquare, CSquare, + OPar, CPar, Array(Vec>), Assign, AssignIf, @@ -19,7 +20,18 @@ pub enum Node { AssignIfInstr(Contextual>, Vec>), AssignAddInstr(Contextual>, Vec>), Ident(String), - Semicolon + Semicolon, Colon, + RArrow, + Command, + CmdCall(Contextual>, Vec>), + OBrace, CBrace, + CmdDef( + Contextual>, // target + Vec>, // requires + Vec>, // provides + Contextual> // body + ), + Block(Vec>), } diff --git a/src/ast/parser.rs b/src/ast/parser.rs index 5ed866a..97694bd 100644 --- a/src/ast/parser.rs +++ b/src/ast/parser.rs @@ -51,6 +51,10 @@ impl Parser { -> ParseResult { match self.lexer.next() { Some(Contextual(Node::Semicolon, _)) => result, + Some(Contextual(node, Context(line))) + => Err(error::AstError { + msg: format!("unexpected node {:?}", node), + line}), _ => Err(error::AstError { msg: String::from("missing semicolon"), line: self.lexer.line()}) @@ -58,6 +62,24 @@ impl Parser { } fn parse_instr(&mut self) -> ParseResult { + match self.lexer.peek(0) { + Some(Contextual(Node::Command, _)) => { return self.parse_cmd_def(); }, + Some(Contextual(Node::Ident(_), _)) => { + match self.lexer.peek(1) { + Some(Contextual(Node::Semicolon, _)) + | Some(Contextual(Node::Assign, _)) + | Some(Contextual(Node::AssignIf, _)) + | Some(Contextual(Node::AssignAdd, _)) => {}, + + _ => { + let cmd_call = self.parse_cmd_call(); + return self.ensure_semicolon(cmd_call); + } + } + }, + _ => {} + } + match self.lexer.peek(1) { Some(Contextual(Node::Assign, _)) => { let assign = self.parse_assign(); @@ -74,6 +96,7 @@ impl Parser { self.ensure_semicolon(assign) }, + _ => match self.parse_expr() { Ok(Some(expr)) => @@ -144,6 +167,135 @@ impl Parser { } } + fn parse_cmd_call(&mut self) -> ParseResult { + match self.lexer.next() { + Some(Contextual(Node::Ident(id), ctx)) + => { + let mut args: Vec> = vec![]; + + loop { + match self.lexer.peek(0) { + Some(Contextual(Node::CPar, _)) + | Some(Contextual(Node::Semicolon, _)) + => break, + Some(node) => { + args.push(node); + self.lexer.next(); + } + None => { + return Err(error::AstError { + msg: String::from("unexpected end of command"), + line: self.lexer.line() + }); + } + } + } + + Ok(Some(Contextual(Node::CmdCall( + Contextual(Box::new(Node::Ident(id)), ctx), + args + ), ctx))) + }, + Some(Contextual(node, Context(line))) => { + Err(error::AstError { + msg: format!("unexpected node <{:?}>", node), + line + }) + }, + None => { + Err(error::AstError { + msg: format!("unexpected end"), + line: self.lexer.line() + }) + }, + } + } + + fn parse_cmd_def(&mut self) -> ParseResult { + let _command = self.lexer.next(); + + let ident : Node; + match self.lexer.next() { + Some(Contextual(node, _)) => { ident = node; }, + _ => { + return Err(error::AstError { + msg: String::from("invalid command target"), + line: self.lexer.line() + }) + } + } + + let _colon = self.lexer.next(); + let mut requires: Vec> = vec![]; + let mut provides: Vec> = vec![]; + let mut block: Vec> = vec![]; + + loop { + if let Some(Contextual(Node::RArrow, _)) = self.lexer.peek(0) { + break; + } + + match self.parse_expr() { + Ok(Some(node)) => { requires.push(node); }, + Ok(None) => { + return Err(error::AstError { + msg: String::from("invalid command requirement"), + line: self.lexer.line() + }); + }, + Err(err) => { return Err(err); } + } + } + + let _rarrow = self.lexer.next(); + + loop { + if let Some(Contextual(Node::OBrace, _)) = self.lexer.peek(0) { + break; + } + + match self.parse_expr() { + Ok(Some(node)) => { provides.push(node); }, + Ok(None) => { + return Err(error::AstError { + msg: String::from("invalid command provide value"), + line: self.lexer.line() + }); + }, + Err(err) => { return Err(err); } + } + } + + let _obrace = self.lexer.next(); + + loop { + if let Some(Contextual(Node::CBrace, _)) = self.lexer.peek(0) { + break; + } + + match self.parse_instr() { + Ok(Some(node)) => { block.push(node); }, + Ok(None) => { + return Err(error::AstError { + msg: String::from("invalid command subcommand"), + line: self.lexer.line() + }); + }, + Err(err) => { return Err(err); } + } + } + + let _cbrace = self.lexer.next(); + let ctx = Context(self.lexer.line()); + + Ok(Some(Contextual(Node::CmdDef( + Contextual(Box::new(ident), ctx), + requires, + provides, + Contextual(Box::new(Node::Block(block)), ctx) + ),ctx))) + } + fn parse_expr(&mut self) -> ParseResult { self.parse_literal() } @@ -155,7 +307,16 @@ impl Parser { self.lexer.next(); Ok(Some(Contextual(Node::Ident(id), ctx))) }, + Some(Contextual(Node::OSquare, _)) => self.parse_array(), + + Some(Contextual(Node::OPar, _)) => { + let _opar = self.lexer.next(); + let res = self.parse_cmd_call(); + let _cpar = self.lexer.next(); + res + }, + Some(_) => self.parse_builtin(), None => { self.lexer.next(); Ok(None) } } @@ -232,6 +393,14 @@ mod test { } } + fn ctx(node: Node) -> Contextual { + Contextual(node, Context(1)) + } + + fn ctx_box(node: Node) -> Contextual> { + Contextual(Box::new(node), Context(1)) + } + #[test] fn premature_end() -> TestResult { let mut lex = lexer::Lexer::new(); @@ -287,8 +456,9 @@ mod test { fn symbols() -> TestResult { test_parser( Node::Module("mod".to_owned(), vec![ - Contextual(Node::Symbol(String::from("bonjour++")), - Context(0)) + Contextual(Node::Symbol( + String::from("bonjour++") + ), Context(0)) ]), " 'bonjour++ ; " )?; @@ -300,7 +470,7 @@ mod test { fn strings() -> TestResult { test_parser( Node::Module("mod".to_owned(), vec![ - Contextual(Node::String(String::from("pizza! ")), + Contextual(Node::String(String::from("pizza! ")), Context(0)) ]), " \"pizza! \"; " @@ -373,4 +543,73 @@ mod test { Ok(()) } + + #[test] + fn cmd_call_instr() -> TestResult { + test_parser( + Node::Module("mod".to_owned(), vec![ + ctx(Node::CmdCall( + ctx_box(Node::Ident(String::from("hello"))), + vec![] + )) + ]), + " (hello) ; " + )?; + + test_parser( + Node::Module("mod".to_owned(), vec![ + ctx(Node::CmdCall( + ctx_box(Node::Ident(String::from("hello"))), + vec![ + ctx(Node::Int(4)), + ctx(Node::Symbol(String::from("b"))), + ctx(Node::Symbol(String::from("c"))), + ] + )) + ]), + " hello 4 'b 'c; " + )?; + + Ok(()) + } + + #[test] + fn cmd_def() -> TestResult { + test_parser( + Node::Module("mod".to_owned(), vec![ + ctx(Node::CmdDef( + ctx_box(Node::Ident(String::from("hello"))), + vec![ + ], + vec![ + ], + ctx_box(Node::Block(vec![ + ])) + )) + ]), + " command hello: -> { } " + )?; + + test_parser( + Node::Module("mod".to_owned(), vec![ + ctx(Node::CmdDef( + ctx_box(Node::Ident(String::from("hello"))), + vec![ + ctx(Node::Ident(String::from("a"))), + ctx(Node::Ident(String::from("b"))), + ctx(Node::Ident(String::from("c"))), + ], + vec![ + ctx(Node::Ident(String::from("d"))), + ctx(Node::Ident(String::from("e"))), + ], + ctx_box(Node::Block(vec![ + ])) + )) + ]), + " command hello: a b c -> d e { } " + )?; + + Ok(()) + } } diff --git a/src/context.rs b/src/context.rs index af66105..be15d01 100644 --- a/src/context.rs +++ b/src/context.rs @@ -5,10 +5,11 @@ pub struct Context(pub i32); #[derive(Debug)] +#[derive(Clone)] pub struct Contextual(pub T, pub Context); impl PartialEq> for Contextual { fn eq(&self, other: &Self) -> bool { self.0 == other.0 } -} +} diff --git a/src/eval/cmd/builder.rs b/src/eval/cmd/builder.rs new file mode 100644 index 0000000..87a722b --- /dev/null +++ b/src/eval/cmd/builder.rs @@ -0,0 +1,40 @@ +use crate::eval::{ + cmd::Command, + value::Value +}; +use crate::ast::node::Node; + +pub struct CmdBuilder { + command: Command +} + +impl CmdBuilder { + pub fn new() -> Self { + Self { + command: Command { + requires: vec![], + provides: vec![], + body: vec![] + } + } + } + + pub fn require(&mut self, dep: Value) -> &mut Self { + self.command.requires.push(dep); + self + } + + pub fn provide(&mut self, dep: Value) -> &mut Self { + self.command.provides.push(dep); + self + } + + pub fn instr(&mut self, cmd: Node) -> &mut Self { + self.command.body.push(cmd); + self + } + + pub fn build(self) -> Command { + self.command + } +} diff --git a/src/eval/cmd/mod.rs b/src/eval/cmd/mod.rs new file mode 100644 index 0000000..ab6bf2d --- /dev/null +++ b/src/eval/cmd/mod.rs @@ -0,0 +1,72 @@ +pub mod runner; +pub mod builder; + +use crate::eval::{ + value::Value, + error::EvalError, + evaluator::Evaluator +}; +use crate::context::{Contextual, Context}; +use crate::ast::node::Node; + +pub type CmdCallResult = Result<(), EvalError>; + +pub trait Cmd { + fn requires(&self) -> Vec { + vec![] + } + + fn provides(&self) -> Vec { + vec![] + } + + fn call(&mut self, args: Vec) -> CmdCallResult; +} + +pub struct Command { + requires: Vec, + provides: Vec, + body: Vec +} + +impl Cmd for Command { + fn requires(&self) -> Vec { + self.requires.clone().into_iter().collect() + } + + fn provides(&self) -> Vec { + self.provides.clone().into_iter().collect() + } + + fn call(&mut self, args: Vec) -> CmdCallResult { + let mut eval = Evaluator::new(); + + eval.symbols(|table| { + // Arguments + table.set("%", Value::Array(args.clone())); + for i in 0..args.len() { + table.set(&format!("%{}", i), + args.get(i).unwrap().clone()); + } + + // Requirements + table.set("$", Value::Array(self.requires())); + for (i, req) in self.requires().iter().enumerate() { + table.set(&format!("${}", i), req.clone()); + } + + // Provided values + table.set("@", Value::Array(self.provides())); + for (i, prov) in self.provides().iter().enumerate() { + table.set(&format!("@{}", i), prov.clone()); + } + }); + + for instr in self.body.iter() { + let i = instr.clone(); + let c = Contextual(i, Context(0)); + eval.run(&c, &args)?; + } + Ok(()) + } +} diff --git a/src/eval/cmd/runner.rs b/src/eval/cmd/runner.rs new file mode 100644 index 0000000..79daedd --- /dev/null +++ b/src/eval/cmd/runner.rs @@ -0,0 +1,45 @@ +use std::collections::HashMap; +use crate::eval::{ + error::EvalError, + cmd::Cmd, + value::Value +}; + +pub struct CommandRunner { + commands: HashMap> +} + +impl CommandRunner { + pub fn new() -> Self { + Self { + commands: HashMap::new() + } + } + + pub fn register(&mut self, name: &str, cmd: Box) -> Result<(), EvalError> { + if self.commands.contains_key(&name.to_owned()) { + return Err(EvalError { + msg: format!("command {} already defined", name), + line: 0 + }); + } + self.commands.insert(name.to_owned(), cmd); + Ok(()) + } + + pub fn call(&mut self, name: &str, args: Vec) -> Result<(), EvalError> { + match self.commands.get_mut(&name.to_owned()) { + Some(cmd) => { + cmd.call(args) + }, + + None => { + Err(EvalError { + msg: format!("command {} not defined", name), + line: 0 + }) + } + } + } +} + diff --git a/src/eval/evaluator.rs b/src/eval/evaluator.rs index 59d9a2c..95c194e 100644 --- a/src/eval/evaluator.rs +++ b/src/eval/evaluator.rs @@ -4,30 +4,53 @@ use crate::{ eval::{ value::Value, error::EvalError, - symtable::SymTable + symtable::SymTable, + cmd::{ + runner::CommandRunner, + builder::CmdBuilder + }, } }; +use crate::natives::cmd::*; + pub struct Evaluator { - table: SymTable + table: SymTable, + runner: CommandRunner } type EvalResult = Result, EvalError>; impl Evaluator { pub fn new() -> Self { + let mut runner = CommandRunner::new(); + + runner.register("print", + Box::new(PrintCmd {})).expect("cannot initialize print"); + + runner.register("sh", + Box::new(ShCmd {})).expect("cannot initialize sh"); + Self { - table: SymTable::new() + table: SymTable::new(), + runner } } - pub fn run(&mut self, root: &Contextual) -> EvalResult { + pub fn symbols(&mut self, callback: T) +where T: Fn(&mut SymTable) + { + callback(&mut self.table); + } + + pub fn run(&mut self, root: &Contextual, args: &Vec) + -> EvalResult { match &root.0 { Node::Module(_, nodes) => { let mut res = None; for node in nodes.iter() { - res = self.run(node)? + res = self.run(node, &args)? } Ok(res) @@ -63,7 +86,7 @@ impl Evaluator { } for node_val in values { - let val = self.run(&node_val)?; + let val = self.run(&node_val, &args)?; if let Some(v) = val { val_array.push(v); } @@ -93,7 +116,7 @@ impl Evaluator { Some(_) => {}, None => { if let Some(value) - = self.nodes_to_value(&values)? { + = self.nodes_to_value(&values)? { self.table.set(&name, value); } } @@ -142,15 +165,15 @@ impl Evaluator { Node::String(value) => Ok(Some(Value::String(value.clone()))), - + Node::Symbol(value) => Ok(Some(Value::Symbol(value.clone()))), - + Node::Array(nodes) => { let mut values: Vec = vec![]; for node in nodes { - match self.run(&node) { + match self.run(&node, &args) { Ok(Some(value)) => { values.push(value); }, @@ -168,15 +191,122 @@ impl Evaluator { Ok(Some(Value::Array(values))) } - _ => Ok(None) + Node::CmdDef( + Contextual(target, ctx), + requires, + provides, + Contextual(body, body_ctx) + ) => { + match &**target { + Node::Ident(name) => { + let mut builder = CmdBuilder::new(); + + for req in requires.iter() { + if let Some(val) = self.run(req, &args)? { + builder.require(val); + } else { + return Err(EvalError { + msg: format!("bad requirement {:?}", req), + line: body_ctx.0 + }); + } + } + + for prov in provides.iter() { + if let Some(val) = self.run(prov, &args)? { + builder.provide(val); + } else { + return Err(EvalError { + msg: format!("bad provided value {:?}", prov), + line: body_ctx.0 + }); + } + } + + match **body { + Node::Block(ref commands) => { + for subcmd in commands.iter() { + let Contextual(node, _) = subcmd; + builder.instr(node.clone()); + } + + self.runner.register( + name, Box::new(builder.build()) + )?; + + Ok(None) + }, + + _ => { + Err(EvalError { + msg: String::from("wrong block"), + line: body_ctx.0 + + }) + } + } + }, + + _ => { Err(EvalError { + msg: String::from( + "wrong target command", + ), + line: ctx.0 + }) } + } + }, + + Node::CmdCall( + Contextual(target, ctx), + args_ctx + ) => { + let mut args: Vec = vec![]; + for a in args_ctx.iter() { + match self.run(a, &args) { + Ok(Some(val)) => { args.push(val); }, + Err(err) => { return Err(err); }, + _ => { + return Err(EvalError { + msg: String::from("unexpected command argument"), + line: ctx.0 + }); + } + } + } + + match &**target { + Node::Ident(name) => { + match self.runner.call(&name, args) { + Ok(()) => {}, + Err(err) => { return Err(err); } + } + } + _ => { + return Err(EvalError { + msg: String::from("target error"), + line: ctx.0 + }); + } + } + + Ok(None) + }, + + _ => { + Err(EvalError { + msg: format!("invalid node <{:?}>", root), + line: root.1.0 + }) + } + //_ => Ok(None) } } fn nodes_to_value(&mut self, nodes: &Vec>) -> EvalResult { let mut val_array: Vec = vec![]; - + let args: Vec = vec![]; for node in nodes { - match self.run(&node) { + match self.run(&node, &args) { Ok(Some(val)) => val_array.push(val), Ok(None) => {}, Err(err) => { return Err(err); } @@ -211,10 +341,11 @@ mod test { let mut parser = Parser::new("mod", lex); let mut evaluator = Evaluator::new(); let root = parser.run()?; + let args: Vec = vec![]; match root { Some(root) => { - if let Some(val) = evaluator.run(&root)? { + if let Some(val) = evaluator.run(&root, &args)? { if val != value { Err(Box::new(EvalError { msg: format!("expected: {:?}, got: {:?}", value, val), @@ -244,8 +375,9 @@ mod test { let root = parser.run()?.unwrap(); let mut evaluator = Evaluator::new(); + let args: Vec = vec![]; - match evaluator.run(&root) { + match evaluator.run(&root, &args) { Err(_) => Ok(()), _ => Err(Box::new(EvalError { msg: format!("\"{}\" should fail", source), diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 72340d0..ef2bbb4 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -2,3 +2,4 @@ pub mod value; pub mod evaluator; pub mod error; pub mod symtable; +pub mod cmd; diff --git a/src/eval/value.rs b/src/eval/value.rs index f96c748..181d21d 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -10,3 +10,25 @@ pub enum Value { Array(Vec) } +pub fn concat_value(value: Value) -> String { + let mut result: Vec = vec![]; + + match value { + Value::String(value) => { + result.push(value.clone()); + }, + Value::Symbol(value) => { + result.push(value.clone()); + }, + Value::Array(values) => { + for v in values { + result.push(concat_value(v)); + } + }, + Value::Int(value) => {result.push(format!("{}", value))}, + Value::Float(value) => {result.push(format!("{}", value))}, + Value::Bool(value) => {result.push(format!("{}", value))}, + } + + result.join(" ") +} diff --git a/src/main.rs b/src/main.rs index b019526..29d5ac3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,9 @@ use std::fs; - +use crate::eval::value::Value; mod ast; mod eval; mod context; +mod natives; fn main() -> Result<(), Box> { let args: Vec = std::env::args().collect(); @@ -19,7 +20,9 @@ fn main() -> Result<(), Box> { let mut evaluator = eval::evaluator::Evaluator::new(); - if let Some(val) = evaluator.run(&root)? { + let args: Vec = vec![]; + + if let Some(val) = evaluator.run(&root, &args)? { println!("{:?}", val); } } diff --git a/src/natives/cmd.rs b/src/natives/cmd.rs new file mode 100644 index 0000000..ca49366 --- /dev/null +++ b/src/natives/cmd.rs @@ -0,0 +1,52 @@ +use std::process; +use crate::eval::cmd::{ + Cmd, + CmdCallResult +}; +use crate::eval::value::{ + Value, + concat_value +}; + +pub struct PrintCmd; + +impl Cmd for PrintCmd { + fn call(&mut self, args: Vec) -> CmdCallResult { + let mut sep = String::from(""); + + for arg in args.iter() { + print!("{}{}", sep, concat_value(arg.clone())); + sep = String::from(" "); + } + + print!("\n"); + + Ok(()) + } +} + +pub struct ShCmd; + + +impl Cmd for ShCmd { + fn call(&mut self, args: Vec) -> CmdCallResult { + let mut str_args: Vec = vec![]; + + for arg in args.iter() { + str_args.push(concat_value(arg.clone())); + } + + let my_cmd = str_args.join(" "); + + let out = process::Command::new("sh") + .arg("-c") + .arg(my_cmd) + .stdout(process::Stdio::piped()) + .output() + .expect("command failed"); + + print!("{}", String::from_utf8_lossy(&out.stdout)); + + Ok(()) + } +} diff --git a/src/natives/mod.rs b/src/natives/mod.rs new file mode 100644 index 0000000..52958ec --- /dev/null +++ b/src/natives/mod.rs @@ -0,0 +1 @@ +pub mod cmd;