Skip to content

Commit

Permalink
wip: get clarification for data model
Browse files Browse the repository at this point in the history
This is yet another intermediate commit.
Compiler::get() returns any ImlValue with a name.
ImlOp and ImlValue will merge as well in their meaning.
  • Loading branch information
phorward committed Apr 4, 2023
1 parent 0010a84 commit d4ee396
Show file tree
Hide file tree
Showing 5 changed files with 198 additions and 213 deletions.
184 changes: 94 additions & 90 deletions src/compiler/ast.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
//! Compiler's internal Abstract Syntax Tree traversal
use indexmap::IndexMap;
use std::collections::HashMap;
use tokay_macros::tokay_function;
extern crate self as tokay;
use super::*;
Expand Down Expand Up @@ -182,17 +181,25 @@ fn traverse_node_value(compiler: &mut Compiler, node: &Dict) -> ImlValue {

match emit {
"gen" => {
let offset = traverse_node_offset(node);

// check if identifier was not provided twice
if constants.contains_key(&ident) {
compiler.errors.push(Error::new(
traverse_node_offset(node),
offset,
format!("Generic '{}' already given in signature before", ident),
));

continue;
}

compiler.set_constant(&ident, ImlValue::Undefined(ident.to_string()));
compiler.set_constant(
&ident,
ImlValue::Generic {
offset,
name: ident.to_string(),
},
);

assert!(children.len() <= 2);

Expand Down Expand Up @@ -292,32 +299,21 @@ fn traverse_node_value(compiler: &mut Compiler, node: &Dict) -> ImlValue {
let target = traverse_node_static(compiler, None, target);

// Traverse generic arguments
let mut by_seq = Vec::new();
let mut by_name = IndexMap::new();
let mut config = Vec::new();

for genarg in children[1..].iter() {
let genarg = genarg.borrow();
let genarg = genarg.object::<Dict>().unwrap();

let offset = traverse_node_offset(genarg);
let emit = genarg["emit"].borrow();

match emit.object::<Str>().unwrap().as_str() {
"genarg" => {
if !by_name.is_empty() {
compiler.errors.push(Error::new(
traverse_node_offset(genarg),
format!(
"Sequencial constants need to be specified before named constants."
),
));

continue;
}

let param = &genarg["children"].borrow();
let param = param.object::<Dict>().unwrap();

by_seq.push(traverse_node_static(compiler, None, param));
config.push((offset, None, traverse_node_static(compiler, None, param)));
}

"genarg_named" => {
Expand All @@ -328,6 +324,7 @@ fn traverse_node_value(compiler: &mut Compiler, node: &Dict) -> ImlValue {
let ident = ident["value"].borrow();
let ident = ident.object::<Str>().unwrap().as_str();

/*
if by_name.contains_key(ident) {
compiler.errors.push(Error::new(
traverse_node_offset(genarg),
Expand All @@ -336,24 +333,26 @@ fn traverse_node_value(compiler: &mut Compiler, node: &Dict) -> ImlValue {
continue;
}
*/

let param = &children[1].borrow();
let param = param.object::<Dict>().unwrap();

by_name.insert(
ident.to_string(),
config.push((
offset,
Some(ident.to_string()),
traverse_node_static(compiler, None, param),
);
));
}

other => unimplemented!("Unhandled genarg type {:?}", other),
}
}

let mut ret = ImlValue::Generic {
let mut ret = ImlValue::Instance {
target: Box::new(target),
by_seq,
by_name,
config,
offset: traverse_node_offset(node),
};

ret.resolve(compiler);
Expand Down Expand Up @@ -473,93 +472,98 @@ fn traverse_node_lvalue(compiler: &mut Compiler, node: &Dict, store: bool, hold:
let name = item["value"].borrow();
let name = name.object::<Str>().unwrap().as_str();

// Check for not assigning to a constant (at any level)
if compiler.get_constant(name).is_some() {
compiler.errors.push(Error::new(
traverse_node_offset(node),
format!("Cannot assign to constant '{}'", name),
));

break;
}

// Check if identifier is valid
if let Err(mut error) = identifier_is_valid(name) {
if let Some(offset) = traverse_node_offset(node) {
error.patch_offset(offset);
}

compiler.errors.push(error);
break;
}

// Check if identifier is not defining a consumable
if utils::identifier_is_consumable(name) {
compiler.errors.push(Error::new(
traverse_node_offset(node),

if &name[0..1] == "_" {
format!(
"The variable '{}' is invalid, only constants may start with '_'",
name
)
}
else {
format!(
"Cannot assign variable named '{}'; Try lower-case identifier, e.g. '{}'",
name, name.to_lowercase()
)
}
));

break;
}

/* Generates code for a symbol store, which means:
1. look-up local variable, and store into
2. look-up global variable, and store into
3. create local variable, and store into
*/
if let Some(addr) = compiler.get_local(name) {
if store {
if hold {
ops.push(Op::StoreFastHold(addr).into())
match compiler.get(name) {
// Known local
Some(ImlValue::Local(addr)) => {
if store {
if hold {
ops.push(Op::StoreFastHold(addr).into())
} else {
ops.push(Op::StoreFast(addr).into())
}
} else {
ops.push(Op::StoreFast(addr).into())
ops.push(Op::LoadFast(addr).into())
}
} else {
ops.push(Op::LoadFast(addr).into())
}
} else if let Some(addr) = compiler.get_global(name) {
if store {
if hold {
ops.push(Op::StoreGlobalHold(addr).into())
// Known global
Some(ImlValue::Global(addr)) => {
if store {
if hold {
ops.push(Op::StoreGlobalHold(addr).into())
} else {
ops.push(Op::StoreGlobal(addr).into())
}
} else {
ops.push(Op::StoreGlobal(addr).into())
ops.push(Op::LoadGlobal(addr).into())
}
} else {
ops.push(Op::LoadGlobal(addr).into())
}
} else {
// When chained lvalue, name must be declared!
if children.len() > 1 {
// Check for not assigning to a constant (at any level)
Some(_) => {
compiler.errors.push(Error::new(
traverse_node_offset(node),
format!("Undeclared variable '{}', please define it first", name),
format!("Cannot assign to constant '{}'", name),
));
break;
}
// Undefined name
None => {
// Check if identifier is valid
if let Err(mut error) = identifier_is_valid(name) {
if let Some(offset) = traverse_node_offset(node) {
error.patch_offset(offset);
}

let addr = compiler.new_local(name);
if store {
if hold {
ops.push(Op::StoreFastHold(addr).into())
compiler.errors.push(error);
break;
}

// Check if identifier is not defining a consumable
if utils::identifier_is_consumable(name) {
compiler.errors.push(Error::new(
traverse_node_offset(node),

if &name[0..1] == "_" {
format!(
"The variable '{}' is invalid, only constants may start with '_'",
name
)
}
else {
format!(
"Cannot assign variable named '{}'; Try lower-case identifier, e.g. '{}'",
name, name.to_lowercase()
)
}
));

break;
}

// When chained lvalue, name must be declared!
if children.len() > 1 {
compiler.errors.push(Error::new(
traverse_node_offset(node),
format!("Undeclared variable '{}', please define it first", name),
));
break;
}

let addr = compiler.new_local(name);
if store {
if hold {
ops.push(Op::StoreFastHold(addr).into())
} else {
ops.push(Op::StoreFast(addr).into())
}
} else {
ops.push(Op::StoreFast(addr).into())
ops.push(Op::LoadFast(addr).into())
}
} else {
ops.push(Op::LoadFast(addr).into())
}
}
}
Expand Down
81 changes: 40 additions & 41 deletions src/compiler/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,25 +324,6 @@ impl Compiler {
unreachable!("There _must_ be at least one parselet scope!");
}

/** Retrieves the address of a local variable under a given name.
Returns None when the variable does not exist. */
pub(in crate::compiler) fn get_local(&self, name: &str) -> Option<usize> {
// Retrieve local variables from next parselet scope owning variables, except global scope!
for scope in &self.scopes[..self.scopes.len() - 1] {
// Check for scope with variables
if let Scope::Parselet { variables, .. } = &scope {
if let Some(addr) = variables.get(name) {
return Some(*addr);
}

break;
}
}

None
}

/** Insert new local variable under given name in current scope. */
pub(in crate::compiler) fn new_local(&mut self, name: &str) -> usize {
for scope in &mut self.scopes {
Expand Down Expand Up @@ -400,19 +381,6 @@ impl Compiler {
unreachable!("There _must_ be at least one parselet scope!");
}

/** Retrieve address of a global variable. */
pub(in crate::compiler) fn get_global(&self, name: &str) -> Option<usize> {
if let Scope::Parselet { variables, .. } = self.scopes.last().unwrap() {
if let Some(addr) = variables.get(name) {
return Some(*addr);
}

return None;
}

unreachable!("Top-level scope is not a parselet scope");
}

/** Set constant to name in current scope. */
pub(in crate::compiler) fn set_constant(&mut self, name: &str, mut value: ImlValue) {
/*
Expand Down Expand Up @@ -475,15 +443,46 @@ impl Compiler {
unreachable!("There _must_ be at least one parselet or block scope!");
}

/** Get constant value, either from current or preceding scope,
a builtin or special. */
pub(in crate::compiler) fn get_constant(&mut self, name: &str) -> Option<ImlValue> {
// Check for constant in available scopes
for scope in &self.scopes {
if let Scope::Parselet { constants, .. } | Scope::Block { constants, .. } = scope {
if let Some(value) = constants.get(name) {
return Some(value.clone());
/** Get named value, either from current or preceding scope, a builtin or special. */
pub(in crate::compiler) fn get(&mut self, name: &str) -> Option<ImlValue> {
let mut top_parselet = true;

for (i, scope) in self.scopes.iter().enumerate() {
match scope {
Scope::Block { constants, .. } => {
if let Some(value) = constants.get(name) {
return Some(value.clone());
}
}
Scope::Parselet {
constants,
variables,
..
} => {
if let Some(value) = constants.get(name) {
if !top_parselet && matches!(value, ImlValue::Generic { .. }) {
continue;
}

return Some(value.clone());
}

// Check for global variable
if i + 1 == self.scopes.len() {
if let Some(addr) = variables.get(name) {
return Some(ImlValue::Global(*addr));
}
}
// Check for local variable
else if top_parselet {
if let Some(addr) = variables.get(name) {
return Some(ImlValue::Local(*addr));
}
}

top_parselet = false;
}
_ => {}
}
}

Expand All @@ -505,7 +504,7 @@ impl Compiler {
RefValue::from(Token::builtin("Whitespaces").unwrap()).into(),
);

return Some(self.get_constant(name).unwrap());
return Some(self.get(name).unwrap());
}

// Check for built-in token
Expand Down
Loading

0 comments on commit d4ee396

Please sign in to comment.