added support for builtin functions and added some for strings
This commit is contained in:
parent
b83c4bb0cc
commit
1315b2878a
10 changed files with 198 additions and 38 deletions
|
|
@ -1,6 +1,6 @@
|
|||
object Person:
|
||||
name: string
|
||||
|
||||
fn get(path: string) -> string:
|
||||
let p = Person(name: path)
|
||||
service.add("hello", p.name)
|
||||
// fn get(path: string) -> string:
|
||||
// let p = Person(name: path)
|
||||
// service.add("hello", p.name)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::ast_compiler::Expression::{
|
||||
FieldGet, FunctionCall, ListGet, MapGet, NamedParameter, Stop, Variable,
|
||||
FieldGet, FunctionCall, ListGet, MapGet, MethodCall, NamedParameter, Stop, Variable,
|
||||
};
|
||||
use crate::errors::CompilerError::{
|
||||
self, Expected, ParseError, TooManyParameters, UnexpectedIndent, UninitializedVariable,
|
||||
|
|
@ -517,7 +517,7 @@ impl AstCompiler {
|
|||
} else if self.match_token(vec![Dot]) {
|
||||
let name = self.peek().clone();
|
||||
self.advance();
|
||||
self.field(expr, name)
|
||||
self.field_or_method(expr, name, symbol_table)
|
||||
} else {
|
||||
Ok(expr)
|
||||
}
|
||||
|
|
@ -557,21 +557,33 @@ impl AstCompiler {
|
|||
}
|
||||
|
||||
// work in progress
|
||||
fn field(
|
||||
fn field_or_method(
|
||||
&mut self,
|
||||
_operand: Expression,
|
||||
index: Token,
|
||||
receiver: Expression,
|
||||
op: Token,
|
||||
symbol_table: &mut HashMap<String, Symbol>,
|
||||
) -> Result<Expression, CompilerErrorAtLine> {
|
||||
Ok(FieldGet {
|
||||
field: index.lexeme.clone(),
|
||||
if self.match_token(vec![LeftParen]) {
|
||||
let arguments = self.arguments(symbol_table)?;
|
||||
Ok(MethodCall {
|
||||
receiver: Box::new(receiver.clone()),
|
||||
method_name: op.lexeme,
|
||||
arguments,
|
||||
line: op.line,
|
||||
})
|
||||
} else {
|
||||
// no test yet
|
||||
Ok(FieldGet {
|
||||
receiver: Box::new(receiver.clone()),
|
||||
field: op.lexeme.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn primary(
|
||||
&mut self,
|
||||
symbol_table: &mut HashMap<String, Symbol>,
|
||||
) -> Result<Expression, CompilerErrorAtLine> {
|
||||
debug!("primary {:?}", self.peek());
|
||||
Ok(if self.match_token(vec![LeftBracket]) {
|
||||
self.list(symbol_table)?
|
||||
} else if self.match_token(vec![LeftBrace]) {
|
||||
|
|
@ -755,6 +767,18 @@ impl AstCompiler {
|
|||
name: Token,
|
||||
symbol_table: &mut HashMap<String, Symbol>,
|
||||
) -> Result<Expression, CompilerErrorAtLine> {
|
||||
let arguments = self.arguments(symbol_table)?;
|
||||
Ok(FunctionCall {
|
||||
line: self.peek().line,
|
||||
name: name.lexeme.to_string(),
|
||||
arguments,
|
||||
})
|
||||
}
|
||||
|
||||
fn arguments(
|
||||
&mut self,
|
||||
symbol_table: &mut HashMap<String, Symbol>,
|
||||
) -> Result<Vec<Expression>, CompilerErrorAtLine> {
|
||||
let mut arguments = vec![];
|
||||
while !self.match_token(vec![RightParen]) {
|
||||
if arguments.len() >= 25 {
|
||||
|
|
@ -769,11 +793,7 @@ impl AstCompiler {
|
|||
break;
|
||||
}
|
||||
}
|
||||
Ok(FunctionCall {
|
||||
line: self.peek().line,
|
||||
name: name.lexeme.to_string(),
|
||||
arguments,
|
||||
})
|
||||
Ok(arguments)
|
||||
}
|
||||
|
||||
fn consume(
|
||||
|
|
@ -919,6 +939,12 @@ pub enum Expression {
|
|||
name: String,
|
||||
arguments: Vec<Expression>,
|
||||
},
|
||||
MethodCall {
|
||||
line: usize,
|
||||
receiver: Box<Expression>,
|
||||
method_name: String,
|
||||
arguments: Vec<Expression>,
|
||||
},
|
||||
Stop {
|
||||
line: usize,
|
||||
},
|
||||
|
|
@ -936,6 +962,7 @@ pub enum Expression {
|
|||
index: Box<Expression>,
|
||||
},
|
||||
FieldGet {
|
||||
receiver: Box<Expression>,
|
||||
field: String,
|
||||
},
|
||||
}
|
||||
|
|
@ -951,6 +978,7 @@ impl Expression {
|
|||
Self::Map { line, .. } => *line,
|
||||
Variable { line, .. } => *line,
|
||||
FunctionCall { line, .. } => *line,
|
||||
MethodCall {line,..} => *line,
|
||||
Stop { line } => *line,
|
||||
NamedParameter { line, .. } => *line,
|
||||
MapGet { .. } => 0,
|
||||
|
|
|
|||
66
src/builtin_functions.rs
Normal file
66
src/builtin_functions.rs
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
use crate::value::Value;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::LazyLock;
|
||||
use crate::errors::RuntimeError;
|
||||
|
||||
type MethodFn = fn(Value, Vec<Value>) -> Result<Value, RuntimeError>;
|
||||
type MethodMap = HashMap<String, MethodFn>;
|
||||
type MethodTable = HashMap<String, MethodMap>;
|
||||
|
||||
const METHODS: LazyLock<MethodTable> = LazyLock::new(|| {
|
||||
let mut table: MethodTable = HashMap::new();
|
||||
|
||||
let mut string_methods: MethodMap = HashMap::new();
|
||||
string_methods.insert("len".to_string(), string_len);
|
||||
string_methods.insert("to_uppercase".to_string(), string_to_uppercase);
|
||||
string_methods.insert("contains".to_string(), string_contains);
|
||||
string_methods.insert("reverse".to_string(), string_reverse);
|
||||
|
||||
table.insert("string".to_string(), string_methods);
|
||||
|
||||
table
|
||||
});
|
||||
|
||||
pub fn call_builtin(
|
||||
type_name: &str,
|
||||
method_name: &str,
|
||||
self_val: Value,
|
||||
args: Vec<Value>,
|
||||
) -> Result<Value, RuntimeError> {
|
||||
METHODS
|
||||
.get(type_name)
|
||||
.and_then(|methods| methods.get(method_name))
|
||||
.ok_or_else(|| RuntimeError::FunctionNotFound(format!("{}.{}",type_name, method_name)))?
|
||||
(self_val, args)
|
||||
}
|
||||
|
||||
fn string_len(self_val: Value, _args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
match self_val {
|
||||
Value::String(s) => Ok(Value::I64(s.len() as i64)),
|
||||
_ => Err(RuntimeError::ExpectedType("string".to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
fn string_to_uppercase(self_val: Value, _args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
match self_val {
|
||||
Value::String(s) => Ok(Value::String(s.to_uppercase())),
|
||||
_ => Err(RuntimeError::ExpectedType("string".to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
fn string_contains(self_val: Value, args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
match (self_val, args.first()) {
|
||||
(Value::String(s), Some(Value::String(pat))) => {
|
||||
Ok(Value::Bool(s.contains(pat.as_str())))
|
||||
}
|
||||
_ => Err(RuntimeError::ExpectedType("string".to_string())),
|
||||
}
|
||||
}
|
||||
fn string_reverse(self_val: Value, _: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
match self_val {
|
||||
Value::String(s) => {
|
||||
Ok(s.chars().rev().collect::<String>().into())
|
||||
}
|
||||
_ => Err(RuntimeError::ExpectedType("string".to_string())),
|
||||
}
|
||||
}
|
||||
|
|
@ -6,12 +6,7 @@ use crate::symbol_builder::{Symbol, calculate_type, infer_type};
|
|||
use crate::tokens::TokenType;
|
||||
use crate::tokens::TokenType::Unknown;
|
||||
use crate::value::Value;
|
||||
use crate::vm::{
|
||||
OP_ADD, OP_AND, OP_ASSIGN, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CALL, OP_CONSTANT, OP_DEF_LIST,
|
||||
OP_DEF_MAP, OP_DIVIDE, OP_EQUAL, OP_GET, OP_GREATER, OP_GREATER_EQUAL, OP_LESS, OP_LESS_EQUAL,
|
||||
OP_LIST_GET, OP_MULTIPLY, OP_NEGATE, OP_NOT, OP_OR, OP_PRINT, OP_RETURN, OP_SHL, OP_SHR,
|
||||
OP_SUBTRACT,
|
||||
};
|
||||
use crate::vm::{OP_ADD, OP_AND, OP_ASSIGN, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CALL, OP_CALL_BUILTIN, OP_CONSTANT, OP_DEF_LIST, OP_DEF_MAP, OP_DIVIDE, OP_EQUAL, OP_GET, OP_GREATER, OP_GREATER_EQUAL, OP_LESS, OP_LESS_EQUAL, OP_LIST_GET, OP_MULTIPLY, OP_NEGATE, OP_NOT, OP_OR, OP_PRINT, OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT};
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub fn compile(
|
||||
|
|
@ -193,6 +188,31 @@ impl Compiler {
|
|||
}
|
||||
}
|
||||
}
|
||||
Expression::MethodCall {
|
||||
receiver,
|
||||
method_name,
|
||||
arguments,
|
||||
..
|
||||
} => {
|
||||
self.compile_expression(namespace, receiver, symbols, registry)?;
|
||||
let receiver_type = infer_type(receiver,symbols).to_string();
|
||||
|
||||
let type_index = self
|
||||
.chunk
|
||||
.find_constant(&receiver_type)
|
||||
.unwrap_or_else(|| self.chunk.add_constant(Value::String(receiver_type)));
|
||||
|
||||
let name_index = self
|
||||
.chunk
|
||||
.find_constant(&method_name)
|
||||
.unwrap_or_else(|| self.chunk.add_constant(Value::String(method_name.to_string())));
|
||||
//TODO lookup parameters for builtin
|
||||
self.get_arguments_in_order( namespace, symbols, registry, arguments, &vec![])?;
|
||||
self.emit_byte(OP_CALL_BUILTIN);
|
||||
self.emit_byte(name_index as u16);
|
||||
self.emit_byte(type_index as u16);
|
||||
self.emit_byte(arguments.len() as u16);
|
||||
}
|
||||
Expression::Variable { name, line, .. } => {
|
||||
let name_index = self.vars.get(name);
|
||||
if let Some(name_index) = name_index {
|
||||
|
|
@ -296,7 +316,13 @@ impl Compiler {
|
|||
if name.lexeme == parameter.name.lexeme {
|
||||
let value_type = infer_type(value, symbols);
|
||||
if parameter.var_type != value_type {
|
||||
return Err(CompilerErrorAtLine::raise(CompilerError::IncompatibleTypes(parameter.var_type.clone(), value_type), argument.line()));
|
||||
return Err(CompilerErrorAtLine::raise(
|
||||
CompilerError::IncompatibleTypes(
|
||||
parameter.var_type.clone(),
|
||||
value_type,
|
||||
),
|
||||
argument.line(),
|
||||
));
|
||||
} else {
|
||||
self.compile_expression(namespace, argument, symbols, registry)?;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -258,10 +258,20 @@ date"#),
|
|||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn string_reverse(){
|
||||
// assert_eq!(run(r#""abc".reverse()"#), Ok(Value::String("cba".into())));
|
||||
// }
|
||||
#[test]
|
||||
fn string_reverse(){
|
||||
assert_eq!(run(r#""abc".reverse()"#), Ok(Value::String("cba".into())));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_to_upper(){
|
||||
assert_eq!(run(r#""abc".to_uppercase()"#), Ok(Value::String("ABC".into())));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_len(){
|
||||
assert_eq!(run(r#""abc".len()"#), Ok(Value::I64(3)));
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn package() {
|
||||
|
|
|
|||
|
|
@ -83,6 +83,8 @@ pub enum RuntimeError {
|
|||
FunctionNotFound(String),
|
||||
#[error("The number of of arguments for {0} is not correct. Should be {1}, got {2}")]
|
||||
IllegalArgumentsException(String,usize,usize),
|
||||
#[error("Expected {0}")]
|
||||
ExpectedType(String),
|
||||
}
|
||||
|
||||
#[derive(Error, Debug, PartialEq)]
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ mod symbol_builder;
|
|||
mod tokens;
|
||||
mod value;
|
||||
pub mod vm;
|
||||
mod builtin_functions;
|
||||
|
||||
pub fn compile_sourcedir(source_dir: &str) -> Result<HashMap<String, Chunk>, CrudLangError> {
|
||||
let mut registry = HashMap::new();
|
||||
|
|
|
|||
15
src/main.rs
15
src/main.rs
|
|
@ -34,10 +34,10 @@ async fn main() -> Result<(), CrudLangError> {
|
|||
let args = Args::parse();
|
||||
let source = args.source.unwrap_or("./source".to_string());
|
||||
let registry = compile_sourcedir(&source)?;
|
||||
let empty = registry.is_empty();
|
||||
|
||||
|
||||
if !registry.is_empty() {
|
||||
let swap = Arc::new(ArcSwap::from(Arc::new(registry)));
|
||||
if !empty {
|
||||
if args.watch {
|
||||
crudlang::file_watch::start_watch_daemon(&source, swap.clone());
|
||||
}
|
||||
|
|
@ -63,13 +63,14 @@ async fn main() -> Result<(), CrudLangError> {
|
|||
}
|
||||
|
||||
axum::serve(listener, app).await.map_err(map_underlying())?;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Platform(
|
||||
"No source files found or compilation error".to_string(),
|
||||
))
|
||||
println!("No source files found or compilation error");
|
||||
if args.repl {
|
||||
crudlang::repl::start(swap.clone())?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct AppState {
|
||||
|
|
@ -111,7 +112,7 @@ async fn handle_any(
|
|||
.await
|
||||
{
|
||||
Ok(value) => Ok(Json(value.to_string())),
|
||||
Err(e) => {
|
||||
Err(_) => {
|
||||
// url checks out but function for method not found
|
||||
if state.registry.load().get(&format!("{}.main", component)).is_some() {
|
||||
Err(StatusCode::METHOD_NOT_ALLOWED)
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ use crate::errors::CompilerError;
|
|||
use crate::errors::CompilerError::IncompatibleTypes;
|
||||
use crate::tokens::TokenType::{
|
||||
Bool, DateTime, F32, F64, FloatingPoint, Greater, GreaterEqual, I32, I64, Integer, Less,
|
||||
LessEqual, ListType, MapType, Minus, ObjectType, Plus, SignedInteger, StringType, U32,
|
||||
U64, Unknown, UnsignedInteger,
|
||||
LessEqual, ListType, MapType, Minus, ObjectType, Plus, SignedInteger, StringType, U32, U64,
|
||||
Unknown, UnsignedInteger,
|
||||
};
|
||||
use crate::tokens::{Token, TokenType};
|
||||
use log::debug;
|
||||
|
|
@ -225,6 +225,10 @@ pub fn infer_type(expr: &Expression, symbols: &HashMap<String, Symbol>) -> Token
|
|||
_ => Unknown,
|
||||
}
|
||||
}
|
||||
Expression::MethodCall {
|
||||
receiver,
|
||||
..
|
||||
} => infer_type(receiver, symbols),
|
||||
Expression::Stop { .. } => TokenType::Unknown,
|
||||
// Expression::PathMatch { .. } => TokenType::Unknown,
|
||||
Expression::NamedParameter { .. } => TokenType::Unknown,
|
||||
|
|
|
|||
22
src/vm.rs
22
src/vm.rs
|
|
@ -7,6 +7,7 @@ use arc_swap::Guard;
|
|||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use tracing::debug;
|
||||
use crate::builtin_functions::call_builtin;
|
||||
|
||||
pub struct Vm {
|
||||
ip: usize,
|
||||
|
|
@ -198,6 +199,26 @@ impl Vm {
|
|||
self.push(list.get(index.cast_usize()?).cloned().unwrap())
|
||||
}
|
||||
}
|
||||
OP_CALL_BUILTIN => {
|
||||
let function_name_index = self.read(chunk);
|
||||
let function_name = chunk.constants[function_name_index].to_string();
|
||||
let function_type_index = self.read(chunk);
|
||||
let receiver_type_name = chunk.constants[function_type_index].to_string();
|
||||
|
||||
let receiver = self.pop();
|
||||
|
||||
let num_args = self.read(chunk);
|
||||
|
||||
let mut args = vec![];
|
||||
for _ in 0..num_args {
|
||||
let arg = self.pop();
|
||||
args.push(arg);
|
||||
}
|
||||
args.reverse();
|
||||
|
||||
let return_value = call_builtin(&receiver_type_name, &function_name, receiver, args)?;
|
||||
self.push(return_value);
|
||||
}
|
||||
OP_CALL => {
|
||||
let function_name_index = self.read(chunk);
|
||||
let num_args = self.read(chunk);
|
||||
|
|
@ -346,3 +367,4 @@ pub const OP_DEF_F32: u16 = 39;
|
|||
pub const OP_DEF_F64: u16 = 40;
|
||||
pub const OP_ASSIGN: u16 = 41;
|
||||
pub const OP_LIST_GET: u16 = 42;
|
||||
pub const OP_CALL_BUILTIN: u16 = 43;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue