or and and are now reserved words

This commit is contained in:
Scott Richmond 2025-05-28 16:37:25 -04:00
parent 182b14e5f4
commit 82ac6744ca
7 changed files with 183 additions and 20 deletions

View File

@ -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());
}

View File

@ -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),
});

View File

@ -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);
}

View File

@ -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);

View File

@ -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 => (),

View File

@ -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>,
}

View File

@ -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!(),
}
}
}