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
+
+
Syntax | Description | Example |
Unless | Inverted if. Only executes the following block if the expression is false | unless 3 > 4 {
+ // code block
+}
|
Loop | While(true)-Loop. Loops forever until either yield, ret, break are used. Loop take no boolean expression. | loop {
+ // code block
+}
|
cont | Short form for “continue”. Jumps to the next iteration of a loop | |
break | Exits a loop immediately | |
yield | Returns from a function with an argument as return value | foo() = int {
+ yield 4
+}
|
ret | Short 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);