mirror of
https://git.sfja.dk/Mikkel/slige.git
synced 2025-01-18 18:16:31 +00:00
buildins and other stuff
This commit is contained in:
parent
a7bf4ac701
commit
d9d08dc2ed
@ -40,9 +40,17 @@ export type Builtins = typeof Builtins;
|
||||
export const Builtins = {
|
||||
StringConcat: 0x10,
|
||||
StringEqual: 0x11,
|
||||
ArraySet: 0x20,
|
||||
StringCharAt: 0x12,
|
||||
StringLength: 0x13,
|
||||
StringPushChar: 0x14,
|
||||
ArrayNew: 0x20,
|
||||
ArraySet: 0x21,
|
||||
ArrayPush: 0x22,
|
||||
ArrayAt: 0x23,
|
||||
ArrayLength: 0x24,
|
||||
StructSet: 0x30,
|
||||
Print: 0x40,
|
||||
|
||||
} as const;
|
||||
|
||||
export function opToString(op: number): string {
|
||||
@ -112,16 +120,14 @@ export function opToString(op: number): string {
|
||||
|
||||
export function builtinToString(builtin: number): string {
|
||||
switch (builtin) {
|
||||
case Builtins.StringConcat:
|
||||
return "StringConcat";
|
||||
case Builtins.StringEqual:
|
||||
return "StringEqual";
|
||||
case Builtins.ArraySet:
|
||||
return "ArraySet";
|
||||
case Builtins.StructSet:
|
||||
return "StructSet";
|
||||
case Builtins.Print:
|
||||
return "Print";
|
||||
case Builtins.StringConcat: return "StringConcat";
|
||||
case Builtins.StringEqual: return "StringEqual";
|
||||
case Builtins.StringCharAt: return "StringCharAt";
|
||||
case Builtins.StringLength: return "StringLength";
|
||||
case Builtins.StringPushChar: return "StringPushChar";
|
||||
case Builtins.ArraySet: return "ArraySet";
|
||||
case Builtins.StructSet: return "StructSet";
|
||||
case Builtins.Print: return "Print";
|
||||
default:
|
||||
return `<unknown Builtin ${builtin}>`;
|
||||
}
|
||||
|
@ -114,7 +114,7 @@ export class Assembler {
|
||||
}).join(", ");
|
||||
console.log(`${ip.toString().padStart(8, " ")}: ${op} ${args}`);
|
||||
ip += line.ins.map((lit) =>
|
||||
typeof lit === "string" ? lit.length : 1
|
||||
typeof lit === "string" ? lit.length + 1 : 1
|
||||
).reduce((acc, curr) => acc + curr, 0);
|
||||
}
|
||||
}
|
||||
|
@ -119,16 +119,17 @@ export class Checker {
|
||||
if (stmt.kind.vtype!.type !== "fn") {
|
||||
throw new Error();
|
||||
}
|
||||
const { returnType } = stmt.kind.vtype!;
|
||||
this.fnReturnStack.push(returnType);
|
||||
|
||||
const isBuiltin = stmt.kind.anno && stmt.kind.anno.ident === "builtin";
|
||||
if (isBuiltin) {
|
||||
stmt.kind.body.kind = { type: "block", stmts: [] };
|
||||
return;
|
||||
}
|
||||
|
||||
const { returnType } = stmt.kind.vtype!;
|
||||
this.fnReturnStack.push(returnType);
|
||||
const body = this.checkExpr(stmt.kind.body);
|
||||
|
||||
this.fnReturnStack.pop();
|
||||
|
||||
if (!vtypesEqual(returnType, body)) {
|
||||
this.report(
|
||||
`incompatible return type` +
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { Reporter } from "./info.ts";
|
||||
import { Pos, Token } from "./token.ts";
|
||||
|
||||
|
||||
export class Lexer {
|
||||
private index = 0;
|
||||
private line = 1;
|
||||
|
@ -128,10 +128,42 @@ export class Lowerer {
|
||||
}
|
||||
const value = anno.kind.value;
|
||||
switch (value) {
|
||||
case "print": {
|
||||
case "Print": {
|
||||
this.program.add(Ops.Builtin, Builtins.Print);
|
||||
break;
|
||||
}
|
||||
case "StringLength": {
|
||||
this.program.add(Ops.Builtin, Builtins.StringLength);
|
||||
break;
|
||||
}
|
||||
case "StringCharAt": {
|
||||
this.program.add(Ops.Builtin, Builtins.StringCharAt);
|
||||
break;
|
||||
}
|
||||
case "StringPushChar": {
|
||||
this.program.add(Ops.Builtin, Builtins.StringPushChar);
|
||||
break;
|
||||
}
|
||||
case "ArrayNew": {
|
||||
this.program.add(Ops.Builtin, Builtins.ArrayNew);
|
||||
break;
|
||||
}
|
||||
case "ArraySet": {
|
||||
this.program.add(Ops.Builtin, Builtins.ArraySet);
|
||||
break;
|
||||
}
|
||||
case "ArrayPush": {
|
||||
this.program.add(Ops.Builtin, Builtins.ArrayPush);
|
||||
break;
|
||||
}
|
||||
case "ArrayLength": {
|
||||
this.program.add(Ops.Builtin, Builtins.ArrayLength);
|
||||
break;
|
||||
}
|
||||
case "ArrayAt": {
|
||||
this.program.add(Ops.Builtin, Builtins.ArrayAt);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new Error(
|
||||
`unrecognized builtin '${value}'`,
|
||||
@ -279,6 +311,9 @@ export class Lowerer {
|
||||
case "+":
|
||||
this.program.add(Ops.Add);
|
||||
return;
|
||||
case "-":
|
||||
this.program.add(Ops.Subtract);
|
||||
return;
|
||||
case "*":
|
||||
this.program.add(Ops.Multiply);
|
||||
return;
|
||||
|
@ -429,7 +429,7 @@ export class Parser {
|
||||
left = this.parBinTail(left, pos, this.parseMulDiv, "+");
|
||||
continue;
|
||||
}
|
||||
if (this.test(".")) {
|
||||
if (this.test("-")) {
|
||||
left = this.parBinTail(left, pos, this.parseMulDiv, "-");
|
||||
continue;
|
||||
}
|
||||
|
53
examples/std.slg
Normal file
53
examples/std.slg
Normal file
@ -0,0 +1,53 @@
|
||||
|
||||
fn array_new_string() -> [string] #[builtin(ArrayNew)] {}
|
||||
|
||||
fn print(msg: string) #[builtin(Print)] {}
|
||||
|
||||
fn array_push_string(array: [string], str: string) #[builtin(ArrayPush)] {}
|
||||
|
||||
fn string_push_char(str: string, value: int) #[builtin(StringPushChar)] {}
|
||||
|
||||
fn string_char_at(str: string, index: int) -> int #[builtin(StringCharAt)] {}
|
||||
|
||||
fn string_length(str: string) -> int #[builtin(StringLength)] {}
|
||||
|
||||
fn array_string_length(array: [string]) -> int #[builtin(StringLength)] {}
|
||||
|
||||
fn array_string_at(array: [string], index: int) -> string #[builtin(ArrayAt)] {}
|
||||
|
||||
fn char(ch: string) -> int {
|
||||
string_char_at(ch, 0)
|
||||
}
|
||||
|
||||
fn split(str: string, seperator: int) -> [string] {
|
||||
let result: [string] = array_new_string();
|
||||
|
||||
let i = 0;
|
||||
let current_str = "";
|
||||
loop {
|
||||
let char = string_char_at(str, i);
|
||||
if char == seperator {
|
||||
array_push_string(result, current_str);
|
||||
current_str = "";
|
||||
} else {
|
||||
string_push_char(current_str, char);
|
||||
}
|
||||
if string_length(str) - 1 == i {
|
||||
break;
|
||||
}
|
||||
i = i + 1;
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let array = split("aoisfjasoifjsaiofjsa", char("a"));
|
||||
let i = 0;
|
||||
loop {
|
||||
print(array_string_at(array, i) + "\n");
|
||||
i = i + 1;
|
||||
if array_string_length(array) - 1 == i {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@ -33,6 +33,10 @@ template <> struct AllocTypeType<AllocType::Struct> { using Type = Struct; };
|
||||
// clang-format on
|
||||
|
||||
struct AllocItem {
|
||||
AllocItem(Value&& val) : type(AllocType::Value), value(val) {}
|
||||
AllocItem(Array&& val) : type(AllocType::Array), value(val) {}
|
||||
AllocItem(Struct&& val) : type(AllocType::Struct), value(val) {}
|
||||
|
||||
template <AllocType AT> inline auto as() & -> AllocTypeType<AT>::Type&
|
||||
{
|
||||
return std::get<typename AllocTypeType<AT>::Type>(this->value);
|
||||
@ -54,6 +58,26 @@ struct AllocItem {
|
||||
std::get<typename AllocTypeType<AT>::Type>(this->value));
|
||||
}
|
||||
|
||||
inline auto as_value() & -> Value& {
|
||||
return std::get<Value>(this->value);
|
||||
}
|
||||
inline auto as_value() const & -> const Value& {
|
||||
return std::get<Value>(this->value);
|
||||
}
|
||||
inline auto as_array() & -> Array& {
|
||||
return std::get<Array>(this->value);
|
||||
}
|
||||
inline auto as_array() const & -> const Array& {
|
||||
return std::get<Array>(this->value);
|
||||
}
|
||||
inline auto as_struct() & -> Struct& {
|
||||
return std::get<Struct>(this->value);
|
||||
}
|
||||
inline auto as_struct() const & -> const Struct& {
|
||||
return std::get<Struct>(this->value);
|
||||
}
|
||||
|
||||
|
||||
AllocType type;
|
||||
std::variant<Value, Array, Struct> value;
|
||||
};
|
||||
@ -65,16 +89,23 @@ enum class ErrType {
|
||||
|
||||
template <typename T> struct Res {
|
||||
Res(T val)
|
||||
: val(std::forward<T>(val))
|
||||
: m_val(std::forward<T>(val))
|
||||
{
|
||||
}
|
||||
Res(ErrType err)
|
||||
: err(err)
|
||||
: m_err(err)
|
||||
{
|
||||
}
|
||||
|
||||
std::optional<T> val;
|
||||
std::optional<ErrType> err;
|
||||
auto ok() const -> bool { return m_val.has_value(); }
|
||||
auto val() & -> T& { return m_val.value(); }
|
||||
auto val() const& -> const T& { return m_val.value(); }
|
||||
auto val() && -> T&& { return std::move(m_val.value()); }
|
||||
auto val() const&& -> const T&& { return std::move(m_val.value()); }
|
||||
auto err() -> ErrType { return m_err.value(); }
|
||||
|
||||
std::optional<T> m_val;
|
||||
std::optional<ErrType> m_err;
|
||||
};
|
||||
|
||||
class Heap {
|
||||
@ -124,9 +155,9 @@ private:
|
||||
inline void move_item_to_other(uint32_t ptr)
|
||||
{
|
||||
auto res = at(ptr);
|
||||
if (!res.val.has_value())
|
||||
if (!res.ok())
|
||||
return;
|
||||
auto val = res.val.value();
|
||||
auto val = res.val();
|
||||
switch (val->type) {
|
||||
case AllocType::Value: {
|
||||
auto& v = val->as<AllocType::Value>();
|
||||
|
@ -41,7 +41,14 @@ enum class Op : uint32_t {
|
||||
enum class Builtin : uint32_t {
|
||||
StringConcat = 0x10,
|
||||
StringEqual = 0x11,
|
||||
ArraySet = 0x20,
|
||||
StringCharAt = 0x12,
|
||||
StringLength = 0x13,
|
||||
StringPushChar = 0x14,
|
||||
ArrayNew = 0x20,
|
||||
ArraySet = 0x21,
|
||||
ArrayPush = 0x22,
|
||||
ArrayAt = 0x23,
|
||||
ArrayLength = 0x24,
|
||||
StructSet = 0x30,
|
||||
Print = 0x40,
|
||||
};
|
||||
|
@ -170,36 +170,7 @@ enum class TokTyp {
|
||||
Colon,
|
||||
};
|
||||
|
||||
inline auto tok_typ_to_string(TokTyp typ) -> std::string
|
||||
{
|
||||
switch (typ) {
|
||||
case TokTyp::Eof:
|
||||
return "Eof";
|
||||
case TokTyp::String:
|
||||
return "String";
|
||||
case TokTyp::Float:
|
||||
return "Float";
|
||||
case TokTyp::False:
|
||||
return "False";
|
||||
case TokTyp::True:
|
||||
return "True";
|
||||
case TokTyp::Null:
|
||||
return "Null";
|
||||
case TokTyp::LBrace:
|
||||
return "LBrace";
|
||||
case TokTyp::RBrace:
|
||||
return "RBrace";
|
||||
case TokTyp::LBracket:
|
||||
return "LBracket";
|
||||
case TokTyp::RBracket:
|
||||
return "RBracket";
|
||||
case TokTyp::Comma:
|
||||
return "Comma";
|
||||
case TokTyp::Colon:
|
||||
return "Colon";
|
||||
}
|
||||
std::unreachable();
|
||||
}
|
||||
auto tok_typ_to_string(TokTyp typ) -> std::string;
|
||||
|
||||
struct Tok {
|
||||
Tok(TokTyp typ, Pos pos)
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
bool print_stack_debug = false;
|
||||
bool print_stack_debug = true;
|
||||
|
||||
int execute_file_and_exit(std::string filename)
|
||||
{
|
||||
|
76
runtime/to_string.cpp
Normal file
76
runtime/to_string.cpp
Normal file
@ -0,0 +1,76 @@
|
||||
#include "vm.hpp"
|
||||
#include "json.hpp"
|
||||
|
||||
using namespace sliger;
|
||||
|
||||
auto sliger::maybe_op_to_string(uint32_t value) -> std::string
|
||||
{
|
||||
switch (static_cast<Op>(value)) {
|
||||
/* clang-format off */
|
||||
case Op::Nop: return "Nop";
|
||||
case Op::PushNull: return "PushNull";
|
||||
case Op::PushInt: return "PushInt";
|
||||
case Op::PushBool: return "PushBool";
|
||||
case Op::PushString: return "PushString";
|
||||
case Op::PushPtr: return "PushPtr";
|
||||
case Op::Pop: return "Pop";
|
||||
case Op::ReserveStatic: return "ReserveStatic";
|
||||
case Op::LoadStatic: return "LoadStatic";
|
||||
case Op::StoreStatic: return "StoreStatic";
|
||||
case Op::LoadLocal: return "LoadLocal";
|
||||
case Op::StoreLocal: return "StoreLocal";
|
||||
case Op::Call: return "Call";
|
||||
case Op::Return: return "Return";
|
||||
case Op::Jump: return "Jump";
|
||||
case Op::JumpIfTrue: return "JumpIfTrue";
|
||||
case Op::Builtin: return "Builtin";
|
||||
case Op::Add: return "Add";
|
||||
case Op::Subtract: return "Subtract";
|
||||
case Op::Multiply: return "Multiply";
|
||||
case Op::Divide: return "Divide";
|
||||
case Op::Remainder: return "Remainder";
|
||||
case Op::Equal: return "Equal";
|
||||
case Op::LessThan: return "LessThan";
|
||||
case Op::And: return "And";
|
||||
case Op::Or: return "Or";
|
||||
case Op::Xor: return "Xor";
|
||||
case Op::Not: return "Not";
|
||||
case Op::SourceMap: return "SourceMap";
|
||||
/* clang-format on */
|
||||
default:
|
||||
return std::to_string(value);
|
||||
}
|
||||
}
|
||||
|
||||
auto json::tok_typ_to_string(json::TokTyp typ) -> std::string
|
||||
{
|
||||
using namespace json;
|
||||
|
||||
switch (typ) {
|
||||
case TokTyp::Eof:
|
||||
return "Eof";
|
||||
case TokTyp::String:
|
||||
return "String";
|
||||
case TokTyp::Float:
|
||||
return "Float";
|
||||
case TokTyp::False:
|
||||
return "False";
|
||||
case TokTyp::True:
|
||||
return "True";
|
||||
case TokTyp::Null:
|
||||
return "Null";
|
||||
case TokTyp::LBrace:
|
||||
return "LBrace";
|
||||
case TokTyp::RBrace:
|
||||
return "RBrace";
|
||||
case TokTyp::LBracket:
|
||||
return "LBracket";
|
||||
case TokTyp::RBracket:
|
||||
return "RBracket";
|
||||
case TokTyp::Comma:
|
||||
return "Comma";
|
||||
case TokTyp::Colon:
|
||||
return "Colon";
|
||||
}
|
||||
std::unreachable();
|
||||
}
|
100
runtime/vm.cpp
100
runtime/vm.cpp
@ -10,45 +10,6 @@
|
||||
|
||||
using namespace sliger;
|
||||
|
||||
inline auto maybe_op_to_string(uint32_t value) -> std::string
|
||||
{
|
||||
switch (static_cast<Op>(value)) {
|
||||
/* clang-format off */
|
||||
case Op::Nop: return "Nop";
|
||||
case Op::PushNull: return "PushNull";
|
||||
case Op::PushInt: return "PushInt";
|
||||
case Op::PushBool: return "PushBool";
|
||||
case Op::PushString: return "PushString";
|
||||
case Op::PushPtr: return "PushPtr";
|
||||
case Op::Pop: return "Pop";
|
||||
case Op::ReserveStatic: return "ReserveStatic";
|
||||
case Op::LoadStatic: return "LoadStatic";
|
||||
case Op::StoreStatic: return "StoreStatic";
|
||||
case Op::LoadLocal: return "LoadLocal";
|
||||
case Op::StoreLocal: return "StoreLocal";
|
||||
case Op::Call: return "Call";
|
||||
case Op::Return: return "Return";
|
||||
case Op::Jump: return "Jump";
|
||||
case Op::JumpIfTrue: return "JumpIfTrue";
|
||||
case Op::Builtin: return "Builtin";
|
||||
case Op::Add: return "Add";
|
||||
case Op::Subtract: return "Subtract";
|
||||
case Op::Multiply: return "Multiply";
|
||||
case Op::Divide: return "Divide";
|
||||
case Op::Remainder: return "Remainder";
|
||||
case Op::Equal: return "Equal";
|
||||
case Op::LessThan: return "LessThan";
|
||||
case Op::And: return "And";
|
||||
case Op::Or: return "Or";
|
||||
case Op::Xor: return "Xor";
|
||||
case Op::Not: return "Not";
|
||||
case Op::SourceMap: return "SourceMap";
|
||||
/* clang-format on */
|
||||
default:
|
||||
return std::to_string(value);
|
||||
}
|
||||
}
|
||||
|
||||
void VM::run_until_done()
|
||||
{
|
||||
while (!done()) {
|
||||
@ -164,8 +125,11 @@ void VM::run_instruction()
|
||||
stack_push(Ptr { .value = this->bp });
|
||||
this->pc = fn_ptr.as_ptr().value;
|
||||
this->bp = static_cast<uint32_t>(this->stack.size());
|
||||
for (size_t i = arguments.size(); i > 0; --i) {
|
||||
stack_push(std::move(arguments.at(i - 1)));
|
||||
// for (size_t i = arguments.size(); i > 0; --i) {
|
||||
// stack_push(std::move(arguments.at(i - 1)));
|
||||
// }
|
||||
for (size_t i = 0; i < arguments.size(); ++i) {
|
||||
stack_push(std::move(arguments.at(i)));
|
||||
}
|
||||
if (this->opts.flame_graph) {
|
||||
this->flame_graph.report_call(
|
||||
@ -348,5 +312,59 @@ void VM::run_builtin(Builtin builtin_id)
|
||||
stack_push(Null());
|
||||
break;
|
||||
}
|
||||
case Builtin::StringCharAt: {
|
||||
assert_stack_has(2);
|
||||
auto str = stack_pop();
|
||||
auto index_value = stack_pop();
|
||||
auto index = static_cast<size_t>(index_value.as_int().value);
|
||||
auto ch = static_cast<int32_t>(str.as_string().value.at(index));
|
||||
stack_push(Int(ch));
|
||||
break;
|
||||
}
|
||||
case Builtin::StringLength: {
|
||||
assert_stack_has(1);
|
||||
auto str = stack_pop().as_string().value;
|
||||
auto length = static_cast<int32_t>(str.length());
|
||||
stack_push(Int(length));
|
||||
break;
|
||||
}
|
||||
case Builtin::StringPushChar: {
|
||||
assert_stack_has(2);
|
||||
auto str = stack_pop();
|
||||
auto ch = stack_pop();
|
||||
auto new_str = std::string(str.as_string().value);
|
||||
new_str.push_back(static_cast<char>(ch.as_int().value));
|
||||
stack_push(String(new_str));
|
||||
break;
|
||||
}
|
||||
case Builtin::ArrayNew: {
|
||||
auto alloc_res = this->heap.alloc<heap::AllocType::Array>();
|
||||
stack_push(Ptr(alloc_res.val()));
|
||||
break;
|
||||
}
|
||||
case Builtin::ArrayPush: {
|
||||
assert_stack_has(2);
|
||||
auto array_ptr = stack_pop().as_ptr().value;
|
||||
auto value = stack_pop();
|
||||
auto array = this->heap.at(array_ptr).val()->as_array();
|
||||
array.values.push_back(value);
|
||||
stack_push(Null());
|
||||
break;
|
||||
}
|
||||
case Builtin::ArrayAt: {
|
||||
assert_stack_has(2);
|
||||
auto array_ptr = stack_pop().as_ptr().value;
|
||||
auto index = stack_pop().as_int().value;
|
||||
auto array = this->heap.at(array_ptr).val()->as_array();
|
||||
stack_push(array.values.at(static_cast<size_t>(index)));
|
||||
break;
|
||||
}
|
||||
case Builtin::ArrayLength: {
|
||||
assert_stack_has(1);
|
||||
auto array_ptr = stack_pop().as_ptr().value;
|
||||
auto array = this->heap.at(array_ptr).val()->as_array();
|
||||
stack_push(Int(static_cast<int32_t>(array.values.size())));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -286,4 +286,6 @@ private:
|
||||
CodeCoverageBuilder code_coverage;
|
||||
};
|
||||
|
||||
auto maybe_op_to_string(uint32_t value) -> std::string;
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user