or
and and
are now reserved words
This commit is contained in:
parent
182b14e5f4
commit
82ac6744ca
152
src/compiler.rs
152
src/compiler.rs
|
@ -5,7 +5,6 @@ use chumsky::prelude::SimpleSpan;
|
|||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
use num_traits::FromPrimitive;
|
||||
use std::cell::OnceCell;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, ToPrimitive)]
|
||||
|
@ -17,11 +16,13 @@ pub enum Op {
|
|||
Constant,
|
||||
Jump,
|
||||
JumpIfFalse,
|
||||
JumpIfTrue,
|
||||
Pop,
|
||||
PopN,
|
||||
PushBinding,
|
||||
Store,
|
||||
StoreAt,
|
||||
Stash,
|
||||
Load,
|
||||
ResetMatch,
|
||||
MatchNil,
|
||||
|
@ -53,6 +54,8 @@ pub enum Op {
|
|||
Truncate,
|
||||
MatchDepth,
|
||||
|
||||
Call,
|
||||
|
||||
Eq,
|
||||
Add,
|
||||
Sub,
|
||||
|
@ -63,6 +66,46 @@ pub enum Op {
|
|||
Assert,
|
||||
Get,
|
||||
At,
|
||||
|
||||
Not,
|
||||
// Inc,
|
||||
// Dec,
|
||||
// Gt,
|
||||
// Gte,
|
||||
// Lt,
|
||||
// Lte,
|
||||
// Mod,
|
||||
// Round,
|
||||
// Ceil,
|
||||
// Floor,
|
||||
// Random,
|
||||
// Sqrt,
|
||||
|
||||
// Assoc,
|
||||
// Concat,
|
||||
// Conj,
|
||||
// Count,
|
||||
// Disj,
|
||||
// Dissoc,
|
||||
// Range,
|
||||
// Rest,
|
||||
// Slice,
|
||||
|
||||
// "atan_2" math/atan2
|
||||
// "chars" chars
|
||||
// "cos" math/cos
|
||||
// "doc" doc
|
||||
// "downcase" string/ascii-lower
|
||||
// "pi" math/pi
|
||||
// "show" show
|
||||
// "sin" math/sin
|
||||
// "split" string/split
|
||||
// "str_slice" string/slice
|
||||
// "tan" math/tan
|
||||
// "trim" string/trim
|
||||
// "triml" string/triml
|
||||
// "trimr" string/trimr
|
||||
// "upcase" string/ascii-upper
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Op {
|
||||
|
@ -76,11 +119,13 @@ impl std::fmt::Display for Op {
|
|||
Constant => "constant",
|
||||
Jump => "jump",
|
||||
JumpIfFalse => "jump_if_false",
|
||||
JumpIfTrue => "jump_if_true",
|
||||
Pop => "pop",
|
||||
PopN => "pop_n",
|
||||
PushBinding => "push_binding",
|
||||
Store => "store",
|
||||
StoreAt => "store_at",
|
||||
Stash => "stash",
|
||||
Load => "load",
|
||||
MatchNil => "match_nil",
|
||||
MatchTrue => "match_true",
|
||||
|
@ -122,6 +167,10 @@ impl std::fmt::Display for Op {
|
|||
Assert => "assert",
|
||||
Get => "get",
|
||||
At => "at",
|
||||
|
||||
Not => "not",
|
||||
|
||||
Call => "call",
|
||||
};
|
||||
write!(f, "{rep}")
|
||||
}
|
||||
|
@ -147,10 +196,10 @@ impl Chunk {
|
|||
let op = Op::from_u8(self.bytecode[i]).unwrap();
|
||||
use Op::*;
|
||||
match op {
|
||||
Pop | Store | Load | Nil | True | False | MatchNil | MatchTrue | MatchFalse
|
||||
Pop | Store | Stash | Load | Nil | True | False | MatchNil | MatchTrue | MatchFalse
|
||||
| PanicIfNoMatch | MatchWord | ResetMatch | GetKey | PanicNoWhen | PanicNoMatch
|
||||
| TypeOf | Duplicate | Decrement | Truncate | Noop | LoadTuple | LoadList | Eq
|
||||
| Add | Sub | Mult | Div | Unbox | BoxStore | Assert | Get | At => {
|
||||
| Add | Sub | Mult | Div | Unbox | BoxStore | Assert | Get | At | Not => {
|
||||
println!("{i:04}: {op}")
|
||||
}
|
||||
Constant | MatchConstant => {
|
||||
|
@ -159,8 +208,8 @@ impl Chunk {
|
|||
println!("{i:04}: {:16} {next:04}: {value}", op.to_string());
|
||||
}
|
||||
PushBinding | MatchTuple | MatchList | MatchDict | LoadDictValue | PushTuple
|
||||
| PushDict | PushList | PushBox | Jump | JumpIfFalse | JumpIfNoMatch | JumpIfMatch
|
||||
| JumpBack | JumpIfZero | MatchDepth | PopN | StoreAt => {
|
||||
| PushDict | PushList | PushBox | Jump | JumpIfFalse | JumpIfTrue | JumpIfNoMatch
|
||||
| JumpIfMatch | JumpBack | JumpIfZero | MatchDepth | PopN | StoreAt | Call => {
|
||||
let next = self.bytecode[i + 1];
|
||||
println!("{i:04}: {:16} {next:04}", op.to_string());
|
||||
}
|
||||
|
@ -201,6 +250,8 @@ fn get_builtin(name: &str, arity: usize) -> Option<Op> {
|
|||
("assert!", 1) => Some(Op::Assert),
|
||||
("get", 2) => Some(Op::Get),
|
||||
("at", 2) => Some(Op::At),
|
||||
("not", 1) => Some(Op::Not),
|
||||
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -697,6 +748,46 @@ impl Compiler {
|
|||
self.emit_op(Op::GetKey);
|
||||
self.stack_depth -= 1;
|
||||
}
|
||||
(Or, Arguments(args)) => {
|
||||
let stack_depth = self.stack_depth;
|
||||
let mut jump_idxes = vec![];
|
||||
if !args.is_empty() {
|
||||
for arg in args {
|
||||
self.visit(arg);
|
||||
self.emit_op(Op::Stash);
|
||||
self.emit_op(Op::JumpIfTrue);
|
||||
jump_idxes.push(self.len());
|
||||
self.emit_byte(0xff);
|
||||
}
|
||||
for idx in jump_idxes {
|
||||
self.chunk.bytecode[idx] = (self.len() - idx + 2) as u8;
|
||||
}
|
||||
self.emit_op(Op::Load);
|
||||
} else {
|
||||
self.emit_op(Op::False);
|
||||
}
|
||||
self.stack_depth = stack_depth + 1;
|
||||
}
|
||||
(And, Arguments(args)) => {
|
||||
let stack_depth = self.stack_depth;
|
||||
let mut jump_idxes = vec![];
|
||||
if !args.is_empty() {
|
||||
for arg in args {
|
||||
self.visit(arg);
|
||||
self.emit_op(Op::Stash);
|
||||
self.emit_op(Op::JumpIfFalse);
|
||||
jump_idxes.push(self.len());
|
||||
self.emit_byte(0xff);
|
||||
}
|
||||
for idx in jump_idxes {
|
||||
self.chunk.bytecode[idx] = (self.len() - idx + 2) as u8;
|
||||
}
|
||||
self.emit_op(Op::Load);
|
||||
} else {
|
||||
self.emit_op(Op::True);
|
||||
}
|
||||
self.stack_depth = stack_depth + 1;
|
||||
}
|
||||
(Word(fn_name), Arguments(args)) => match get_builtin(fn_name, args.len()) {
|
||||
Some(code) => {
|
||||
for arg in args {
|
||||
|
@ -705,7 +796,28 @@ impl Compiler {
|
|||
self.emit_op(code);
|
||||
self.stack_depth -= args.len() - 1;
|
||||
}
|
||||
None => todo!(),
|
||||
None => {
|
||||
//algo
|
||||
// visit all the args
|
||||
// then store them
|
||||
// then call the function
|
||||
for (register_slot, arg) in args.iter().enumerate() {
|
||||
self.visit(arg);
|
||||
self.emit_op(Op::StoreAt);
|
||||
self.emit_byte(register_slot);
|
||||
}
|
||||
|
||||
self.emit_op(Op::PushBinding);
|
||||
self.stack_depth += 1;
|
||||
let biter = self.bindings.iter().rev();
|
||||
for binding in biter {
|
||||
if binding.name == *fn_name {
|
||||
self.emit_byte(binding.stack_pos);
|
||||
break;
|
||||
}
|
||||
}
|
||||
self.emit_op(Op::Call);
|
||||
}
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
@ -817,6 +929,21 @@ impl Compiler {
|
|||
self.emit_constant(init_val);
|
||||
self.bind(name);
|
||||
|
||||
let FnBody(fn_body) = &body.as_ref().0 else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let mut chunks = vec![];
|
||||
|
||||
for clause in fn_body {
|
||||
let MatchClause(pattern, guard, clause_body) = &clause.0 else {
|
||||
unreachable!()
|
||||
};
|
||||
let TuplePattern(pattern) = &pattern.0 else {
|
||||
unreachable!()
|
||||
};
|
||||
let arity = pattern.len();
|
||||
}
|
||||
// compile the function
|
||||
let mut compiler = Compiler::new(body, self.name, self.src);
|
||||
compiler.compile();
|
||||
|
@ -828,7 +955,7 @@ impl Compiler {
|
|||
let lfn = crate::value::LFn {
|
||||
name,
|
||||
doc: *doc,
|
||||
chunk: compiler.chunk,
|
||||
chunks,
|
||||
closed: vec![],
|
||||
};
|
||||
|
||||
|
@ -976,6 +1103,7 @@ impl Compiler {
|
|||
| InterpolatedPattern(..)
|
||||
| AsPattern(..)
|
||||
| Splattern(..) => todo!(),
|
||||
And | Or => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -987,10 +1115,11 @@ impl Compiler {
|
|||
let op = Op::from_u8(*byte).unwrap();
|
||||
use Op::*;
|
||||
match op {
|
||||
Noop | Pop | Store | Load | Nil | True | False | MatchNil | MatchTrue
|
||||
Noop | Pop | Store | Stash | Load | Nil | True | False | MatchNil | MatchTrue
|
||||
| MatchFalse | MatchWord | ResetMatch | PanicIfNoMatch | GetKey | PanicNoWhen
|
||||
| PanicNoMatch | TypeOf | Duplicate | Truncate | Decrement | LoadTuple
|
||||
| LoadList | Eq | Add | Sub | Mult | Div | Unbox | BoxStore | Assert | Get | At => {
|
||||
| LoadList | Eq | Add | Sub | Mult | Div | Unbox | BoxStore | Assert | Get | At
|
||||
| Not => {
|
||||
println!("{i:04}: {op}")
|
||||
}
|
||||
Constant | MatchConstant => {
|
||||
|
@ -999,8 +1128,9 @@ impl Compiler {
|
|||
println!("{i:04}: {:16} {next:04}: {value}", op.to_string());
|
||||
}
|
||||
PushBinding | MatchTuple | MatchList | PushTuple | PushDict | PushList
|
||||
| MatchDict | LoadDictValue | PushBox | Jump | JumpIfFalse | JumpIfNoMatch
|
||||
| JumpIfMatch | JumpBack | JumpIfZero | MatchDepth | PopN | StoreAt => {
|
||||
| MatchDict | LoadDictValue | PushBox | Jump | JumpIfFalse | JumpIfTrue
|
||||
| JumpIfNoMatch | JumpIfMatch | JumpBack | JumpIfZero | MatchDepth | PopN
|
||||
| StoreAt | Call => {
|
||||
let (_, next) = codes.next().unwrap();
|
||||
println!("{i:04}: {:16} {next:04}", op.to_string());
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ pub fn lexer(
|
|||
"nil" => Token::Nil,
|
||||
// todo: hard code these as type constructors
|
||||
"as" | "box" | "do" | "else" | "fn" | "if" | "let" | "loop" | "match" | "panic!"
|
||||
| "recur" | "repeat" | "then" | "when" | "with" => Token::Reserved(word),
|
||||
| "recur" | "repeat" | "then" | "when" | "with" | "or" | "and" => Token::Reserved(word),
|
||||
_ => Token::Word(word),
|
||||
});
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ use std::env;
|
|||
const DEBUG_COMPILE: bool = true;
|
||||
const DEBUG_RUN: bool = true;
|
||||
|
||||
mod memory_sandbox;
|
||||
// mod base;
|
||||
|
||||
mod spans;
|
||||
use crate::spans::Spanned;
|
||||
|
@ -74,8 +74,7 @@ pub fn run(src: &'static str) {
|
|||
pub fn main() {
|
||||
env::set_var("RUST_BACKTRACE", "1");
|
||||
let src = "
|
||||
let foo = #{:a 1, :b 2}
|
||||
:a (foo)
|
||||
or (true, true, 42)
|
||||
";
|
||||
run(src);
|
||||
}
|
||||
|
|
|
@ -63,6 +63,9 @@ pub enum Ast {
|
|||
// may come in handy?
|
||||
Error,
|
||||
|
||||
And,
|
||||
Or,
|
||||
|
||||
// expression nodes
|
||||
Placeholder,
|
||||
Nil,
|
||||
|
@ -121,6 +124,8 @@ impl fmt::Display for Ast {
|
|||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use Ast::*;
|
||||
match self {
|
||||
And => write!(f, "And"),
|
||||
Or => write!(f, "Or"),
|
||||
Error => write!(f, "Error"),
|
||||
Nil => write!(f, "nil"),
|
||||
String(s) => write!(f, "String: \"{}\"", s),
|
||||
|
@ -727,7 +732,11 @@ where
|
|||
.delimited_by(just(Token::Punctuation("(")), just(Token::Punctuation(")")))
|
||||
.map_with(|args, e| (Arguments(args), e.span()));
|
||||
|
||||
let synth_root = word.or(keyword);
|
||||
let or = just(Token::Reserved("or")).map_with(|_, e| (Or, e.span()));
|
||||
|
||||
let and = just(Token::Reserved("and")).map_with(|_, e| (And, e.span()));
|
||||
|
||||
let synth_root = or.or(and).or(word).or(keyword);
|
||||
|
||||
let synth_term = keyword.or(args);
|
||||
|
||||
|
|
|
@ -570,7 +570,7 @@ impl<'a> Validator<'a> {
|
|||
}
|
||||
PairPattern(_, patt) => self.visit(patt.as_ref()),
|
||||
// terminals can never be invalid
|
||||
Nil | Boolean(_) | Number(_) | Keyword(_) | String(_) => (),
|
||||
Nil | Boolean(_) | Number(_) | Keyword(_) | String(_) | And | Or => (),
|
||||
// terminal patterns can never be invalid
|
||||
NilPattern | BooleanPattern(..) | NumberPattern(..) | StringPattern(..)
|
||||
| KeywordPattern(..) | PlaceholderPattern => (),
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct LFn {
|
|||
// pub has_run: bool,
|
||||
// pub input: &'static str,
|
||||
// pub src: &'static str,
|
||||
pub chunk: Chunk,
|
||||
pub chunks: Vec<(u8, Chunk)>,
|
||||
pub closed: Vec<Value>,
|
||||
}
|
||||
|
||||
|
|
29
src/vm.rs
29
src/vm.rs
|
@ -34,7 +34,7 @@ pub struct Vm<'a> {
|
|||
pub stack: Vec<Value>,
|
||||
pub chunk: &'a Chunk,
|
||||
pub ip: usize,
|
||||
pub return_register: [Value; 8],
|
||||
pub return_register: [Value; 7],
|
||||
pub matches: bool,
|
||||
pub match_depth: u8,
|
||||
pub result: Option<Result<Value, Panic>>,
|
||||
|
@ -54,7 +54,6 @@ impl<'a> Vm<'a> {
|
|||
Value::Nothing,
|
||||
Value::Nothing,
|
||||
Value::Nothing,
|
||||
Value::Nothing,
|
||||
],
|
||||
matches: false,
|
||||
match_depth: 0,
|
||||
|
@ -158,6 +157,18 @@ impl<'a> Vm<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
JumpIfTrue => {
|
||||
let jump_len = self.chunk.bytecode[self.ip + 1];
|
||||
let cond = self.pop();
|
||||
match cond {
|
||||
Value::Nil | Value::False => {
|
||||
self.ip += 2;
|
||||
}
|
||||
_ => {
|
||||
self.ip += jump_len as usize + 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
JumpIfZero => {
|
||||
let jump_len = self.chunk.bytecode[self.ip + 1];
|
||||
let cond = self.pop();
|
||||
|
@ -198,6 +209,10 @@ impl<'a> Vm<'a> {
|
|||
self.return_register[i] = self.pop();
|
||||
self.ip += 2;
|
||||
}
|
||||
Stash => {
|
||||
self.return_register[0] = self.peek().clone();
|
||||
self.ip += 1;
|
||||
}
|
||||
Load => {
|
||||
let mut i = 0;
|
||||
while i < 8 && self.return_register[i] != Value::Nothing {
|
||||
|
@ -530,6 +545,16 @@ impl<'a> Vm<'a> {
|
|||
self.stack.push(value);
|
||||
self.ip += 1;
|
||||
}
|
||||
Not => {
|
||||
let value = self.pop();
|
||||
let negated = match value {
|
||||
Value::Nil | Value::False => Value::True,
|
||||
_ => Value::False,
|
||||
};
|
||||
self.push(negated);
|
||||
self.ip += 1;
|
||||
}
|
||||
Call => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user