Compare commits
2 Commits
2184f7e654
...
83c0c59ae9
Author | SHA1 | Date | |
---|---|---|---|
83c0c59ae9 | |||
cb1c0d80f8 |
@ -4,7 +4,13 @@ use super::Transpilable;
|
|||||||
|
|
||||||
impl Transpilable for FunctionCall {
|
impl Transpilable for FunctionCall {
|
||||||
fn transpile(&self) -> String {
|
fn transpile(&self) -> String {
|
||||||
let parameters = &self.arguments.arguments.iter().map(|expr| expr.transpile()).collect::<Vec<_>>().join(", ");
|
let parameters = &self
|
||||||
|
.arguments
|
||||||
|
.arguments
|
||||||
|
.iter()
|
||||||
|
.map(|expr| expr.transpile())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", ");
|
||||||
|
|
||||||
format!("{}({})", self.function.transpile(), parameters)
|
format!("{}({})", self.function.transpile(), parameters)
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,8 @@ mod file;
|
|||||||
mod syntax;
|
mod syntax;
|
||||||
// Module to handle syntactic analysis
|
// Module to handle syntactic analysis
|
||||||
mod lexic;
|
mod lexic;
|
||||||
|
// Module to handle semantic analysis
|
||||||
|
mod semantic;
|
||||||
// Transforms an AST to JS
|
// Transforms an AST to JS
|
||||||
mod codegen;
|
mod codegen;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
55
src/semantic/mod.rs
Normal file
55
src/semantic/mod.rs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
mod symbol_table;
|
||||||
|
|
||||||
|
use symbol_table::{SymbolEntry, SymbolTable};
|
||||||
|
|
||||||
|
// What to do?
|
||||||
|
// 1. Create a mutable symbol table
|
||||||
|
// 2. Walk the AST
|
||||||
|
// 3. Add the symbols declared to the symbol table, annotating them with their type
|
||||||
|
// 4. Check if the symbols used are declared
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::{borrow::BorrowMut, rc::Rc};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_1() {
|
||||||
|
let mut global_scope = SymbolTable::new();
|
||||||
|
let main_function = SymbolEntry::new_function(vec![], String::from("Unit"));
|
||||||
|
|
||||||
|
global_scope.insert("main".into(), main_function);
|
||||||
|
|
||||||
|
assert!(global_scope.test(&"main".into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_2() {
|
||||||
|
let global_scope = SymbolTable::new();
|
||||||
|
|
||||||
|
let main_function = SymbolEntry::new_function(vec![], String::from("Unit"));
|
||||||
|
global_scope.insert("main".into(), main_function);
|
||||||
|
global_scope.insert("db_url".into(), SymbolEntry::Variable("String".into()));
|
||||||
|
|
||||||
|
let add_function =
|
||||||
|
SymbolEntry::new_function(vec!["Int".into(), "Int".into()], "Int".into());
|
||||||
|
|
||||||
|
global_scope.insert("add".into(), add_function);
|
||||||
|
|
||||||
|
let main_function_scope = SymbolTable::new_from_parent(&global_scope);
|
||||||
|
main_function_scope.insert("message".into(), SymbolEntry::Variable("String".into()));
|
||||||
|
|
||||||
|
assert!(main_function_scope.test(&"message".into()));
|
||||||
|
assert!(main_function_scope.test(&"db_url".into()));
|
||||||
|
assert_eq!(main_function_scope.test(&"non_existant".into()), false);
|
||||||
|
|
||||||
|
let mut add_function_scope = SymbolTable::new_from_parent(&global_scope);
|
||||||
|
|
||||||
|
add_function_scope.insert("a".into(), SymbolEntry::Variable("Int".into()));
|
||||||
|
add_function_scope.insert("b".into(), SymbolEntry::Variable("Int".into()));
|
||||||
|
|
||||||
|
assert!(add_function_scope.test(&"a".into()));
|
||||||
|
global_scope.insert("test".into(), SymbolEntry::Variable("Int".into()));
|
||||||
|
}
|
||||||
|
}
|
96
src/semantic/symbol_table.rs
Normal file
96
src/semantic/symbol_table.rs
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
||||||
|
|
||||||
|
/// Public interface for the symbol table
|
||||||
|
pub struct SymbolTable {
|
||||||
|
node: Rc<RefCell<SymbolTableNode>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// struct for a symbol table
|
||||||
|
struct SymbolTableNode {
|
||||||
|
// the parent scope
|
||||||
|
parent: Option<Rc<RefCell<SymbolTableNode>>>,
|
||||||
|
// the current scope
|
||||||
|
scope: HashMap<String, SymbolEntry>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum SymbolEntry {
|
||||||
|
// Just a Datatype
|
||||||
|
Variable(String),
|
||||||
|
// Contains: parameters, return type
|
||||||
|
Function(Vec<String>, String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SymbolTable {
|
||||||
|
/// Creates a new, global symbol table
|
||||||
|
pub fn new() -> SymbolTable {
|
||||||
|
SymbolTable {
|
||||||
|
node: Rc::new(RefCell::new(SymbolTableNode::new())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_from_parent(parent: &SymbolTable) -> SymbolTable {
|
||||||
|
let new_table = SymbolTableNode::new_from_parent(&parent.node);
|
||||||
|
|
||||||
|
SymbolTable {
|
||||||
|
node: Rc::new(RefCell::new(new_table)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inserts a new symbol into the current table scope
|
||||||
|
pub fn insert(&self, key: String, value: SymbolEntry) {
|
||||||
|
self.node.borrow_mut().insert(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tests if a symbol is declared in the current or parent scopes
|
||||||
|
pub fn test(&self, key: &String) -> bool {
|
||||||
|
self.node.borrow_mut().test(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SymbolTableNode {
|
||||||
|
/// Creates a new, global symbol table
|
||||||
|
pub fn new<'a>() -> SymbolTableNode {
|
||||||
|
SymbolTableNode {
|
||||||
|
parent: None,
|
||||||
|
scope: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new symbol table with a parent
|
||||||
|
pub fn new_from_parent<'a>(parent: &Rc<RefCell<SymbolTableNode>>) -> SymbolTableNode {
|
||||||
|
SymbolTableNode {
|
||||||
|
parent: Some(Rc::clone(&parent)),
|
||||||
|
scope: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inserts a new symbol into the current scope
|
||||||
|
pub fn insert(&mut self, key: String, value: SymbolEntry) {
|
||||||
|
self.scope.insert(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tests if a symbol is declared in the current or parent scopes
|
||||||
|
pub fn test(&mut self, key: &String) -> bool {
|
||||||
|
if self.scope.contains_key(key) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
match &self.parent {
|
||||||
|
Some(parent) => {
|
||||||
|
let mut parent = parent.as_ref().borrow_mut();
|
||||||
|
parent.test(key)
|
||||||
|
}
|
||||||
|
None => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SymbolEntry {
|
||||||
|
pub fn new_variable(datatype: String) -> SymbolEntry {
|
||||||
|
SymbolEntry::Variable(datatype)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_function(parameters: Vec<String>, return_type: String) -> SymbolEntry {
|
||||||
|
SymbolEntry::Function(parameters, return_type)
|
||||||
|
}
|
||||||
|
}
|
@ -10,4 +10,3 @@ pub struct FunctionCall {
|
|||||||
pub struct ArgumentsList {
|
pub struct ArgumentsList {
|
||||||
pub arguments: Vec<Expression>,
|
pub arguments: Vec<Expression>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ mod tests {
|
|||||||
ParseResult::Ok(p, _) => p,
|
ParseResult::Ok(p, _) => p,
|
||||||
_ => {
|
_ => {
|
||||||
panic!("Expected a block, got: {:?}\n\n{:?}", block, tokens)
|
panic!("Expected a block, got: {:?}\n\n{:?}", block, tokens)
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(block.statements.len(), 1);
|
assert_eq!(block.statements.len(), 1);
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
lexic::token::Token,
|
lexic::token::Token,
|
||||||
syntax::{
|
syntax::{
|
||||||
ast::{
|
ast::{functions::FunctionCall, Expression},
|
||||||
functions::{ArgumentsList, FunctionCall},
|
|
||||||
Expression,
|
|
||||||
},
|
|
||||||
functions::arguments_list,
|
functions::arguments_list,
|
||||||
ParseResult,
|
ParseResult,
|
||||||
},
|
},
|
||||||
|
@ -14,8 +14,5 @@ pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParseResult<Expression, ()>
|
|||||||
return equality::try_parse(tokens, pos);
|
return equality::try_parse(tokens, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {}
|
||||||
|
|
||||||
}
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
|
use super::super::utils::Tokenizer;
|
||||||
use crate::{
|
use crate::{
|
||||||
lexic::token::{Token, TokenType},
|
lexic::token::{Token, TokenType},
|
||||||
syntax::{ast::Expression, ParseResult},
|
syntax::{ast::Expression, ParseResult},
|
||||||
};
|
};
|
||||||
use super::super::utils::Tokenizer;
|
|
||||||
|
|
||||||
/// This grammar may not be up to date. Refer to the spec for the latest grammar.
|
/// This grammar may not be up to date. Refer to the spec for the latest grammar.
|
||||||
///
|
///
|
||||||
@ -12,12 +12,14 @@ use super::super::utils::Tokenizer;
|
|||||||
pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParseResult<Expression, ()> {
|
pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParseResult<Expression, ()> {
|
||||||
match tokens.get_significant(pos) {
|
match tokens.get_significant(pos) {
|
||||||
Some((token, token_pos)) => match token.token_type {
|
Some((token, token_pos)) => match token.token_type {
|
||||||
TokenType::Number => {
|
TokenType::Number => ParseResult::Ok(
|
||||||
ParseResult::Ok(Expression::Number(Box::new(token.value.clone())), token_pos + 1)
|
Expression::Number(Box::new(token.value.clone())),
|
||||||
}
|
token_pos + 1,
|
||||||
TokenType::String => {
|
),
|
||||||
ParseResult::Ok(Expression::String(Box::new(token.value.clone())), token_pos + 1)
|
TokenType::String => ParseResult::Ok(
|
||||||
}
|
Expression::String(Box::new(token.value.clone())),
|
||||||
|
token_pos + 1,
|
||||||
|
),
|
||||||
TokenType::Identifier if token.value == "true" || token.value == "false" => {
|
TokenType::Identifier if token.value == "true" || token.value == "false" => {
|
||||||
ParseResult::Ok(Expression::Boolean(token.value == "true"), token_pos + 1)
|
ParseResult::Ok(Expression::Boolean(token.value == "true"), token_pos + 1)
|
||||||
}
|
}
|
||||||
|
@ -79,8 +79,6 @@ pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParseResult<Argument
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::net;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::lexic::get_tokens;
|
use crate::lexic::get_tokens;
|
||||||
|
|
||||||
@ -123,7 +121,6 @@ mod tests {
|
|||||||
assert_eq!(list.arguments.len(), 0);
|
assert_eq!(list.arguments.len(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_parse_one_argument() {
|
fn should_parse_one_argument() {
|
||||||
let tokens = get_tokens(&String::from("(0)")).unwrap();
|
let tokens = get_tokens(&String::from("(0)")).unwrap();
|
||||||
@ -142,7 +139,7 @@ mod tests {
|
|||||||
panic!("Expected a number")
|
panic!("Expected a number")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_parse_one_argument_with_trailing_comma() {
|
fn should_parse_one_argument_with_trailing_comma() {
|
||||||
let tokens = get_tokens(&String::from("(0, )")).unwrap();
|
let tokens = get_tokens(&String::from("(0, )")).unwrap();
|
||||||
|
@ -4,12 +4,14 @@ use crate::{
|
|||||||
utils::Result3,
|
utils::Result3,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::super::{
|
use super::{
|
||||||
ast::FunctionDeclaration,
|
super::{
|
||||||
block::parse_block,
|
ast::FunctionDeclaration,
|
||||||
|
block::parse_block,
|
||||||
|
utils::{parse_token_type, try_token_type},
|
||||||
|
ParseResult,
|
||||||
|
},
|
||||||
params_list::parse_params_list,
|
params_list::parse_params_list,
|
||||||
utils::{parse_token_type, try_token_type},
|
|
||||||
ParseResult,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParseResult<FunctionDeclaration, ()> {
|
pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParseResult<FunctionDeclaration, ()> {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
pub mod arguments_list;
|
pub mod arguments_list;
|
||||||
|
|
||||||
pub mod function_declaration;
|
pub mod function_declaration;
|
||||||
|
pub mod params_list;
|
||||||
|
@ -4,7 +4,7 @@ use crate::{
|
|||||||
syntax::utils::parse_token_type,
|
syntax::utils::parse_token_type,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::super::{
|
||||||
ast::{Parameter, ParamsList},
|
ast::{Parameter, ParamsList},
|
||||||
utils, ParseResult,
|
utils, ParseResult,
|
||||||
};
|
};
|
@ -4,7 +4,6 @@ mod binding;
|
|||||||
mod block;
|
mod block;
|
||||||
mod expression;
|
mod expression;
|
||||||
mod functions;
|
mod functions;
|
||||||
mod params_list;
|
|
||||||
mod statement;
|
mod statement;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user