aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--LICENSE21
-rw-r--r--progs/tests/simple.sy32
-rw-r--r--src/compiler.rs348
-rw-r--r--src/error.rs42
-rw-r--r--src/lib.rs73
-rw-r--r--src/main.rs2
-rw-r--r--src/tokenizer.rs5
-rw-r--r--sylt_macro/src/lib.rs78
8 files changed, 431 insertions, 170 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..be43f6d
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 Edvard Thörnros & Gustav Sörnäs
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/progs/tests/simple.sy b/progs/tests/simple.sy
index 0f69ae2..84bc86d 100644
--- a/progs/tests/simple.sy
+++ b/progs/tests/simple.sy
@@ -1,11 +1,23 @@
-a := 0
-{
- b := 99999
- {
- a := 99999
- a
- }
- b
- a -= 1
+//
+// import A
+
+//
+f :: fn {
+ g!
+ print q
}
-a <=> -1
+
+//
+q :: 1
+
+//
+a := 1
+
+qq :: fn {
+ g!
+ print q
+}
+
+
+// Steg 1: Hitta sektioner
+// Dela sektioner, compilera felera sektioner efter varandra
diff --git a/src/compiler.rs b/src/compiler.rs
index ec3a52c..680d1f1 100644
--- a/src/compiler.rs
+++ b/src/compiler.rs
@@ -1,11 +1,11 @@
-use std::{borrow::Cow, path::{Path, PathBuf}};
+use std::{path::{Path, PathBuf}};
use std::cell::RefCell;
use std::collections::{HashMap, hash_map::Entry};
use std::rc::Rc;
use crate::{Blob, Block, Op, Prog, RustFunction, Type, Value};
use crate::error::{Error, ErrorKind};
-use crate::tokenizer::{Token, TokenStream};
+use crate::tokenizer::{Token, PlacedToken, TokenStream};
macro_rules! nextable_enum {
( $name:ident { $( $thing:ident ),* $( , )? } ) => {
@@ -46,7 +46,7 @@ macro_rules! parse_branch {
($compiler:expr, $block:expr, [ $( $call:expr ),* ]) => {
{
let block_length = $block.ops.len();
- let token_length = $compiler.curr;
+ let token_length = $compiler.current_token;
let num_errors = $compiler.errors.len();
let mut stored_errors = Vec::new();
@@ -63,7 +63,7 @@ macro_rules! parse_branch {
return true;
}
$compiler.panic = false;
- $compiler.curr = token_length;
+ $compiler.current_token = token_length;
let thrown_errors = $compiler.errors.len() - num_errors - 1;
stored_errors.extend($compiler.errors.split_off(thrown_errors));
$block.ops.truncate(block_length);
@@ -82,7 +82,7 @@ macro_rules! parse_branch {
($compiler:expr, $block:expr, $call:expr) => {
{
let block_length = $block.ops.len();
- let token_length = $compiler.curr;
+ let token_length = $compiler.current_token;
let num_errors = $compiler.errors.len();
let mut stored_errors = Vec::new();
// Closures for early return on success.
@@ -97,7 +97,7 @@ macro_rules! parse_branch {
return true;
}
$compiler.panic = false;
- $compiler.curr = token_length;
+ $compiler.current_token = token_length;
let thrown_errors = $compiler.errors.len() - num_errors - 1;
stored_errors.extend($compiler.errors.split_off(thrown_errors));
$block.ops.truncate(block_length);
@@ -107,6 +107,77 @@ macro_rules! parse_branch {
};
}
+macro_rules! push_frame {
+ ($compiler:expr, $block:expr, $code:tt) => {
+ {
+ $compiler.current_context_mut().push(Frame::new());
+
+ // Return value stored as a variable
+ let var = Variable::new("", true, Type::Unknown);
+ $compiler.define(var).unwrap();
+
+ $code
+
+ let frame = $compiler.current_context_mut().pop().unwrap();
+ // 0-th slot is the function itself.
+ for var in frame.stack.iter().skip(1) {
+ if !(var.read || var.upvalue) {
+ let e = ErrorKind::SyntaxError(
+ var.line,
+ Token::Identifier(var.name.clone()
+ ));
+ $compiler.error_on_line(
+ e,
+ var.line,
+ Some(format!("Unused value '{}'.", var.name))
+ );
+ }
+ $compiler.panic = false;
+ }
+ // The 0th slot is the return value, which is passed out
+ // from functions, and should not be popped.
+ 0
+ }
+ };
+}
+
+macro_rules! push_scope {
+ ($compiler:expr, $block:expr, $code:tt) => {
+ let ss = $compiler.stack().len();
+ $compiler.frame_mut().scope += 1;
+
+ $code;
+
+ $compiler.frame_mut().scope -= 1;
+
+ let mut errors = Vec::new();
+ for var in $compiler.frame().stack.iter().skip(ss).rev() {
+ if !(var.read || var.upvalue) {
+ let e = ErrorKind::SyntaxError(
+ var.line,
+ Token::Identifier(var.name.clone()
+ ));
+ errors.push((
+ e,
+ var.line,
+ format!("Usage of undefined value: '{}'.", var.name),)
+ );
+ }
+ if var.captured {
+ add_op($compiler, $block, Op::PopUpvalue);
+ } else {
+ add_op($compiler, $block, Op::Pop);
+ }
+ }
+
+ for (e, l, m) in errors.iter() {
+ $compiler.error_on_line(e.clone(), *l, Some(m.clone()));
+ $compiler.panic = false;
+ }
+ $compiler.stack_mut().truncate(ss);
+ };
+}
+
nextable_enum!(Prec {
No,
Assert,
@@ -243,6 +314,22 @@ impl Frame {
}
}
+type CompilerContext = Vec<Frame>;
+
+struct Section<'a> {
+ path: PathBuf,
+ tokens: &'a [PlacedToken],
+}
+
+impl<'a> Section<'a> {
+ fn new(path: PathBuf, tokens: &'a [PlacedToken]) -> Self {
+ Section {
+ path,
+ tokens
+ }
+ }
+}
+
#[derive(Debug)]
enum Name {
Slot(usize, usize),
@@ -250,12 +337,12 @@ enum Name {
Space(HashMap<String, usize>),
}
-pub(crate) struct Compiler {
- curr: usize,
- tokens: TokenStream,
- current_file: PathBuf,
+pub(crate) struct Compiler<'a> {
+ current_token: usize,
+ current_section: usize,
+ sections: Vec<Section<'a>>,
- frames: Vec<Frame>,
+ contextes: HashMap<PathBuf, CompilerContext>,
panic: bool,
errors: Vec<Error>,
@@ -272,90 +359,85 @@ pub(crate) struct Compiler {
names: HashMap<String, Name>,
}
-macro_rules! push_frame {
- ($compiler:expr, $block:expr, $code:tt) => {
- {
- $compiler.frames.push(Frame::new());
-
- // Return value stored as a variable
- let var = Variable::new("", true, Type::Unknown);
- $compiler.define(var).unwrap();
-
- $code
-
- let frame = $compiler.frames.pop().unwrap();
- // 0-th slot is the function itself.
- for var in frame.stack.iter().skip(1) {
- if !(var.read || var.upvalue) {
- let e = ErrorKind::SyntaxError(
- var.line,
- Token::Identifier(var.name.clone()
- ));
- $compiler.error_on_line(
- e,
- var.line,
- Some(format!("Unused value '{}'.", var.name))
- );
- }
- $compiler.panic = false;
- }
- // The 0th slot is the return value, which is passed out
- // from functions, and should not be popped.
- 0
- }
- };
+/// Helper function for adding operations to the given block.
+fn add_op(compiler: &Compiler, block: &mut Block, op: Op) -> usize {
+ block.add(op, compiler.line())
}
-macro_rules! push_scope {
- ($compiler:expr, $block:expr, $code:tt) => {
- let ss = $compiler.stack().len();
- $compiler.frame_mut().scope += 1;
+fn split_sections<'a>(file_name: PathBuf, tokens: &'a TokenStream) -> Vec<Section> {
+ let mut sections = Vec::new();
- $code;
+ let mut last = 0;
+ let mut curr = 0;
+ while curr < tokens.len() {
+ if match (tokens.get(curr + 0), tokens.get(curr + 1), tokens.get(curr + 2)) {
+ (Some((Token::LeftBrace, _)), ..)
+ => {
+ let mut blocks = 0;
+ loop {
+ curr += 1;
+ match tokens.get(curr) {
+ Some((Token::LeftBrace, _)) => {
+ blocks += 1;
+ }
- $compiler.frame_mut().scope -= 1;
+ Some((Token::RightBrace, _)) => {
+ curr += 1;
+ blocks -= 1;
+ if blocks <= 0 {
+ break;
+ }
+ }
- let mut errors = Vec::new();
- for var in $compiler.frame().stack.iter().skip(ss).rev() {
- if !(var.read || var.upvalue) {
- let e = ErrorKind::SyntaxError(
- var.line,
- Token::Identifier(var.name.clone()
- ));
- errors.push((
- e,
- var.line,
- format!("Variable is unused: '{}'.", var.name),)
- );
- }
- if var.captured {
- add_op($compiler, $block, Op::PopUpvalue);
- } else {
- add_op($compiler, $block, Op::Pop);
- }
- }
+ None => {
+ break;
+ }
- for (e, l, m) in errors.iter() {
- $compiler.error_on_line(e.clone(), *l, Some(m.clone()));
- $compiler.panic = false;
+ _ => {}
+ }
+ }
+ false
+ },
+
+ (Some((Token::Identifier(_), _)),
+ Some((Token::ColonColon, _)),
+ Some((Token::Fn, _)))
+ => true,
+
+ (Some((Token::Identifier(_), _)),
+ Some((Token::ColonColon, _)),
+ Some(_))
+ => true,
+
+ (Some((Token::Identifier(_), _)),
+ Some((Token::ColonEqual, _)),
+ Some(_))
+ => true,
+
+ _ => false,
+ } {
+ sections.push(Section::new(file_name.clone(), &tokens[last..curr]));
+ last = curr;
}
- $compiler.stack_mut().truncate(ss);
- };
+ curr += 1;
+ }
+ sections.push(Section::new(file_name, &tokens[last..curr]));
+ sections
}
-/// Helper function for adding operations to the given block.
-fn add_op(compiler: &Compiler, block: &mut Block, op: Op) -> usize {
- block.add(op, compiler.line())
-}
+impl<'a> Compiler<'a> {
+ pub(crate) fn new(current_file: &Path, tokens: &'a TokenStream) -> Self {
+ let current_file = current_file.to_path_buf();
+ let sections = split_sections(current_file.clone(), tokens);
-impl Compiler {
- pub(crate) fn new(current_file: &Path, tokens: TokenStream) -> Self {
+ let mut contextes = HashMap::new();
+ contextes.insert(current_file, vec![Frame::new()]);
Self {
- curr: 0,
- tokens,
- current_file: PathBuf::from(current_file),
+ current_token: 0,
+ current_section: 0,
+ sections,
- frames: vec![Frame::new()],
+ contextes,
panic: false,
errors: vec![],
@@ -385,7 +467,8 @@ impl Compiler {
| Value::Bool(_)
| Value::String(_)
| Value::Tuple(_)
- | Value::Nil) {
+ | Value::Nil)
+ {
let entry = self.values.entry(value.clone());
if let Entry::Occupied(entry) = entry {
*entry.get()
@@ -406,14 +489,31 @@ impl Compiler {
self.strings.len() - 1
}
+ fn section(&self) -> &Section {
+ &self.sections[self.current_section]
+ }
+
+ fn current_file(&self) -> &Path {
+ &self.section().path
+ }
+
+ fn current_context(&self) -> &CompilerContext {
+ self.contextes.get(self.current_file()).unwrap()
+ }
+
+ fn current_context_mut(&mut self) -> &mut CompilerContext {
+ let file = self.current_file().to_path_buf();
+ self.contextes.get_mut(&file).unwrap()
+ }
+
fn frame(&self) -> &Frame {
- let last = self.frames.len() - 1;
- &self.frames[last]
+ let last = self.current_context().len() - 1;
+ &self.current_context()[last]
}
fn frame_mut(&mut self) -> &mut Frame {
- let last = self.frames.len() - 1;
- &mut self.frames[last]
+ let last = self.current_context().len() - 1;
+ &mut self.current_context_mut()[last]
}
/// Marks a variable as read. Also marks upvalues.
@@ -424,15 +524,15 @@ impl Compiler {
}
- if if let Some(up) = self.frames[frame_id].upvalues.get(var.slot) {
+ if if let Some(up) = self.current_context()[frame_id].upvalues.get(var.slot) {
up.name == var.name
} else { false } {
- let mut inner_var = self.frames[frame_id].upvalues[var.slot].clone();
+ let mut inner_var = self.current_context()[frame_id].upvalues[var.slot].clone();
inner_var.slot = inner_var.outer_slot;
self.mark_read(frame_id - 1, &inner_var);
- self.frames[frame_id].upvalues[var.slot].read = true;
+ self.current_context_mut()[frame_id].upvalues[var.slot].read = true;
} else {
- self.frames[frame_id].stack[var.slot].read = true;
+ self.current_context_mut()[frame_id].stack[var.slot].read = true;
}
}
@@ -468,22 +568,26 @@ impl Compiler {
self.panic = true;
self.errors.push(Error {
kind,
- file: self.current_file.clone(),
+ file: self.current_file().to_path_buf(),
line,
message,
});
}
+ fn init_section(&mut self, section: usize) {
+ self.current_token = 0;
+ self.current_section = section;
+ }
fn peek(&self) -> Token {
self.peek_at(0)
}
fn peek_at(&self, at: usize) -> Token {
- if self.tokens.len() <= self.curr + at {
+ if self.section().tokens.len() <= self.current_token + at {
crate::tokenizer::Token::EOF
} else {
- self.tokens[self.curr + at].0.clone()
+ self.section().tokens[self.current_token + at].0.clone()
}
}
@@ -494,16 +598,28 @@ impl Compiler {
fn eat(&mut self) -> Token {
let t = self.peek();
- self.curr += 1;
+ self.current_token += 1;
+ match t {
+ Token::GitConflictBegin => {
+ self.current_token -= 1;
+ let start = self.line();
+ self.current_token += 1;
+ while !matches!(self.eat(), Token::GitConflictEnd) {}
+ self.panic = false;
+ self.error_on_line(ErrorKind::GitConflictError(start, self.line()), start, None);
+ self.panic = true;
+ }
+ _ => {}
+ }
t
}
/// The line of the current token.
fn line(&self) -> usize {
- if self.curr < self.tokens.len() {
- self.tokens[self.curr].1
+ if self.section().tokens.len() == 0 {
+ 0xCAFEBABE
} else {
- self.tokens[self.tokens.len() - 1].1
+ self.section().tokens[std::cmp::min(self.current_token, self.section().tokens.len() - 1)].1
}
}
@@ -689,8 +805,8 @@ impl Compiler {
}
}
- fn find_and_capture_variable<'a, I>(name: &str, mut iterator: I) -> Option<Variable>
- where I: Iterator<Item = &'a mut Frame> {
+ fn find_and_capture_variable<'b, I>(name: &str, mut iterator: I) -> Option<Variable>
+ where I: Iterator<Item = &'b mut Frame> {
if let Some(frame) = iterator.next() {
if let Some(res) = frame.find_local(name) {
frame.stack[res.slot].captured = true;
@@ -720,7 +836,7 @@ impl Compiler {
return Some(res);
}
- Self::find_and_capture_variable(name, self.frames.iter_mut().rev())
+ Self::find_and_capture_variable(name, self.current_context_mut().iter_mut().rev())
}
fn find_constant(&mut self, name: &str) -> usize {
@@ -846,15 +962,15 @@ impl Compiler {
self.stack_mut()[top].active = true;
self.stack()[top].name.clone()
} else {
- format!("λ {}@{:03}", self.current_file.display(), self.line())
+ format!("λ {}@{:03}", self.current_file().display(), self.line())
};
let mut args = Vec::new();
let mut return_type = Type::Void;
- let mut function_block = Block::new(&name, &self.current_file);
+ let mut function_block = Block::new(&name, self.current_file());
let block_id = self.blocks.len();
- let temp_block = Block::new(&name, &self.current_file);
+ let temp_block = Block::new(&name, self.current_file());
self.blocks.push(Rc::new(RefCell::new(temp_block)));
let _ret = push_frame!(self, function_block, {
@@ -953,7 +1069,7 @@ impl Compiler {
// Variables
if let Some(var) = self.find_variable(&name) {
- self.mark_read(self.frames.len() - 1, &var);
+ self.mark_read(self.current_context().len() - 1, &var);
if var.upvalue {
add_op(self, block, Op::ReadUpvalue(var.slot));
} else {
@@ -1017,7 +1133,7 @@ impl Compiler {
fn constant_statement(&mut self, name: &str, typ: Type, block: &mut Block) {
// Magical global constants
- if self.frames.len() <= 1 && self.peek() == Token::Fn {
+ if self.current_context().len() <= 1 && self.peek() == Token::Fn {
self.function(block, Some(name));
// Remove the function, since it's a constant and we already
// added it.
@@ -1305,7 +1421,7 @@ impl Compiler {
_ => unreachable!(),
};
if let Some(var) = self.find_variable(&name) {
- self.mark_read(self.frames.len() - 1, &var);
+ self.mark_read(self.current_context().len() - 1, &var);
if var.upvalue {
add_op(self, block, Op::ReadUpvalue(var.slot));
} else {
@@ -1473,7 +1589,7 @@ impl Compiler {
}
- pub(crate) fn compile(&mut self, name: &str, file: &Path, functions: &[(String, RustFunction)]) -> Result<Prog, Vec<Error>> {
+ pub(crate) fn compile(&'a mut self, name: &str, file: &Path, functions: &[(String, RustFunction)]) -> Result<Prog, Vec<Error>> {
self.functions = functions
.to_vec()
.into_iter()
@@ -1484,10 +1600,14 @@ impl Compiler {
let _ = self.define(main);
let mut block = Block::new(name, file);
- while self.peek() != Token::EOF {
- self.statement(&mut block);
- expect!(self, Token::Newline | Token::EOF,
- "Expect newline or EOF after expression.");
+ for section in 0..self.sections.len() {
+ self.init_section(section);
+ while self.peek() != Token::EOF {
+ println!("compiling {} -- statement -- {:?}", section, self.line());
+ self.statement(&mut block);
+ expect!(self, Token::Newline | Token::EOF,
+ "Expect newline or EOF after expression.");
+ }
}
let tmp = self.add_constant(Value::Unknown);
add_op(self, &mut block, Op::Constant(tmp));
@@ -1509,7 +1629,7 @@ impl Compiler {
}
}
- for var in self.frames.pop().unwrap().stack.iter().skip(1) {
+ for var in self.current_context_mut().pop().unwrap().stack.iter().skip(1) {
if !(var.read || var.upvalue) {
let e = ErrorKind::SyntaxError(var.line, Token::Identifier(var.name.clone()));
let m = format!("Unused value '{}'.", var.name);
diff --git a/src/error.rs b/src/error.rs
index c2ad228..9aca985 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -32,6 +32,8 @@ pub enum ErrorKind {
/// (line, token)
SyntaxError(usize, Token),
+ /// (start, end)
+ GitConflictError(usize, usize),
}
#[derive(Debug, Clone)]
@@ -67,9 +69,8 @@ impl fmt::Display for ErrorKind {
write!(f, "Argument types do not match, expected [{:?}] but got [{:?}]",
expected, given)
}
- ErrorKind::IndexOutOfBounds(value, len, slot) => {
- write!(f, "Failed to index for {:?} - length is {} but index is {}",
- value, len, slot)
+ ErrorKind::IndexError(value, slot) => {
+ write!(f, "Cannot index value '{:?}' with type '{:?}'.", value, slot)
}
ErrorKind::ExternTypeMismatch(name, types) => {
write!(f, "Extern function '{}' doesn't accept argument(s) with type(s) {:?}",
@@ -81,27 +82,32 @@ impl fmt::Display for ErrorKind {
.fold(String::new(), |a, v| { format!("{}{:?}, ", a, v) });
write!(f, "Cannot apply {:?} to values {}", op, values)
}
- ErrorKind::AssertFailed => {
- write!(f, "Assertion failed")
+ ErrorKind::UnknownField(obj, field) => {
+ write!(f, "Cannot find field '{}' on {:?}", field, obj)
}
- ErrorKind::SyntaxError(line, token) => {
- write!(f, "Syntax Error on line {} at token {:?}", line, token)
+ ErrorKind::ArgumentCount(expected, given) => {
+ write!(f, "Incorrect argument count, expected {} but got {}.",
+ expected, given)
}
- ErrorKind::Unreachable => {
- write!(f, "Reached unreachable code.")
+ ErrorKind::IndexOutOfBounds(value, len, slot) => {
+ write!(f, "Failed to index for {:?} - length is {} but index is {}",
+ value, len, slot)
+ }
+ ErrorKind::AssertFailed => {
+ write!(f, "Assertion failed")
}
ErrorKind::InvalidProgram => {
write!(f, "{}", "[!!] Invalid program [!!]".bold())
}
- ErrorKind::IndexError(value, slot) => {
- write!(f, "Cannot index value '{:?}' with type '{:?}'.", value, slot)
+ ErrorKind::Unreachable => {
+ write!(f, "Reached unreachable code.")
}
- ErrorKind::UnknownField(obj, field) => {
- write!(f, "Cannot find field '{}' on {:?}", field, obj)
+ ErrorKind::SyntaxError(line, token) => {
+ write!(f, "Syntax Error on line {} at token {:?}", line, token)
}
- ErrorKind::ArgumentCount(expected, given) => {
- write!(f, "Incorrect argument count, expected {} but got {}.",
- expected, given)
+ ErrorKind::GitConflictError(start_line, end_line) => {
+ write!(f, "Git conflict markers found between lines {} and {}",
+ start_line, end_line)
}
}
}
@@ -109,7 +115,7 @@ impl fmt::Display for ErrorKind {
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let prompt = "*****".red();
+ let prompt = " ";
let message = match &self.message {
Some(s) => format!("\n{} {}", prompt, s),
None => String::from(""),
@@ -123,7 +129,7 @@ impl fmt::Display for Error {
String::new()
};
- write!(f, "\n {} {}:{} \n{} {}{}{}\n", "ERR".red(),
+ write!(f, "{} {}:{}\n{} {}{}{}", "ERROR".red(),
self.file.display().blue(), self.line.blue(), prompt, self.kind, message, line)
}
}
diff --git a/src/lib.rs b/src/lib.rs
index 9b938ab..9463c73 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -21,12 +21,13 @@ mod tokenizer;
/// Compiles a file and links the supplied functions as callable external
/// functions. Use this if you want your programs to be able to yield.
-pub fn compile_file(path: &Path,
- print: bool,
- functions: Vec<(String, RustFunction)>
- ) -> Result<vm::VM, Vec<Error>> {
+pub fn compile_file(
+ path: &Path,
+ print: bool,
+ functions: Vec<(String, RustFunction)>
+) -> Result<vm::VM, Vec<Error>> {
let tokens = tokenizer::file_to_tokens(path);
- match compiler::Compiler::new(path, tokens).compile("main", path, &functions) {
+ match compiler::Compiler::new(path, &tokens).compile("main", path, &functions) {
Ok(prog) => {
let mut vm = vm::VM::new();
vm.print_blocks = print;
@@ -54,7 +55,7 @@ pub fn run_string(s: &str, print: bool, functions: Vec<(String, RustFunction)>)
}
fn run(tokens: TokenStream, path: &Path, print: bool, functions: Vec<(String, RustFunction)>) -> Result<(), Vec<Error>> {
- match compiler::Compiler::new(path, tokens).compile("main", path, &functions) {
+ match compiler::Compiler::new(path, &tokens).compile("main", path, &functions) {
Ok(prog) => {
let mut vm = vm::VM::new();
vm.print_blocks = print;
@@ -880,7 +881,8 @@ mod tests {
#[test]
fn $fn() {
crate::tests::panic_after(std::time::Duration::from_millis(500), || {
- match $crate::run_string($prog, true, Vec::new()) {
+ let prog = std::concat!("q :: fn {", $prog, "\n{}\n}\nq()");
+ match $crate::run_string(&prog, true, Vec::new()) {
Ok(()) => {},
Err(errs) => {
for e in errs.iter() {
@@ -897,7 +899,8 @@ mod tests {
#[test]
fn $fn() {
crate::tests::panic_after(std::time::Duration::from_millis(500), || {
- $crate::assert_errs!($crate::run_string($prog, true, Vec::new()), $errs);
+ let prog = std::concat!("q :: fn {", $prog, "\n{}\n}\nq()");
+ $crate::assert_errs!($crate::run_string(&prog, true, Vec::new()), $errs);
})
}
}
@@ -1059,6 +1062,7 @@ a
}
add(1, 1) <=> 2
add(10, 20) <=> 30",
+ /*
calls_inside_calls: "one := fn -> int {
ret 1
}
@@ -1082,6 +1086,7 @@ a
ret inner(a)
}
f(g, 2) <=> 4",
+ */
multiple_returns: "f := fn a: int -> int {
if a == 1 {
ret 2
@@ -1108,6 +1113,7 @@ a
factorial(6) <=> 720
factorial(12) <=> 479001600",
+/*
returning_closures: "
f : fn -> fn -> int = fn -> fn -> int {
x : int = 0
@@ -1130,15 +1136,17 @@ b() <=> 3
a() <=> 4
",
+*/
);
test_multiple!(
blob,
simple: "blob A {}",
+ field: "blob A { a: int }",
+ /*
instantiate: "blob A {}
a := A()
a",
- field: "blob A { a: int }",
field_assign: "blob A { a: int }
a := A()
a.a = 2",
@@ -1156,6 +1164,7 @@ a() <=> 4
a.b = 3
a.a + a.b <=> 5
5 <=> a.a + a.b",
+ */
blob_infer: "
blob A { }
a : A = A()
@@ -1172,7 +1181,7 @@ a
);
test_file!(scoping, "progs/tests/scoping.sy");
- test_file!(for_, "progs/tests/for.sy");
+ // test_file!(for_, "progs/tests/for.sy");
test_multiple!(
op_assign,
@@ -1180,6 +1189,7 @@ a
sub: "a := 2\na -= 1\na <=> 1",
mul: "a := 2\na *= 2\na <=> 4",
div: "a := 2\na /= 2\na <=> 1",
+/*
cluster: "
blob A { a: int }
a := A()
@@ -1192,6 +1202,7 @@ a.a /= 2
a.a <=> 1
a.a -= 1
a.a <=> 0"
+*/
);
test_multiple!(
@@ -1357,6 +1368,7 @@ a
",
+/*
constant_function: "
a()
a :: fn {}
@@ -1381,21 +1393,6 @@ q :: fn -> int {
ret k()
}
",
-
- constant_function_closure: "
-q := 1
-
-f :: fn -> int {
- q += 1
- ret q
-}
-
-f() <=> 2
-f() <=> 3
-f() <=> 4
-f() <=> 5
-",
-
constants_in_inner_functions: "
q : int = 0
@@ -1414,7 +1411,33 @@ q <=> 2
g()
q <=> 3
",
+*/
+ constant_function_closure: "
+q := 1
+
+f :: fn -> int {
+ q += 1
+ ret q
+}
+
+f() <=> 2
+f() <=> 3
+f() <=> 4
+f() <=> 5
+",
+ );
+
+/*
+ test_string!(conflict_markers, "
+<<<<<<< HEAD
+print extern_test(4.0)
+=======
+print extern_test(5.0)
+>>>>>>> 2
+",
+ [ErrorKind::SyntaxError(_, _), ErrorKind::GitConflictError(2, 6)]
);
+*/
}
diff --git a/src/main.rs b/src/main.rs
index bc68d40..28e4e79 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -10,7 +10,7 @@ struct Args {
fn main() {
let args = parse_args();
let file = args.file.unwrap_or_else(|| Path::new("progs/tests/simple.sy").to_owned());
- let errs = match run_file(&file, args.print, vec![(String::from("extern_test"), extern_test)]) {
+ let errs = match run_file(&file, args.print, sylt_macro::link!(extern_test as test)) {
Err(it) => it,
_ => return,
};
diff --git a/src/tokenizer.rs b/src/tokenizer.rs
index b54e194..2c8e5e8 100644
--- a/src/tokenizer.rs
+++ b/src/tokenizer.rs
@@ -127,6 +127,11 @@ pub enum Token {
#[token("\n")]
Newline,
+ #[token("<<<<<<<")]
+ GitConflictBegin,
+ #[token(">>>>>>>")]
+ GitConflictEnd,
+
#[regex(r"//[^\n]*", logos::skip)]
Comment,
diff --git a/sylt_macro/src/lib.rs b/sylt_macro/src/lib.rs
index fc43b5c..3b8b37c 100644
--- a/sylt_macro/src/lib.rs
+++ b/sylt_macro/src/lib.rs
@@ -73,15 +73,89 @@ pub fn extern_function(tokens: TokenStream) -> TokenStream {
#[allow(unused_variables)]
match __values {
#(#typecheck_blocks),*
- _ => Err(sylt::error::ErrorKind::ExternTypeMismatch(stringify!(#function).to_string(), __values.iter().map(|v| sylt::Type::from(v)).collect()))
+ _ => Err(sylt::error::ErrorKind::ExternTypeMismatch(
+ stringify!(#function).to_string(),
+ __values.iter().map(|v| sylt::Type::from(v)).collect()
+ ))
}
} else {
match __values {
#(#eval_blocks),*
- _ => Err(sylt::error::ErrorKind::ExternTypeMismatch(stringify!(#function).to_string(), __values.iter().map(|v| sylt::Type::from(v)).collect()))
+ _ => Err(sylt::error::ErrorKind::ExternTypeMismatch(
+ stringify!(#function).to_string(),
+ __values.iter().map(|v| sylt::Type::from(v)).collect()
+ ))
}
}
}
};
TokenStream::from(tokens)
}
+
+struct LinkRename {
+ _as: Token![as],
+ name: syn::Ident,
+}
+
+impl Parse for LinkRename {
+ fn parse(input: ParseStream) -> Result<Self> {
+ Ok(Self {
+ _as: input.parse()?,
+ name: input.parse()?,
+ })
+ }
+}
+
+struct Link {
+ path: syn::Path,
+ rename: Option<LinkRename>,
+}
+
+impl Parse for Link {
+ fn parse(input: ParseStream) -> Result<Self> {
+ Ok(Self {
+ path: input.parse()?,
+ rename: input.parse().ok(),
+ })
+ }
+}
+
+struct Links {
+ links: Vec<Link>,
+}
+
+impl Parse for Links {
+ fn parse(input: ParseStream) -> Result<Self> {
+ let mut res = Self {
+ links: Vec::new(),
+ };
+ while !input.is_empty() {
+ res.links.push(input.parse()?);
+ let _comma: Option<Token![,]> = input.parse().ok();
+ }
+ Ok(res)
+ }
+}
+
+
+#[proc_macro]
+pub fn link(tokens: TokenStream) -> TokenStream {
+ let links: Links = parse_macro_input!(tokens);
+
+ let links: Vec<_> = links.links.iter().map(|link| {
+ let name = if let Some(rename) = &link.rename {
+ &rename.name
+ } else {
+ &link.path.segments.last().unwrap().ident
+ };
+ let path = &link.path;
+ quote! {
+ (stringify!(#name).to_string(), #path)
+ }
+ }).collect();
+
+ let tokens = quote! {
+ vec![ #(#links),* ]
+ };
+ TokenStream::from(tokens)
+}