Skip to content

Commit 2accc2e

Browse files
committed
ok, so maybe we are writing a bit of a compiler
1 parent bfe2062 commit 2accc2e

29 files changed

Lines changed: 3741 additions & 1364 deletions

Cargo.lock

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,5 @@ members = [
2121
"crates/vim9-parser",
2222
"crates/vim9-gen",
2323
"crates/macros",
24+
"crates/vimfuncs",
2425
]

crates/vim9-gen/src/lib.rs

Lines changed: 179 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#![feature(iter_intersperse)]
22

3-
use std::path::Path;
3+
use std::{collections::HashMap, path::Path};
44

55
use lexer::Lexer;
66
use parser::{
@@ -10,8 +10,8 @@ use parser::{
1010
DictLiteral, EchoCommand, ElseCommand, ElseIfCommand, ExCommand,
1111
ExecuteCommand, Expandable, ExportCommand, Expression, ForCommand,
1212
GroupedExpression, Heredoc, Identifier, IfCommand, ImportCommand,
13-
IndexExpression, IndexType, InfixExpression, InnerType, Lambda, Literal,
14-
MethodCall, MutationStatement, PrefixExpression, RawIdentifier, Register,
13+
IndexExpression, IndexType, InfixExpression, Lambda, Literal, MethodCall,
14+
MutationStatement, Operator, PrefixExpression, RawIdentifier, Register,
1515
ReturnCommand, ScopedIdentifier, SharedCommand, Signature,
1616
StatementCommand, Ternary, TryCommand, Type, UnpackIdentifier, UserCommand,
1717
VarCommand, Vim9ScriptCommand, VimBoolean, VimKey, VimNumber, VimOption,
@@ -68,6 +68,24 @@ impl State {
6868
let res = f(self);
6969
(res, self.scopes.pop().expect("balanced scopes"))
7070
}
71+
72+
fn push_declaration(&mut self, expr_1: &Expression, expr_2: Type) -> Type {
73+
self.scopes
74+
.last_mut()
75+
.unwrap()
76+
.push_declaration(expr_1, expr_2)
77+
}
78+
79+
fn lookup_declaration(&self, expr_1: &Expression) -> Option<Type> {
80+
match Scope::declaration_key(expr_1) {
81+
Some(key) => self
82+
.scopes
83+
.iter()
84+
.rev()
85+
.find_map(|s| s.declarations.get(&key).cloned()),
86+
None => None,
87+
}
88+
}
7189
}
7290

7391
macro_rules! find_scope {
@@ -94,24 +112,33 @@ macro_rules! scope_or_empty {
94112
}};
95113
}
96114

97-
#[derive(PartialEq, Eq, Debug)]
115+
#[derive(PartialEq, Eq, Debug, Default)]
98116
pub enum ScopeKind {
117+
#[default]
99118
TopLevel,
100119
Function,
101-
While { has_continue: bool },
102-
For { has_continue: bool },
120+
While {
121+
has_continue: bool,
122+
},
123+
For {
124+
has_continue: bool,
125+
},
103126
If,
104127
}
105128

106-
#[derive(Debug)]
129+
#[derive(Debug, Default)]
107130
pub struct Scope {
108131
kind: ScopeKind,
109132
deferred: usize,
133+
declarations: HashMap<String, Type>,
110134
}
111135

112136
impl Scope {
113137
pub fn new(kind: ScopeKind) -> Self {
114-
Self { kind, deferred: 0 }
138+
Self {
139+
kind,
140+
..Default::default()
141+
}
115142
}
116143

117144
/// Whether the current scope contains any unique behavior for embedded continues
@@ -122,6 +149,28 @@ impl Scope {
122149
_ => false,
123150
}
124151
}
152+
153+
pub fn declaration_key(expr: &Expression) -> Option<String> {
154+
match expr {
155+
Expression::Identifier(ident) => match &ident {
156+
Identifier::Raw(raw) => Some(raw.name.clone()),
157+
Identifier::Scope(_) => return None,
158+
Identifier::Unpacked(_) => return None,
159+
Identifier::Ellipsis => return None,
160+
},
161+
_ => return None,
162+
}
163+
}
164+
165+
pub fn push_declaration(&mut self, expr: &Expression, ty: Type) -> Type {
166+
let key = match Self::declaration_key(expr) {
167+
Some(key) => key,
168+
None => return ty,
169+
};
170+
171+
self.declarations.insert(key, ty.clone());
172+
ty
173+
}
125174
}
126175

127176
pub trait Generate {
@@ -423,19 +472,19 @@ impl Generate for DeclCommand {
423472
"local {} = {}",
424473
self.name.gen(state),
425474
match &self.ty {
426-
Some(ty) => match ty.inner {
427-
InnerType::Any => "0",
428-
InnerType::Bool => "false",
429-
InnerType::Number => "0",
430-
InnerType::Float => "0",
431-
InnerType::String => "''",
432-
InnerType::List { .. } => "{}",
433-
InnerType::Dict { .. } => "vim.empty_dict()",
434-
InnerType::Job => todo!(),
435-
InnerType::Channel => todo!(),
436-
InnerType::Void => "nil",
437-
InnerType::Func(_) => "function() end",
438-
InnerType::Blob => unreachable!(),
475+
Some(ty) => match ty {
476+
Type::Any => "0",
477+
Type::Bool => "false",
478+
Type::Number => "0",
479+
Type::Float => "0",
480+
Type::String => "''",
481+
Type::List { .. } => "{}",
482+
Type::Dict { .. } => "vim.empty_dict()",
483+
Type::Job => todo!(),
484+
Type::Channel => todo!(),
485+
Type::Void => "nil",
486+
Type::Func(_) => "function() end",
487+
Type::Blob => unreachable!(),
439488
},
440489
None => "nil",
441490
}
@@ -715,14 +764,30 @@ impl Generate for IfCommand {
715764
None => "".to_string(),
716765
};
717766

767+
let condition = self.condition.gen(state);
768+
769+
// TODO: Numbers are automatically coerced into bools for vim
770+
// so sometimes the type system lies to us.
771+
//
772+
// Perhaps I need an additional type: BoolNumber that we use
773+
// for most cases of vim, and we only escalate to Bool when we're confident
774+
// that this what is happening.
775+
//
776+
// This would allow us to remove the NVIM9.bool condition (assuming the type
777+
// system is correct in vim9script)
778+
//
779+
// let condition = match guess_type_of_expr(state, &self.condition).inner {
780+
// Type::Bool => condition,
781+
// _ => format!("NVIM9.bool({condition})"),
782+
// };
783+
718784
format!(
719785
r#"
720-
if NVIM9.bool({}) then
786+
if NVIM9.bool({condition}) then
721787
{}
722788
{}
723789
{}
724790
end"#,
725-
self.condition.gen(state),
726791
self.body.gen(state),
727792
self.elseifs
728793
.iter()
@@ -740,7 +805,7 @@ impl Generate for ElseIfCommand {
740805
fn gen(&self, state: &mut State) -> String {
741806
format!(
742807
r#"
743-
elseif {} then
808+
elseif NVIM9.bool({}) then
744809
{}
745810
"#,
746811
self.condition.gen(state),
@@ -802,17 +867,68 @@ fn identifier_list(state: &mut State, unpacked: &UnpackIdentifier) -> String {
802867
.collect()
803868
}
804869

870+
fn guess_type_of_expr(_: &State, expr: &Expression) -> Type {
871+
match expr {
872+
Expression::Number(_) => Type::Number,
873+
Expression::String(_) => Type::String,
874+
Expression::Boolean(_) => Type::Bool,
875+
Expression::Call(c) => match c.name() {
876+
Some(ident) => match ident {
877+
// TODO: Use our very cool new generated stuff here
878+
Identifier::Raw(raw) => match raw.name.as_str() {
879+
"charcol" => Type::Number,
880+
_ => Type::Any,
881+
},
882+
_ => Type::Any,
883+
},
884+
_ => Type::Any,
885+
},
886+
Expression::Infix(infix) => {
887+
if infix.operator.is_comparison() {
888+
return Type::Bool;
889+
} else if matches!(infix.operator, Operator::And | Operator::Or) {
890+
// if guess_type_of_expr(infix.left) {}
891+
Type::Any
892+
// todo!()
893+
} else {
894+
Type::Any
895+
}
896+
}
897+
_ => Type::Any,
898+
// Expression::Grouped(_) => todo!(),
899+
// Expression::Index(_) => todo!(),
900+
// Expression::Slice(_) => todo!(),
901+
// Expression::Array(_) => todo!(),
902+
// Expression::Dict(_) => todo!(),
903+
// Expression::DictAccess(_) => todo!(),
904+
// Expression::VimOption(_) => todo!(),
905+
// Expression::Register(_) => todo!(),
906+
// Expression::Lambda(_) => todo!(),
907+
// Expression::Expandable(_) => todo!(),
908+
// Expression::MethodCall(_) => todo!(),
909+
// Expression::Ternary(_) => todo!(),
910+
// Expression::Prefix(_) => todo!(),
911+
// Expression::Infix(_) => todo!(),
912+
}
913+
}
914+
805915
impl Generate for VarCommand {
806916
fn gen(&self, state: &mut State) -> String {
917+
state.push_declaration(
918+
&self.expr,
919+
match &self.ty {
920+
Some(ty) => ty.clone(),
921+
None => guess_type_of_expr(state, &self.expr),
922+
},
923+
);
924+
807925
let expr = match self.ty {
808-
Some(Type {
809-
inner: InnerType::Bool,
810-
..
811-
}) => format!("NVIM9.convert.decl_bool({})", self.expr.gen(state)),
812-
Some(Type {
813-
inner: InnerType::Dict { .. },
814-
..
815-
}) => format!("NVIM9.convert.decl_dict({})", self.expr.gen(state)),
926+
Some(Type::Bool) => {
927+
format!("NVIM9.convert.decl_bool({})", self.expr.gen(state))
928+
}
929+
Some(Type::Dict { .. }) => {
930+
format!("NVIM9.convert.decl_dict({})", self.expr.gen(state))
931+
}
816932
_ => self.expr.gen(state),
817933
};
818934

@@ -1029,7 +1145,22 @@ impl Generate for PrefixExpression {
10291145
match &self.operator {
10301146
parser::Operator::Increment => format!("{right} = {right} + 1"),
10311147
parser::Operator::Decrement => format!("{right} = {right} - 1"),
1032-
_ => format!("NVIM9.prefix['{:?}']({right})", self.operator,),
1148+
_ => {
1149+
let ty = guess_type_of_expr(state, &self.right);
1150+
match ty {
1151+
Type::Number => {
1152+
let operator = match &self.operator {
1153+
parser::Operator::Minus => "-",
1154+
_ => todo!("OPERATOR: {:?}", self.operator),
1155+
};
1156+
1157+
format!("{operator}{right}")
1158+
}
1159+
_ => {
1160+
format!("NVIM9.prefix['{:?}']({right})", self.operator,)
1161+
}
1162+
}
1163+
}
10331164
}
10341165
}
10351166
}
@@ -1188,12 +1319,21 @@ impl Generate for VimNumber {
11881319

11891320
impl Generate for InfixExpression {
11901321
fn gen(&self, state: &mut State) -> String {
1191-
format!(
1192-
"NVIM9.ops['{:?}']({}, {})",
1193-
self.operator,
1194-
self.left.gen(state),
1195-
self.right.gen(state)
1196-
)
1322+
use Type::*;
1323+
1324+
let ty = guess_type_of_expr(state, &Expression::Infix(self.clone()));
1325+
1326+
let left = self.left.gen(state);
1327+
let right = self.right.gen(state);
1328+
1329+
if let Some(literal) = self.operator.literal() {
1330+
match ty {
1331+
Bool => return format!("{left} {literal} {right}",),
1332+
_ => {}
1333+
}
1334+
}
1335+
1336+
format!("NVIM9.ops['{:?}']({left}, {right})", self.operator)
11971337

11981338
// match self.operator {
11991339
// Operator::And => gen_operation(

crates/vim9-gen/testdata/busted/assign.vim

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
vim9script
22

3-
def Test_assignment_bool()
3+
def Test_assignment_bool_1()
44
var bool1: bool = true
55
assert_equal(v:true, bool1)
66
var bool2: bool = false
77
assert_equal(v:false, bool2)
8+
enddef
89

10+
def Test_assignment_bool_2()
911
var bool3: bool = 0
1012
assert_equal(false, bool3)
1113
var bool4: bool = 1
1214
assert_equal(true, bool4)
15+
enddef
1316

17+
def Test_assignment_bool_3()
1418
var bool5: bool = 1 && true
1519
assert_equal(true, bool5)
1620
var bool6: bool = 0 && 1

0 commit comments

Comments
 (0)