Browse Source

dot syntax works for user defined functions

master
Inderjit Gill 5 months ago
parent
commit
eb4de8f989
4 changed files with 188 additions and 12 deletions
  1. +129
    -12
      core/src/compiler.rs
  2. +11
    -0
      core/src/lexer.rs
  3. +47
    -0
      core/src/parser.rs
  4. +1
    -0
      core/src/unparser.rs

+ 129
- 12
core/src/compiler.rs View File

@@ -212,6 +212,12 @@ fn assign_genes_to_nodes(node: &mut Node, genotype: &mut Genotype) -> Result<()>
genotype.current_gene_index += 1;
}
}
Node::FromName(_, _, meta) => {
if let Some(ref mut node_meta) = meta {
node_meta.gene = Some(genotype.genes[genotype.current_gene_index].clone());
genotype.current_gene_index += 1;
}
}
Node::Name(_, _, meta) => {
if let Some(ref mut node_meta) = meta {
node_meta.gene = Some(genotype.genes[genotype.current_gene_index].clone());
@@ -1211,6 +1217,22 @@ impl Compiler {
Err(Error::Compiler)
};
}
Node::FromName(text, _, _) => {
let iname = self.get_iname(ast)?;

return if self.compile_user_defined_name(compilation, iname)? {
Ok(())
} else if let Some(kw) = self.name_to_keyword.get(&iname) {
compilation.emit(Opcode::LOAD, Mem::Constant, *kw)?;
Ok(())
} else if let Some(native) = self.name_to_native.get(&iname) {
compilation.emit(Opcode::NATIVE, *native, 0)?;
Ok(())
} else {
error!("compile: can't find user defined name or keyword: {}", text);
Err(Error::Compiler)
};
}
_ => {
error!("compile ast: {:?}", ast);
return Err(Error::Compiler);
@@ -1232,17 +1254,35 @@ impl Compiler {
let kids = only_semantic_nodes(kids);
self.compile_list(compilation, &kids[..])?
}
Node::FromName(_, _, _) => {
// syntax sugar for the 'from' parameter
// e.g. (some-vec.vector/length)
// is equivalent to (vector/length from: some-vec)

if children.len() == 1 || !children[1].is_name() {
error!("Node::FromName should always be followed by a Node::Name");
return Err(Error::Compiler);
}

if let Some(fn_info_index) = compilation.get_fn_info_index(&children[1]) {

self.compile_fn_invocation_prologue(compilation, fn_info_index)?;
self.compile_fn_invocation_implicit_from(compilation, fn_info_index, &children[0])?;
self.compile_fn_invocation_args(compilation, &children[2..], fn_info_index)?;
self.compile_fn_invocation_epilogue(compilation, fn_info_index)?;

}
}
Node::Name(_, _, _) => {
let iname = self.get_iname(&children[0])?;

if let Some(fn_info_index) = compilation.get_fn_info_index(&children[0]) {
// todo: get_fn_info_index is re-checking that this is a Node::Name
self.compile_fn_invocation(compilation, &children[1..], fn_info_index)?;
return Ok(());
}

if self.compile_user_defined_name(compilation, iname)? {
return Ok(());
self.compile_fn_invocation_prologue(compilation, fn_info_index)?;
self.compile_fn_invocation_args(compilation, &children[1..], fn_info_index)?;
self.compile_fn_invocation_epilogue(compilation, fn_info_index)?;

} else if let Some(kw) = self.name_to_keyword.get(&iname) {
match *kw {
Keyword::Define => {
@@ -2231,13 +2271,10 @@ impl Compiler {
Ok(())
}

// if (adder a: 10 b: 20) then children == a: 10 b: 20
fn compile_fn_invocation(
&self,
compilation: &mut Compilation,
children: &[&Node],
fn_info_index: usize,
) -> Result<()> {

fn compile_fn_invocation_prologue(&self,
compilation: &mut Compilation,
fn_info_index: usize) -> Result<()> {
// NOTE: CALL and CALL_0 get their function offsets and num args from the
// stack so add some placeholder LOAD CONST opcodes and fill the CALL, CALL_0
// with fn_info indexes that can later be used to fill in the LOAD CONST
@@ -2253,6 +2290,29 @@ impl Compiler {

compilation.emit(Opcode::CALL, fn_info_index, fn_info_index)?;

Ok(())
}

fn compile_fn_invocation_implicit_from(
&self,
compilation: &mut Compilation,
fn_info_index: usize,
from_name: &Node,
) -> Result<()> {

self.compile(compilation, from_name)?;
let from_iname = Iname::from(Keyword::From);
compilation.emit(Opcode::PLACEHOLDER_STORE, fn_info_index, from_iname)?;

Ok(())
}

fn compile_fn_invocation_args(
&self,
compilation: &mut Compilation,
children: &[&Node],
fn_info_index: usize,
) -> Result<()> {
// overwrite the default arguments with the actual arguments given by the fn invocation
let mut arg_vals = &children[..];
while arg_vals.len() > 0 {
@@ -2272,7 +2332,12 @@ impl Compiler {
return Err(Error::Compiler);
}
}
Ok(())
}

fn compile_fn_invocation_epilogue(&self,
compilation: &mut Compilation,
fn_info_index: usize) -> Result<()> {
// call the body of the function
compilation.emit(Opcode::LOAD, Mem::Constant, NONSENSE)?;
compilation.emit(Opcode::CALL_0, fn_info_index, fn_info_index)?;
@@ -3224,4 +3289,56 @@ mod tests {
);
}

#[test]
fn test_fromname_fn_invocation() {
let expected_bytecode = vec![
jump(14),
load_const_name(259),
store_arg(0),
load_const_f32(99.0),
store_arg(1),
load_const_name(246),
store_arg(2),
load_const_f32(88.0),
store_arg(3),
ret_0(),
load_arg(1),
load_arg(3),
add(),
ret(),
load_const_f32(33.0),
store_global(15),
load_const_i32(1),
load_const_i32(2),
call(),
load_global_i32(15),
store_arg(1),
load_const_f32(10.0),
store_arg(3),
load_const_i32(10),
call_0(),
stop(),
];

// traditional syntax
assert_eq!(
compile(
"(define x 33)
(fn (increase from: 99 by: 88) (+ from by))
(increase from: x by: 10)"
),
expected_bytecode
);

// fromname syntax
assert_eq!(
compile(
"(define x 33)
(fn (increase from: 99 by: 88) (+ from by))
(x.increase by: 10)"
),
expected_bytecode
);
}

}

+ 11
- 0
core/src/lexer.rs View File

@@ -25,6 +25,7 @@ pub enum Token<'a> {
Comment(&'a str),
CurlyBracketEnd,
CurlyBracketStart,
Dot,
String(&'a str),
Name(&'a str),
Number(&'a str),
@@ -74,6 +75,7 @@ impl<'a> Lexer<'a> {
']' => Ok((Token::SquareBracketEnd, 1)),
'{' => Ok((Token::CurlyBracketStart, 1)),
'}' => Ok((Token::CurlyBracketEnd, 1)),
'.' => Ok((Token::Dot, 1)),
':' => Ok((Token::Colon, 1)),
'\'' => Ok((Token::Quote, 1)),
'`' => Ok((Token::BackQuote, 1)),
@@ -249,6 +251,15 @@ mod tests {
]
);

assert_eq!(
tokenize("some-vector.vec/length").unwrap(),
[
Token::Name("some-vector"),
Token::Dot,
Token::Name("vec/length")
]
);

assert_eq!(tokenize("5").unwrap(), [Token::Number("5")]);
assert_eq!(tokenize("-3").unwrap(), [Token::Number("-3")]);
assert_eq!(tokenize("3.14").unwrap(), [Token::Number("3.14")]);

+ 47
- 0
core/src/parser.rs View File

@@ -51,6 +51,7 @@ pub enum Node {
List(Vec<Node>, Option<NodeMeta>),
Vector(Vec<Node>, Option<NodeMeta>),
Float(f32, String, Option<NodeMeta>),
FromName(String, Iname, Option<NodeMeta>), // text, iname, meta
Name(String, Iname, Option<NodeMeta>), // text, iname, meta
Label(String, Iname, Option<NodeMeta>), // text, iname, meta
String(String, Iname, Option<NodeMeta>),
@@ -107,6 +108,23 @@ impl Node {
return Ok(*iname);
}
}
Node::FromName(_text, iname, meta) => {
if use_genes && meta.is_some() {
if let Some(meta) = meta {
if let Some(gene) = &meta.gene {
match gene {
Gene::Name(i) => return Ok(*i),
_ => {
error!("Node::get_iname incompatible gene for FromName");
return Err(Error::Parser);
}
}
}
}
} else {
return Ok(*iname);
}
}
Node::String(_text, iname, meta) => {
if use_genes && meta.is_some() {
if let Some(meta) = meta {
@@ -210,11 +228,19 @@ impl Node {
Err(Error::Parser)
}

pub fn is_name(&self) -> bool {
match self {
Node::Name(_, _, _) => true,
_ => false
}
}

pub fn is_alterable(&self) -> bool {
match self {
Node::List(_, meta)
| Node::Vector(_, meta)
| Node::Float(_, _, meta)
| Node::FromName(_, _, meta)
| Node::Name(_, _, meta)
| Node::Label(_, _, meta)
| Node::String(_, _, meta)
@@ -228,6 +254,7 @@ impl Node {
Node::List(_, meta)
| Node::Vector(_, meta)
| Node::Float(_, _, meta)
| Node::FromName(_, _, meta)
| Node::Name(_, _, meta)
| Node::Label(_, _, meta)
| Node::String(_, _, meta)
@@ -498,6 +525,7 @@ fn eat_alterable<'a>(t: &'a [Token<'a>], word_lut: &WordLut) -> Result<NodeAndRe
Node::List(ns, _) => Node::List(ns, meta),
Node::Vector(ns, _) => Node::Vector(ns, meta),
Node::Float(f, s, _) => Node::Float(f, s, meta),
Node::FromName(s, i, _) => Node::FromName(s, i, meta),
Node::Name(s, i, _) => Node::Name(s, i, meta),
Node::Label(s, i, _) => Node::Label(s, i, meta),
Node::String(s, i, _) => Node::String(s, i, meta),
@@ -562,6 +590,11 @@ fn eat_token<'a>(
node: Node::Label(t, ti, meta),
tokens: &tokens[2..],
})
} else if tokens.len() > 1 && tokens[1] == Token::Dot {
Ok(NodeAndRemainder {
node: Node::FromName(t, ti, meta),
tokens: &tokens[2..],
})
} else {
Ok(NodeAndRemainder {
node: Node::Name(t, ti, meta),
@@ -798,4 +831,18 @@ mod tests {
)]
);
}

#[test]
fn test_parser_from_name() {
assert_eq!(
ast("(some-vector.vector/length)"),
[Node::List(
vec![
Node::FromName("some-vector".to_string(), Iname::new(0), None),
Node::Name("vector/length".to_string(), Iname::from(Native::VectorLength), None),
],
None
)]
);
}
}

+ 1
- 0
core/src/unparser.rs View File

@@ -207,6 +207,7 @@ fn format_node_value(node: &Node) -> Result<String> {
Err(Error::Unparser)
}
Node::Float(_, s, _) => Ok(s.to_string()),
Node::FromName(s, _, _) => Ok(s.to_string() + "."),
Node::Name(s, _, _) => Ok(s.to_string()),
Node::Label(s, _, _) => Ok(s.to_string() + ":"),
Node::String(s, _, _) => Ok("\"".to_owned() + &s.to_string() + "\""),

Loading…
Cancel
Save