Skip to content

Commit

Permalink
string methods
Browse files Browse the repository at this point in the history
  • Loading branch information
dvtate committed Dec 12, 2023
1 parent 7211810 commit 4cf5880
Show file tree
Hide file tree
Showing 12 changed files with 175 additions and 30 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,5 @@

cmake-build-debug/
.idea/
build/
.vscode/
57 changes: 57 additions & 0 deletions builtin_libs/xml/parse.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//
// Created by tate on 17/06/2021.
//

#ifndef SCL_PARSE_HPP
#define SCL_PARSE_HPP

#include <optional>

#include "../../vm/vm.hpp"
#include "../../vm/error.hpp"

/// Exported native function
static NativeFunction* parse_nfn;

class XMLParseFn : public NativeFunction {
/// Thrown on invalid XML input
class ParseError : public std::exception {
public:
// Character index
size_t pos;

// Why it's bad
std::string message;

ParseError(size_t pos, std::string message):
pos(pos), message(std::move(message))
{}
};

struct Node {
std::string tag;
std::vector<std::variant<Node, std::string>> members;
std::unordered_map<std::string, std::string> attributes;

Node(std::string tag): tag(std::move(tag)) {}
};


/// Skip whitespaces
static void ws(const std::string& s, size_t& i) {
while (i < s.size() && isspace(s[i]))
if (s[i] != ' ' && s[i] != '\t' && s[i] != '\n' && s[i] != '\r')
return;
else
i++;
}

static std::optional<Node> parse_xml(std::string& s, size_t& i) {
std::deque<Node> tags;

ws(s, i);
if (s[i] != '<') {
throw ParseError(i, "expected <");
}
}
};
10 changes: 10 additions & 0 deletions builtin_libs/xml/spec.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

Node {
tag: string,
children: (Node | string)[],
attributes: { string => string },
}

decode(string) => Node | null
encode(Node) => string

6 changes: 3 additions & 3 deletions examples/async_demo.s
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// Import a native module
let delay = import('./delay.so')
let time = import('libtime.so')

// This is equivalent to JavaScript's window.setTimeout
let set_timeout = (:
let duration = i[0], action = i[1], arg = i[2]
async((:
delay(duration)
time.delay(duration)
action(arg)
))()
)
Expand All @@ -14,7 +14,7 @@ let set_timeout = (:
set_timeout(3000, print, "Hello 1")
set_timeout(2000, (: print("Hello 2") ))
async((:
delay(1000)
time.delay(1000)
print("Hello 3")
))()

Expand Down
18 changes: 12 additions & 6 deletions examples/delay.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Import runtime
#include "../vm/vm.hpp"
#include "../vm/gc/gc.hpp"
#include "../vm/error.hpp"

// g++ delay.cpp ../vm/vm.cpp ../vm/gc/gc.cpp -lpthread -shared -fPIC -o delay.so --std=c++20 -g -Wall -Wextra
// delay :: Num -> Num
// - freeze current thread so others can run
Expand Down Expand Up @@ -28,11 +30,15 @@ class DelayFn : public virtual NativeFunction {
// Get sleep duration from user
using namespace std::chrono_literals;
auto duration = 1ms;
if (f.eval_stack.back().type() == Value::VType::FLOAT)
duration *= std::get<Value::float_t>(f.eval_stack.back().v);
else if (f.eval_stack.back().type() == Value::VType::INT)
duration *= std::get<Value::int_t>(f.eval_stack.back().v);
else {}// TODO typerror
if (f.eval_stack.back().type() == ValueTypes::VType::FLOAT)
duration *= std::get<ValueTypes::float_t>(f.eval_stack.back().v);
else if (f.eval_stack.back().type() == ValueTypes::VType::INT)
duration *= std::get<ValueTypes::int_t>(f.eval_stack.back().v);
else
f.rt->running->throw_error(gen_error_object(
"TypeError",
"delay expected a number",
f));
std::cout <<"dur: " <<duration.count() <<std::endl;

// freeze thread
Expand All @@ -56,6 +62,6 @@ class DelayFn : public virtual NativeFunction {

// Export action simply gives the user an instance of our function
extern "C" void export_action(Frame* f) {
f->eval_stack.back() = Value(::new(GC::alloc<NativeFunction>()) DelayFn());
f->eval_stack.back() = Value(::new(GC::static_alloc<NativeFunction>()) DelayFn());
}

15 changes: 15 additions & 0 deletions examples/methods.scl
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

let s = " abc, 123, you and me "

print(s.split(','))

print("abc,123,you and me".split(','))


print(s = s.trim())
print(s = s.trim())
print(s = s.trim())
print(s = s.trim())
print(s = s.trim())
print(s = s.trim())
print(s = s.trim())
25 changes: 17 additions & 8 deletions vm/bc/exec_bc_instr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "../operators/operators.hpp"
#include "../operators/internal_tools.hpp"
#include "../error.hpp"
#include "../primitive_methods.hpp"

#include "exec_bc_instr.hpp"

Expand Down Expand Up @@ -223,19 +224,27 @@ void exec_bc_instr(Frame& f, BCInstr cmd) {
f.eval_stack.emplace_back(f.gc_make<ValueTypes::obj_t>());
return;

case BCInstr::OPCode::USE_MEM_L:
case BCInstr::OPCode::USE_MEM_L: {
// Object member getter
try {
f.eval_stack.back() = (*std::get<ValueTypes::obj_ref>(f.eval_stack.back().v))
[std::get<ValueTypes::str_t>(std::get<Value>(f.rt->vm->literals[cmd.i].v).v)];
} catch (const std::bad_variant_access& e) {
f.rt->running->throw_error(gen_error_object(
Value& v = f.eval_stack.back();
const auto& str = std::get<ValueTypes::str_t>(std::get<Value>(f.rt->vm->literals[cmd.i].v).v);
switch (v.type()) {
case ValueTypes::VType::OBJ:
f.eval_stack.back() = (*std::get<ValueTypes::obj_ref>(v.v))
[str];
return;
case ValueTypes::VType::EMPTY:
f.rt->running->throw_error(gen_error_object(
"TypeError",
std::string("cannot request member of non-object type - ") + f.eval_stack.back().type_name(),
std::string("cannot accesss property") + str + " of empty",
f));
break;
return;
default:
f.eval_stack.back() = get_primitive_member(f, f.eval_stack.back(), str);

}
return;
}

case BCInstr::OPCode::SET_MEM_L:
// Object member setter
Expand Down
1 change: 1 addition & 0 deletions vm/gc/gc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,5 +151,6 @@ SCL__GC_SPEC(Value, value);
SCL__GC_SPEC(obj_t, obj);
SCL__GC_SPEC(list_t, list);
SCL__GC_SPEC(NativeFunction, nfn);
SCL__GC_SPEC(NativeClosure, nfc);
SCL__GC_SPEC(Closure, closure);
SCL__GC_SPEC(LambdaReturnNativeFn, lamret);
4 changes: 4 additions & 0 deletions vm/gc/gc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ class GarbageCollector {
std::vector<ValueTypes::obj_t*> _recycle_obj;
std::vector<NativeFunction*> _heap_nfn;
std::vector<NativeFunction*> _recycle_nfn;
std::vector<NativeClosure*> _heap_nfc;
std::vector<NativeClosure*> _recycle_nfc;
std::vector<Closure*> _heap_closure;
std::vector<Closure*> _recycle_closure;
std::vector<LambdaReturnNativeFn*> _heap_lamret;
Expand All @@ -118,6 +120,7 @@ class GarbageCollector {
void destroy(ValueTypes::list_t*);
void destroy(ValueTypes::obj_t*);
void destroy(NativeFunction*);
void destroy(NativeClosure*);
void destroy(Closure*);
void destroy(LambdaReturnNativeFn*);
};
Expand Down Expand Up @@ -180,6 +183,7 @@ namespace GC {
template<> ValueTypes::list_t* alloc<ValueTypes::list_t>(GarbageCollector&);
template<> ValueTypes::obj_t* alloc<ValueTypes::obj_t>(GarbageCollector&);
template<> NativeFunction* alloc<NativeFunction>(GarbageCollector&);
template<> NativeClosure* alloc<NativeClosure>(GarbageCollector&);
template<> Closure* alloc<Closure>(GarbageCollector&);
template<> LambdaReturnNativeFn* alloc<LambdaReturnNativeFn>(GarbageCollector&);
}
Expand Down
59 changes: 47 additions & 12 deletions vm/primitive_methods.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "vm.hpp"
#include "value.hpp"
#include "error.hpp"
#include "gc/gc.hpp"

#include "primitive_methods.hpp"

Expand All @@ -15,36 +16,45 @@
*/

// Remove leading and trailing whitespace
class StrTrimFn : public NativeFunction {
std::string str;
class StrTrimFn : public NativeClosure {
public:
explicit StrTrimFn(std::string&& str): str(str) {}
explicit StrTrimFn(std::string str) {
this->data = (void*) new std::string(str);
}
virtual ~StrTrimFn() {
delete (std::string*) this->data;
}

void operator()(Frame& f) override {
// start = index of first non-space character
size_t start = 0;
std::string& str = *(std::string*)this->data;
do {
if (!isspace(this->str[start]))
if (!isspace(str[start]))
break;
} while (++start < this->str.size());
} while (++start < str.size());

// end = index of last non-space character
auto end = this->str.size() - 1;
auto end = str.size() - 1;
do {
if (!isspace(this->str[start]))
if (!isspace(str[end]))
break;
} while (--end > start);

// substring onto stack
f.eval_stack.back() = Value(str.substr(start, end - start));
f.eval_stack.back() = Value(str.substr(start, 1 + end - start));
}
void mark() override {}
};

class StrSplitFn : public NativeFunction {
std::string str;
class StrSplitFn : public NativeClosure {
public:
explicit StrSplitFn(std::string&& str): str(str) {}
explicit StrSplitFn(std::string str) {
this->data = (void*) new std::string(str);
}
virtual ~StrSplitFn() {
delete (std::string*) this->data;
}

void operator()(Frame& f) override {
// Get delimiter
Expand All @@ -62,6 +72,7 @@ class StrSplitFn : public NativeFunction {
size_t last = 0;
size_t next;
auto* ret = f.gc_make<ValueTypes::list_t>();
std::string& str = *(std::string*)this->data;
while ((next = str.find(delimiter, last)) != std::string::npos) {
ret->emplace_back(Value(str.substr(last, next-last)));
last = next + 1;
Expand All @@ -74,7 +85,31 @@ class StrSplitFn : public NativeFunction {
void mark() override {}
};

/* TODO
Str: startsWith, endsWith, substr, find
List: foreach, map, reduce, ?
*/

Value get_primitive_member(Frame& f, Value& v, const std::string& key) {
// TODO
switch (v.type())
{
case ValueTypes::VType::STR:
{
auto s = v.get<std::string>();
if (key == "split")
return Value(::new(f.rt->vm->gc.alloc<NativeClosure>()) StrSplitFn(s));
else if (key == "trim")
return Value(::new(f.rt->vm->gc.alloc<NativeClosure>()) StrTrimFn(s));
else
f.rt->running->throw_error(gen_error_object(
"TypeError",
std::string("cannot accesss property") + key + " of Str",
f));
}
break;

default:
break;
}
return Value();
}
2 changes: 1 addition & 1 deletion vm/primitive_methods.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
#include <string>
#include "value.hpp"

Value get_primitive_member(Frame& f, Value& v, std::string key);
Value get_primitive_member(Frame& f, Value& v, const std::string& key);

#endif //SCL_PRIMITIVE_METHODS_HPP
6 changes: 6 additions & 0 deletions vm/value_types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ class NativeFunction {
virtual void mark() = 0;
};

class NativeClosure : public NativeFunction {
public:
void* data{nullptr}; // Captured data
virtual ~NativeClosure() = default; // child class must define this
};

namespace ValueTypes {
using empty_t = std::monostate;

Expand Down

0 comments on commit 4cf5880

Please sign in to comment.