first part of static type system

This commit is contained in:
Sven Vogel 2022-10-11 19:54:45 +02:00
parent 85e0c299af
commit 843f8dbcf0
4 changed files with 96 additions and 50 deletions

View File

@ -15,7 +15,7 @@ fn main() {
r"
pi = 3.1415926535
sin(x) = {
sin(x: f4) = {
x
}

View File

@ -1,12 +1,13 @@
use std::collections::{VecDeque};
use crate::token::{Token};
use crate::Prim;
#[derive(Eq, Debug)]
#[derive(Debug)]
pub struct Func<'a> {
/// name of this function
pub name: Option<&'a str>,
/// parameter names
pub args: Option<Vec<&'a str>>,
pub args: Option<Vec<(&'a str, Prim)>>,
/// raw tokens
pub raw: Option<VecDeque<Token<'a>>>,
/// if the function returns a single value
@ -43,11 +44,11 @@ impl<'a> std::fmt::Display for Func<'a> {
f.write_str("(")?;
for (x, arg) in args.iter().enumerate() {
if x == 0 {
f.write_fmt(format_args!("{}", arg))?;
f.write_fmt(format_args!("{}", arg.0))?;
continue;
}
f.write_fmt(format_args!(", {}", arg))?;
f.write_fmt(format_args!(", {}", arg.0))?;
}
f.write_str(")")?;
}
@ -62,7 +63,7 @@ impl<'a> std::fmt::Display for Func<'a> {
pub type Block<'a> = VecDeque<Expr<'a>>;
#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum Expr<'a> {
/// group of more expressions
Block(Block<'a>),
@ -72,9 +73,9 @@ pub enum Expr<'a> {
pub struct Scope<'a> {
pub funcs: Vec<&'a str>,
pub args: Option<&'a Vec<&'a str>>,
pub args: Option<&'a Vec<(&'a str, Prim)>>,
/// stack of scoped block variables
pub vars: Vec<Vec<String>>,
pub vars: Vec<Vec<(String, Option<Prim>)>>,
}
impl<'a> Scope<'a> {
@ -86,8 +87,8 @@ impl<'a> Scope<'a> {
self.vars.pop();
}
pub fn decl_var(&mut self, name: String) {
self.vars.last_mut().unwrap().push(name)
pub fn decl_var(&mut self, name: String, typ: Option<Prim>) {
self.vars.last_mut().unwrap().push((name, typ))
}
pub fn is_func(&self, name: &'a str) -> bool {
@ -96,21 +97,27 @@ impl<'a> Scope<'a> {
pub fn is_arg(&self, name: &'a str) -> bool {
if let Some(args) = self.args {
return args.contains(&name);
for arg in args.iter() {
if arg.0 == name {
return true;
}
}
}
false
}
pub fn is_var(&self, name: &'a str) -> bool {
pub fn is_var(&self, name: &'a str) -> Option<Prim> {
// create an owned version of the string
let owned = &name.to_owned();
// search
for vars in self.vars.iter() {
if vars.contains(owned) {
return true;
for var in vars.iter() {
if &var.0 == owned {
return var.1;
}
}
}
false
None
}
}

View File

@ -1,6 +1,6 @@
use core::panic;
use core::{panic};
use std::{collections::{VecDeque}, vec};
use crate::token::{Token, Operator, Assoc};
use crate::token::{Token, Operator, Assoc, Prim};
pub mod data;
@ -86,7 +86,7 @@ fn discover_functions<'a>(tokens: &mut VecDeque<crate::Token<'a>>) -> Vec<Func<'
_ => ()
}
Token::Assign(name) => {
Token::Assign(name, _) => {
if func.results {
panic!("double function assignment not permitted")
}
@ -151,8 +151,9 @@ fn discover_functions<'a>(tokens: &mut VecDeque<crate::Token<'a>>) -> Vec<Func<'
}
match &top {
Token::Word(text) => args.push(text),
_ => panic!("Argument is not a word {:?}", &top)
Token::Decl(name, typ) => args.push((name, *typ)),
Token::Word(name) => panic!("Missing type declaration {name}"),
_ => panic!("Argument is not a declaration {:?}", &top)
}
continue;
}
@ -221,6 +222,14 @@ fn discover_exprs<'a>(functions: &mut Vec<Func<'a>>) {
fn parse_term<'a>(term: &mut VecDeque<Token<'a>>, scope: &mut Scope) {
let mut op_stack = vec![];
let mut output = VecDeque::with_capacity(term.len());
let mut value_stack = vec![];
/*
Token::Number(text) => value_stack.push(CompileTimeType::UntypedNum(text)),
Token::Bool(_) => value_stack.push(CompileTimeType::Prim(Prim::Bool)),
*/
'outer:
while let Some(token) = term.pop_front() {
@ -232,15 +241,18 @@ fn parse_term<'a>(term: &mut VecDeque<Token<'a>>, scope: &mut Scope) {
} else if scope.is_arg(text) {
output.push_back(Token::Arg(text));
continue;
} else if scope.is_var(text) {
} else if scope.is_var(text).is_some() {
output.push_back(Token::Var(text));
continue;
}
panic!("Unknwon word: {text}")
}
Token::Number(_) => output.push_back(token),
Token::Assign(text) => {
scope.decl_var((*text).to_owned());
Token::Number(_) => {
output.push_back(token);
value_stack.push(CompileTimeType::UntypedNum)
},
Token::Assign(text, typ) => {
scope.decl_var((*text).to_owned(), typ.to_owned());
op_stack.push(token);
},
Token::Keyword(_) => op_stack.push(token),
@ -303,6 +315,11 @@ fn parse_term<'a>(term: &mut VecDeque<Token<'a>>, scope: &mut Scope) {
term.append(&mut output);
}
enum CompileTimeType {
Prim(Prim),
UntypedNum,
}
fn parse_block(block: &mut Block, scope: &mut Scope) {
scope.alloc_scope();
for expr in block.iter_mut() {

View File

@ -57,13 +57,7 @@ impl Operator {
Operator::Lt => 2,
Operator::Gt => 2,
Operator::LtEq => 2,
Operator::GtEq => 2, Operator::Eq => 2,
Operator::Lt => 2,
Operator::Gt => 2,
Operator::LtEq => 2,
Operator::GtEq => 2,
Operator::NotEq => 2,
Operator::NotEq => 2,
Operator::Or => 0,
Operator::Xor => 0,
@ -108,15 +102,25 @@ impl Keyword {
}
}
pub struct SourceString<'a> {
pub string: &'a str,
/// line in which the source string is to be found
pub line: usize,
/// index in source where the token starts
pub start: usize
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum Prim {
Int,
Real,
Bool,
}
#[derive(Debug, Hash, PartialEq, Eq, Copy, Clone)]
impl Prim {
fn from<'a>(text: &'a str) -> Prim {
return match text {
"i4" => Prim::Int,
"f4" => Prim::Real,
"bool" => Prim::Bool,
_ => panic!("Unknown type declaration: {text}")
}
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
/// A token represents a basic building block for source code.
/// They give a meaning to patterns of chars allowing to interpret them.
pub enum Token<'a> {
@ -129,13 +133,13 @@ pub enum Token<'a> {
Func(&'a str),
Var(&'a str),
Arg(&'a str),
Assign(&'a str),
Assign(&'a str, Option<Prim>),
Decl(&'a str, Prim),
Bool(bool),
Keyword(Keyword),
TypeDecl(&'a str)
}
const TOKEN_REGEX_SRC: &'static str = r"(#.*)|(if|while|loop|break|continue)|(true|false|yes|no|maybe)|([A-Za-z_]+)\s*(?::\s*(i4|f4|bool))?\s*=|([A-Za-z_]+)|(\d*\.?\d+)|(!=|==|<=|<=|[&|+\-*/<>])|([(){}])|(\n)";
const TOKEN_REGEX_SRC: &'static str = r"(#.*)|(if|while|loop|break|continue)|(true|false|yes|no|maybe)|([A-Za-z_]+)\s*(?::\s*([a-zA-Z0-9]+))|([A-Za-z_]+)\s*(?::\s*([a-zA-Z0-9]+))?\s*=|([A-Za-z_]+)|(\d*\.?\d+)|(!=|==|<=|<=|[&|+\-*/<>])|([(){}])|(\n+)";
lazy_static::lazy_static! {
static ref TOKEN_REGEX: regex::Regex = regex::Regex::new(TOKEN_REGEX_SRC).unwrap();
@ -146,7 +150,14 @@ pub fn tokenize<'a>(source: &'a str) -> VecDeque<Token<'a>> {
let mut tokens = VecDeque::new();
for cap in TOKEN_REGEX.captures_iter(source) {
for (i, group) in cap.iter().enumerate() {
let mut enumerator = cap.iter().enumerate();
loop {
let next = enumerator.next();
if next.is_none() {
break
}
let (i, group) = next.unwrap();
// ignore first group as its the entire match,
// as well as the 1st group (= comments)
@ -155,18 +166,29 @@ pub fn tokenize<'a>(source: &'a str) -> VecDeque<Token<'a>> {
}
// if we have a match, save it as token
if let Some(mat) = group {
tokens.push_back(match i {
if let Some(mat) = group {
tokens.push_back(match i {
2 => Token::Keyword(Keyword::parse(mat.as_str())),
3 => Token::Bool(parse_bool(mat.as_str())),
4 => Token::Assign(mat.as_str()),
5 => Token::Word(mat.as_str()),
6 => Token::Number(mat.as_str()),
7 => Token::Operator(Operator::parse(mat.as_str())),
8 => Token::Delemiter(mat.as_str().chars().nth(0).unwrap()),
9 => Token::LineBreak,
4 => {
let var_type = Prim::from(enumerator.next().unwrap().1.unwrap().as_str());
Token::Decl(mat.as_str(), var_type)
},
6 => {
let var_type = if let Some(mat) = enumerator.next().unwrap().1 {
Some(Prim::from(mat.as_str()))
} else {
None
};
Token::Assign(mat.as_str(), var_type)
},
8 => Token::Word(mat.as_str()),
9 => Token::Number(mat.as_str()),
10 => Token::Operator(Operator::parse(mat.as_str())),
11 => Token::Delemiter(mat.as_str().chars().nth(0).unwrap()),
12 => Token::LineBreak,
_ => panic!("Unknown match to tokenize: {}", mat.as_str())
_ => panic!("Unknown match to tokenize ({i}): {}", mat.as_str())
});
break;
}