diff --git a/README.md b/README.md index e4d3429..03dbee6 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,34 @@ -# Yard -Yard is an funny programming language compiler and interpreter written in pure Rust -It *will* contain features such as: -1. a COMEFROM keyword (inverse goto) -2. a ```don't``` code block which never executes -3. ```rand(x)``` returns x, always. -4. no if. only `unless`, an inverted version of if. Meaning a block get executed if the is false and doesn't if it is true \ No newline at end of file +## Yard + +Yard is an funny programming language compiler and interpreter written in pure Rust. +Its a functional language, having no concept of classes or inheritance. Types are static and must be known at compile time. +It contains features such as: + +1. a COMEFROM keyword (inverse goto) +2. a `don't` code block which never executes +3. `rand(x)` returns x, always. +4. no if. only `unless`, an inverted version of if. Meaning a block gets executed if the expression is false +5. three spaces start a single line comment +6. many ways of making comments: `// comment`, `:REM: comment`, `# comment`, `-- comment --` cuz we can + +### Keywords + +
SyntaxDescriptionExample
UnlessInverted if. Only executes the following block if the expression is false
unless 3 > 4 {
+    // code block
+}
LoopWhile(true)-Loop. Loops forever until either yield, ret, break are used.
Loop take no boolean expression.
loop {
+    // code block
+}
contShort form for “continue”. Jumps to the next iteration of a loop 
breakExits a loop immediately 
yieldReturns from a function with an argument as return value
foo() = int {
+    yield 4
+}
retShort form for “return”. This keyword takes no argument and returns from a function. NOTE: if ret is used in a function with a return type, an error occurs. 
+ +### Example code + +```plaintext +foo(bar: int, abc:int) = int { + bar + abc +} + +main { + foo(4, 5) +} +``` \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index e5c75b4..202c661 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,18 +18,24 @@ fn main() { let source = r" -#pi = 3.1415926535 +# this is pi +pi = rat 3.1415926535 foo(x :int, y: rat) = bool { - + yield maybe } - + comment main { + -- comment -- unless 3 > 4 { - a = 4 + 5; - b = 0.3 + 7; + a = 4 - 5; // also comment + b:rat = 0.3 + 7 + + t = foo(a, b) :REM: comment 3 + } + + don't { - foo(a, b) } } "; diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 39d4eb7..997c6dd 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -76,6 +76,11 @@ fn discover_functions<'a>(tokens: &mut VecDeque>, source: &str) Token::Type(typ, dbginf) => { if declr.results { + if declr.result_typ.is_some() { + dbginf.print(MessageType::Error, "Function must return either nothing or a single type", source); + panic!(); + } + declr.result_typ = Some(*typ); continue; } else { @@ -202,7 +207,7 @@ fn discover_functions<'a>(tokens: &mut VecDeque>, source: &str) // if we have anything left it might be an error match &top { - Token::LineBreak(_) => (), // valid whitespace + Token::LineBreak(_) | Token::Terminator(_) => (), // valid whitespace _ => { top.print(MessageType::Error, "unresolvable token", source); panic!() @@ -233,7 +238,7 @@ fn discover_exprs<'a>(functions: &mut Vec>, _: &Vec>, source: while let Some(top) = func.raw.as_mut().unwrap().pop_front() { match &top { - Token::LineBreak(dbginf) => if !expr.is_empty() { + Token::LineBreak(dbginf) | Token::Terminator(dbginf) => if !expr.is_empty() { blocks.last_mut().unwrap_or_else(|| { dbginf.print(MessageType::Error, "curly brace missmatch", source); panic!() @@ -369,8 +374,9 @@ fn call_func(name: &str, declrs: &Vec, _: &mut Scope, operands: &mut Vec< } } - // TODO: push result type - // operands.push(); + if let Some(typ) = declr.result_typ { + operands.push(typ); + } break } @@ -537,7 +543,7 @@ pub fn parse<'a>(tokens: &mut VecDeque>, source: &'a str) -> Ve parse_exprs(&mut funcs, &declrs, source); for (x, f) in funcs.iter().enumerate() { - println!("{:?}{:?}", declrs[x], f); + println!("{:#?}{:#?}", declrs[x], f); } funcs diff --git a/src/token/mod.rs b/src/token/mod.rs index 4d97ddb..54c27f2 100644 --- a/src/token/mod.rs +++ b/src/token/mod.rs @@ -186,6 +186,8 @@ pub enum Keyword { Loop, Break, Continue, + Return, + Yield } impl Keyword { @@ -195,7 +197,9 @@ impl Keyword { "while" => Keyword::While, "loop" => Keyword::Loop, "break" => Keyword::Break, - "continue" => Keyword::Continue, + "cont" => Keyword::Continue, + "ret" => Keyword::Return, + "yield" => Keyword::Yield, _ => panic!("Text not a known keyword {text}") } } @@ -267,9 +271,9 @@ impl Prim { #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub struct DebugInfo { - /// index in source string where the token begins + /// index in source string where the token begins in the current line start: usize, - /// index in source string where the token ends + /// index in source string where the token ends in the current line end: usize, /// line number where the line in which the token is begins line: usize @@ -303,10 +307,19 @@ impl DebugInfo { /// Error (message) in line 7: token `code` /// somewhere in here: /// --> `code line` + /// ^^^^ /// ``` pub fn print<'a>(&self, typ: MessageType, msg: &str, source: &'a str) { println!("{} ({}) in line {}: token `{}`", typ.to_colored(), msg.bold().bright_white(), self.line, &source[self.start..self.end].bold()); - println!(" somewhere in here:\n --> `{}`\n", source.lines().nth(self.line).unwrap().trim().bold().bright_white()) + println!(" somewhere in here:\n --> `{}`", source.lines().nth(self.line).unwrap().trim().bold().bright_white()); + + for _ in 0..self.start + 6 { + print!(" "); + } + for _ in self.start..self.end { + print!("^"); + } + println!("\n"); } } @@ -333,7 +346,9 @@ pub enum Token<'a> { Bool(bool, DebugInfo), /// Keywords like ```if```,```break```,```while``` Keyword(Keyword, DebugInfo), - Type(Prim, DebugInfo) + Type(Prim, DebugInfo), + /// Semicolon + Terminator(DebugInfo) } impl<'a> Token<'a> { @@ -353,11 +368,12 @@ impl<'a> Token<'a> { Token::Decl(_, _, dbginf) => dbginf.print(error, arg, source), Token::Bool(_, dbginf) => dbginf.print(error, arg, source), Token::Keyword(_, dbginf) => dbginf.print(error, arg, source), + Token::Terminator(dbginf) => dbginf.print(error, arg, source), } } } -const TOKEN_REGEX_SRC: &'static str = r"(#.*)|(unless|while|loop|break|continue)|(int|rat|bool)|(true|false|ye|no|maybe)|([A-Za-z_]+)\s*(?::\s*([a-zA-Z0-9]+))?\s*=|([A-Za-z_]+)\s*(?::\s*([a-zA-Z0-9]+))|([A-Za-z_]+)|(\d*\.?\d+)|(!=|==|<=|<=|[&|+\-*/<>=])|([(){}])|(\n|;)"; +const TOKEN_REGEX_SRC: &'static str = r"(#.*|--.*--|//.*|:REM:.*| .*)|(unless|while|loop|break|cont|ret|yield)|(int|rat|bool)|(true|false|ye|no|maybe)|([A-Za-z_]+)\s*(?::\s*([a-zA-Z0-9]+))?\s*=|([A-Za-z_]+)\s*(?::\s*([a-zA-Z0-9]+))|([A-Za-z_]+)|(\d*\.?\d+)|(!=|==|<=|<=|[&|+\-*/<>=])|([(){}])|(\n)|(;)"; lazy_static::lazy_static! { static ref TOKEN_REGEX: regex::Regex = regex::Regex::new(TOKEN_REGEX_SRC).unwrap(); @@ -368,6 +384,7 @@ pub fn tokenize<'a>(source: &'a str) -> VecDeque> { let mut tokens = VecDeque::new(); let mut line_count = 0; + let mut line_start = 0; for cap in TOKEN_REGEX.captures_iter(source) { let mut enumerator = cap.iter().enumerate(); @@ -388,8 +405,8 @@ pub fn tokenize<'a>(source: &'a str) -> VecDeque> { // if we have a match, save it as token if let Some(mat) = group { let debug_info = DebugInfo { - start: mat.start(), - end: mat.end(), + start: mat.start() - line_start, + end: mat.end() - line_start, line: line_count }; @@ -417,8 +434,10 @@ pub fn tokenize<'a>(source: &'a str) -> VecDeque> { 12 => Token::Delemiter(mat.as_str().chars().nth(0).unwrap(), debug_info), 13 => { line_count += 1; + line_start = mat.start(); Token::LineBreak(debug_info) }, + 14 => Token::Terminator(debug_info), _ => { debug_info.print(MessageType::Error, "Unable to identify sequence as token", source);