Compare commits
12 Commits
b628c00f9e
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| d85efb69dc | |||
| a6c3a0e39e | |||
| e4432cbe21 | |||
| 26cb591c49 | |||
| 2ae3086481 | |||
| 38557cdf4c | |||
| 8fea13cc17 | |||
| e0ec910dc2 | |||
| e89d7ffacc | |||
| 3dc53ed43d | |||
| faebaf7d3f | |||
| e3b24b585b |
1
.vim/ftdetect/kyn.vim
Normal file
1
.vim/ftdetect/kyn.vim
Normal file
@@ -0,0 +1 @@
|
|||||||
|
autocmd BufNewFile,BufRead *.kyn setfiletype kyn
|
||||||
52
.vim/indent/kyn.vim
Normal file
52
.vim/indent/kyn.vim
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
" Vim indent file
|
||||||
|
" Language: Kyn
|
||||||
|
" Maintainer: Gemini
|
||||||
|
|
||||||
|
if exists("b:did_indent")
|
||||||
|
finish
|
||||||
|
endif
|
||||||
|
let b:did_indent = 1
|
||||||
|
|
||||||
|
setlocal indentexpr=GetKynIndent()
|
||||||
|
setlocal indentkeys+=={,},0)
|
||||||
|
|
||||||
|
let b:undo_indent = "setlocal indentexpr< indentkeys<"
|
||||||
|
|
||||||
|
" Only define the function once
|
||||||
|
if exists("*GetKynIndent")
|
||||||
|
finish
|
||||||
|
endif
|
||||||
|
|
||||||
|
function GetKynIndent()
|
||||||
|
" Get the line number of the current line
|
||||||
|
let lnum = v:lnum
|
||||||
|
|
||||||
|
" Get the current line
|
||||||
|
let cline = getline(lnum)
|
||||||
|
|
||||||
|
" If the current line has a '}', decrease indent
|
||||||
|
if cline =~ '^\s*}'
|
||||||
|
let lnum = prevnonblank(lnum - 1)
|
||||||
|
return indent(lnum)
|
||||||
|
endif
|
||||||
|
|
||||||
|
" Find the previous non-blank line
|
||||||
|
let lnum = prevnonblank(lnum - 1)
|
||||||
|
|
||||||
|
" At the start of the file, no indent
|
||||||
|
if lnum == 0
|
||||||
|
return 0
|
||||||
|
endif
|
||||||
|
|
||||||
|
" Get the indent of the previous line
|
||||||
|
let prev_indent = indent(lnum)
|
||||||
|
let prev_line = getline(lnum)
|
||||||
|
|
||||||
|
" If the previous line ends with '{', increase indent
|
||||||
|
if prev_line =~ '{\s*$'
|
||||||
|
return prev_indent + &shiftwidth
|
||||||
|
endif
|
||||||
|
|
||||||
|
" Otherwise, keep the same indent
|
||||||
|
return prev_indent
|
||||||
|
endfunction
|
||||||
47
.vim/syntax/kyn.vim
Normal file
47
.vim/syntax/kyn.vim
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
" Vim syntax file
|
||||||
|
" Language: Kyn
|
||||||
|
|
||||||
|
if exists("b:current_syntax")
|
||||||
|
finish
|
||||||
|
endif
|
||||||
|
|
||||||
|
" Keywords
|
||||||
|
syn keyword kynKeyword fun struct let if while return assert is
|
||||||
|
|
||||||
|
" Instructions/Modules
|
||||||
|
syn keyword kynInstruction println print math exit compare input concat split file
|
||||||
|
|
||||||
|
" Comments
|
||||||
|
syn match kynComment /#.*/
|
||||||
|
|
||||||
|
" Strings
|
||||||
|
syn region kynString start=/"/ end=/"/
|
||||||
|
|
||||||
|
" Numbers
|
||||||
|
syn match kynNumber /\d\+\(\.\d\+\)\?/
|
||||||
|
|
||||||
|
" Variables
|
||||||
|
syn match kynVariable /\$[a-zA-Z_][a-zA-Z0-9_]*/
|
||||||
|
|
||||||
|
" Type Placeholders
|
||||||
|
syn match kynType /<[a-zA-Z_][a-zA-Z0-9_]*>/
|
||||||
|
|
||||||
|
" Operators
|
||||||
|
syn match kynOperator /[=+\-*\/^%]/
|
||||||
|
syn match kynOperator /==\|!=\|>=\|<=/
|
||||||
|
|
||||||
|
" Special variables
|
||||||
|
syn keyword kynSpecial self
|
||||||
|
|
||||||
|
" Highlighting links
|
||||||
|
hi def link kynKeyword Keyword
|
||||||
|
hi def link kynInstruction Statement
|
||||||
|
hi def link kynComment Comment
|
||||||
|
hi def link kynString String
|
||||||
|
hi def link kynNumber Number
|
||||||
|
hi def link kynVariable Identifier
|
||||||
|
hi def link kynType Type
|
||||||
|
hi def link kynOperator Operator
|
||||||
|
hi def link kynSpecial Special
|
||||||
|
|
||||||
|
let b:current_syntax = "kyn"
|
||||||
3
Makefile
3
Makefile
@@ -27,8 +27,9 @@ TARGET = kyn
|
|||||||
all: $(TARGET)
|
all: $(TARGET)
|
||||||
|
|
||||||
# Link the object files to create the executable
|
# Link the object files to create the executable
|
||||||
|
# At this point, we do -lcurl to link curl for the request lib
|
||||||
$(TARGET): $(OBJS)
|
$(TARGET): $(OBJS)
|
||||||
$(CXX) $(CXXFLAGS) -o $(TARGET) $(OBJS)
|
$(CXX) $(CXXFLAGS) -lcurl -o $(TARGET) $(OBJS)
|
||||||
|
|
||||||
# Compile .cpp files to .o files in the build directory
|
# Compile .cpp files to .o files in the build directory
|
||||||
$(BUILD_DIR)/%.o: %.cpp
|
$(BUILD_DIR)/%.o: %.cpp
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ Value handleListGet(const Value& subject, const std::vector<Value>& args) {
|
|||||||
return Value();
|
return Value();
|
||||||
}
|
}
|
||||||
Value accessor = args[0];
|
Value accessor = args[0];
|
||||||
if (accessor.valtype == ValueType::String && accessor.string_val == "size") {
|
if (accessor.valtype == ValueType::Identifier && accessor.string_val == "size") {
|
||||||
return Value((long long)subject.list.size());
|
return Value((long long)subject.list.size());
|
||||||
}
|
}
|
||||||
if (accessor.valtype != ValueType::Int) {
|
if (accessor.valtype != ValueType::Int) {
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ Value handleStringGet(const Value& subject, const std::vector<Value>& args) {
|
|||||||
}
|
}
|
||||||
Value accessor = args[0];
|
Value accessor = args[0];
|
||||||
if (accessor.valtype != ValueType::Int) {
|
if (accessor.valtype != ValueType::Int) {
|
||||||
if (accessor.valtype == ValueType::String && accessor.string_val == "size") {
|
if (accessor.valtype == ValueType::Identifier && accessor.string_val == "size") {
|
||||||
return Value((long long)subject.string_val.length());
|
return Value((long long)subject.string_val.length());
|
||||||
}
|
}
|
||||||
error("String accessor must be an integer or \"size\"");
|
error("String accessor must be an integer or \"size\"");
|
||||||
@@ -51,7 +51,7 @@ Value handleStringGet(const Value& subject, const std::vector<Value>& args) {
|
|||||||
error("String index out of bounds");
|
error("String index out of bounds");
|
||||||
return Value();
|
return Value();
|
||||||
}
|
}
|
||||||
return Value(std::string(1, subject.string_val.at(index)));
|
return Value("\"" + std::string(1, subject.string_val.at(index)) + "\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleStringSet(std::string var_name, Value& subject, const Value& index_val, const Value& new_val) {
|
void handleStringSet(std::string var_name, Value& subject, const Value& index_val, const Value& new_val) {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include "../datatypes/lists/lists.h"
|
#include "../datatypes/lists/lists.h"
|
||||||
|
#include "../utils/escapes/escapes.h"
|
||||||
|
|
||||||
InstructionType strToInstructionType(std::string in) {
|
InstructionType strToInstructionType(std::string in) {
|
||||||
if (in == "println") return InstructionType::Println;
|
if (in == "println") return InstructionType::Println;
|
||||||
@@ -22,6 +23,7 @@ InstructionType strToInstructionType(std::string in) {
|
|||||||
else if (in == "file") return InstructionType::File;
|
else if (in == "file") return InstructionType::File;
|
||||||
else if (in == "struct") return InstructionType::Struct;
|
else if (in == "struct") return InstructionType::Struct;
|
||||||
else if (in == "assert") return InstructionType::Assert;
|
else if (in == "assert") return InstructionType::Assert;
|
||||||
|
else if (in == "request") return InstructionType::Request;
|
||||||
else return InstructionType::Variable;
|
else return InstructionType::Variable;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,7 +54,11 @@ Instruction::Instruction(std::vector<Value> toks) {
|
|||||||
} else if (toks[0].valtype == ValueType::Variable) {
|
} else if (toks[0].valtype == ValueType::Variable) {
|
||||||
instruction = InstructionType::Variable;
|
instruction = InstructionType::Variable;
|
||||||
} else {
|
} else {
|
||||||
error("Instruction should be an identifier or variable value");
|
if (inReplMode) {
|
||||||
|
instruction = InstructionType::Variable;
|
||||||
|
} else {
|
||||||
|
error("Instruction should be an identifier or variable value");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (instruction == InstructionType::Variable) {
|
if (instruction == InstructionType::Variable) {
|
||||||
for (const auto& tok : toks) {
|
for (const auto& tok : toks) {
|
||||||
@@ -71,20 +77,20 @@ Instruction::Instruction(std::vector<Value> toks) {
|
|||||||
|
|
||||||
Value::Value(std::string stringval) {
|
Value::Value(std::string stringval) {
|
||||||
// This constructor will attempt to parse the string into the most specific type possible.
|
// This constructor will attempt to parse the string into the most specific type possible.
|
||||||
|
if (stringval.length() >= 2 && stringval.front() == '"' && stringval.back() == '"') {
|
||||||
|
valtype = ValueType::String;
|
||||||
|
string_val = interpretEscapeSequences(stringval.substr(1, stringval.length() - 2));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (stringval.length() > 2 && stringval.front() == '<' && stringval.back() == '>') {
|
if (stringval.length() > 2 && stringval.front() == '<' && stringval.back() == '>') {
|
||||||
valtype = ValueType::TypePlaceholder;
|
valtype = ValueType::TypePlaceholder;
|
||||||
type_placeholder_name = stringval.substr(1, stringval.length() - 2);
|
type_placeholder_name = stringval.substr(1, stringval.length() - 2);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stringval.length() >= 2 && stringval.front() == '"' && stringval.back() == '"') {
|
|
||||||
valtype = ValueType::String;
|
|
||||||
string_val = stringval.substr(1, stringval.length() - 2);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stringval.empty()) {
|
if (stringval.empty()) {
|
||||||
valtype = ValueType::String;
|
valtype = ValueType::None;
|
||||||
string_val = "";
|
string_val = "";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -268,7 +274,7 @@ std::vector<Value> split(std::string line) {
|
|||||||
splitvals.push_back(Value(buf));
|
splitvals.push_back(Value(buf));
|
||||||
}
|
}
|
||||||
buf = "";
|
buf = "";
|
||||||
} else if (chr == '(' || chr == '[') {
|
} else if ((chr == '(' || chr == '[') && !instring) {
|
||||||
if (brackets == 0) {
|
if (brackets == 0) {
|
||||||
bracket_type = chr;
|
bracket_type = chr;
|
||||||
if (!buf.empty()) {
|
if (!buf.empty()) {
|
||||||
@@ -279,7 +285,7 @@ std::vector<Value> split(std::string line) {
|
|||||||
buf += chr;
|
buf += chr;
|
||||||
}
|
}
|
||||||
brackets++;
|
brackets++;
|
||||||
} else if (chr == ')' || chr == ']') {
|
} else if ((chr == ')' || chr == ']') && !instring) {
|
||||||
brackets--;
|
brackets--;
|
||||||
if (brackets == 0) {
|
if (brackets == 0) {
|
||||||
if (!buf.empty()) {
|
if (!buf.empty()) {
|
||||||
|
|||||||
@@ -6,11 +6,11 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
enum class InstructionType {
|
enum class InstructionType {
|
||||||
None, Print, Println, Math, Let, Variable, Exit, If, While, Input, Compare, Function, Struct, Return, Concat, Split, File, Assert
|
None, Print, Println, Math, Let, Variable, Exit, If, While, Input, Compare, Function, Struct, Return, Concat, Split, File, Assert, Request
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class ValueType {
|
enum class ValueType {
|
||||||
Processed, Variable, InstructionGroup, List, Map, String, Int, Double, Identifier, Custom, TypePlaceholder
|
Processed, Variable, InstructionGroup, List, Map, String, Int, Double, Identifier, Custom, TypePlaceholder, None
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Instruction;
|
struct Instruction;
|
||||||
@@ -67,6 +67,7 @@ struct Instruction {
|
|||||||
InstructionType instruction = InstructionType::None;
|
InstructionType instruction = InstructionType::None;
|
||||||
std::vector<Value> args;
|
std::vector<Value> args;
|
||||||
std::string toString() const;
|
std::string toString() const;
|
||||||
|
int lineNum;
|
||||||
Instruction() = default;
|
Instruction() = default;
|
||||||
Instruction(std::vector<std::string> toks);
|
Instruction(std::vector<std::string> toks);
|
||||||
Instruction(std::vector<Value> toks);
|
Instruction(std::vector<Value> toks);
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#include "../modules/split/split.h"
|
#include "../modules/split/split.h"
|
||||||
#include "../modules/concat/concat.h"
|
#include "../modules/concat/concat.h"
|
||||||
#include "../modules/file/file.h"
|
#include "../modules/file/file.h"
|
||||||
|
#include "../modules/request/request.h"
|
||||||
#include "../modules/assert/assert.h"
|
#include "../modules/assert/assert.h"
|
||||||
|
|
||||||
// Forward declaration for mutual recursion
|
// Forward declaration for mutual recursion
|
||||||
@@ -43,6 +44,8 @@ Value execute(Instruction inst) {
|
|||||||
if (inst.instruction == InstructionType::Assert) {
|
if (inst.instruction == InstructionType::Assert) {
|
||||||
return modules::assert(inst.args);
|
return modules::assert(inst.args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle variable operations that require unevaluated arguments
|
||||||
if (inst.instruction == InstructionType::Variable) {
|
if (inst.instruction == InstructionType::Variable) {
|
||||||
auto& args = inst.args;
|
auto& args = inst.args;
|
||||||
|
|
||||||
@@ -116,8 +119,6 @@ Value execute(Instruction inst) {
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Indexed assignment: $var index = value
|
// Indexed assignment: $var index = value
|
||||||
if (args.size() == 4 && args[2].valtype == ValueType::Identifier && args[2].string_val == "=") {
|
if (args.size() == 4 && args[2].valtype == ValueType::Identifier && args[2].string_val == "=") {
|
||||||
std::string var_name;
|
std::string var_name;
|
||||||
@@ -165,14 +166,23 @@ Value execute(Instruction inst) {
|
|||||||
data::modifyValue(var_name, new_val);
|
data::modifyValue(var_name, new_val);
|
||||||
return Value();
|
return Value();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Variable access or indexed get
|
// For all other instructions, evaluate the arguments first
|
||||||
// If args[0] is an Identifier and there are no args, it's a variable access.
|
for(auto& arg : inst.args) {
|
||||||
if (args[0].valtype == ValueType::Identifier && args.empty()) {
|
arg = evaluate(arg);
|
||||||
return data::getValue(args[0].string_val);
|
}
|
||||||
|
|
||||||
|
if (inst.instruction == InstructionType::Variable) {
|
||||||
|
// This part handles variable access and member/method access on evaluated arguments
|
||||||
|
auto& args = inst.args;
|
||||||
|
|
||||||
|
if (args.empty()) {
|
||||||
|
error("Empty variable instruction after evaluation");
|
||||||
|
return Value();
|
||||||
}
|
}
|
||||||
|
|
||||||
Value subject = evaluate(args[0]);
|
Value subject = args[0]; // First arg is already evaluated
|
||||||
std::vector<Value> op_args(args.begin() + 1, args.end());
|
std::vector<Value> op_args(args.begin() + 1, args.end());
|
||||||
|
|
||||||
if (op_args.empty()) {
|
if (op_args.empty()) {
|
||||||
@@ -182,14 +192,14 @@ Value execute(Instruction inst) {
|
|||||||
// Indexed get: subject index
|
// Indexed get: subject index
|
||||||
if (subject.valtype == ValueType::List) {
|
if (subject.valtype == ValueType::List) {
|
||||||
return handleListGet(subject, op_args);
|
return handleListGet(subject, op_args);
|
||||||
} else if (subject.valtype == ValueType::String) {
|
} else if (subject.valtype == ValueType::String) {
|
||||||
return handleStringGet(subject, op_args);
|
return handleStringGet(subject, op_args);
|
||||||
} else if (subject.valtype == ValueType::Map) {
|
} else if (subject.valtype == ValueType::Map) {
|
||||||
if (op_args.empty()) {
|
if (op_args.empty()) {
|
||||||
return subject;
|
return subject;
|
||||||
}
|
}
|
||||||
if (op_args[0].valtype != ValueType::Identifier) {
|
if (op_args[0].valtype != ValueType::Identifier && op_args[0].valtype != ValueType::String) {
|
||||||
error("Struct member name must be an identifier.");
|
error("Struct member name must be an identifier or string.");
|
||||||
return Value();
|
return Value();
|
||||||
}
|
}
|
||||||
std::string member_name = op_args[0].string_val;
|
std::string member_name = op_args[0].string_val;
|
||||||
@@ -211,7 +221,7 @@ Value execute(Instruction inst) {
|
|||||||
data::pushScope();
|
data::pushScope();
|
||||||
data::scopes.back()["self"] = subject;
|
data::scopes.back()["self"] = subject;
|
||||||
for (size_t i = 0; i < func.arg_names.size(); ++i) {
|
for (size_t i = 0; i < func.arg_names.size(); ++i) {
|
||||||
data::scopes.back()[func.arg_names[i]] = evaluate(func_args[i]);
|
data::scopes.back()[func.arg_names[i]] = func_args[i];
|
||||||
}
|
}
|
||||||
Value return_val;
|
Value return_val;
|
||||||
for (const auto& body_inst : func.body) {
|
for (const auto& body_inst : func.body) {
|
||||||
@@ -234,11 +244,6 @@ Value execute(Instruction inst) {
|
|||||||
return Value();
|
return Value();
|
||||||
}
|
}
|
||||||
|
|
||||||
// For all other instructions, evaluate the arguments first
|
|
||||||
for(auto& arg : inst.args) {
|
|
||||||
arg = evaluate(arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then, execute the instruction with the evaluated arguments
|
// Then, execute the instruction with the evaluated arguments
|
||||||
switch (inst.instruction) {
|
switch (inst.instruction) {
|
||||||
case InstructionType::Print:
|
case InstructionType::Print:
|
||||||
@@ -257,6 +262,8 @@ Value execute(Instruction inst) {
|
|||||||
return modules::concat(inst.args);
|
return modules::concat(inst.args);
|
||||||
case InstructionType::File:
|
case InstructionType::File:
|
||||||
return modules::file(inst.args);
|
return modules::file(inst.args);
|
||||||
|
case InstructionType::Request:
|
||||||
|
return modules::request(inst.args);
|
||||||
case InstructionType::Exit:
|
case InstructionType::Exit:
|
||||||
return modules::exit(inst.args);
|
return modules::exit(inst.args);
|
||||||
case InstructionType::Return: {
|
case InstructionType::Return: {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ Value modules::concat(std::vector<Value> args) {
|
|||||||
std::string buf;
|
std::string buf;
|
||||||
for (Value val : args) {
|
for (Value val : args) {
|
||||||
switch (val.valtype) {
|
switch (val.valtype) {
|
||||||
|
case ValueType::Identifier:
|
||||||
case ValueType::String:
|
case ValueType::String:
|
||||||
buf += val.string_val;
|
buf += val.string_val;
|
||||||
break;
|
break;
|
||||||
@@ -17,7 +18,7 @@ Value modules::concat(std::vector<Value> args) {
|
|||||||
buf += std::to_string(val.double_val);
|
buf += std::to_string(val.double_val);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
error("Can only concatenate String, Int, and Double values");
|
error("Can only concatenate String, Int, and Double values. Value given: " + val.toString());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,17 +22,12 @@ Value modules::file(std::vector<Value> args) {
|
|||||||
}
|
}
|
||||||
std::string buf;
|
std::string buf;
|
||||||
std::string retval;
|
std::string retval;
|
||||||
bool notfirst = false;
|
|
||||||
while (getline(file, buf)) {
|
while (getline(file, buf)) {
|
||||||
if (notfirst) {
|
buf += "\n";
|
||||||
buf += "\n";
|
|
||||||
} else {
|
|
||||||
notfirst = true;
|
|
||||||
}
|
|
||||||
retval += buf;
|
retval += buf;
|
||||||
}
|
}
|
||||||
file.close();
|
file.close();
|
||||||
return Value(retval);
|
return Value("\"" + retval + "\"");
|
||||||
} else if (args[0].string_val == "readlines") {
|
} else if (args[0].string_val == "readlines") {
|
||||||
std::ifstream file(args[1].string_val);
|
std::ifstream file(args[1].string_val);
|
||||||
if (!file) {
|
if (!file) {
|
||||||
@@ -42,7 +37,7 @@ Value modules::file(std::vector<Value> args) {
|
|||||||
std::string buf;
|
std::string buf;
|
||||||
std::vector<Value> retval;
|
std::vector<Value> retval;
|
||||||
while (getline(file, buf)) {
|
while (getline(file, buf)) {
|
||||||
retval.push_back(Value(buf));
|
retval.push_back(Value("\"" + buf + "\""));
|
||||||
}
|
}
|
||||||
file.close();
|
file.close();
|
||||||
return Value(retval);
|
return Value(retval);
|
||||||
|
|||||||
116
src/modules/request/request.cpp
Normal file
116
src/modules/request/request.cpp
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
#include "request.h"
|
||||||
|
#include "../../defs/defs.h"
|
||||||
|
#include "../../error/error.h"
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <curl/curl.h>
|
||||||
|
|
||||||
|
enum class RequestType {
|
||||||
|
GET, POST
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* userp) {
|
||||||
|
size_t totalSize = size * nmemb;
|
||||||
|
userp->append((char*)contents, totalSize);
|
||||||
|
return totalSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string curlWrapper(std::string url, RequestType requestType, std::vector<std::string> headers = {"Content-Type: text/plain"}, std::string data = "") {
|
||||||
|
CURL* curl = curl_easy_init();
|
||||||
|
std::string response;
|
||||||
|
|
||||||
|
struct curl_slist *list = NULL;
|
||||||
|
|
||||||
|
if (curl) {
|
||||||
|
for (const std::string& header : headers) {
|
||||||
|
list = curl_slist_append(list, header.c_str());
|
||||||
|
}
|
||||||
|
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_USERAGENT, "kyn-libcurl-agent/1.0");
|
||||||
|
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
|
||||||
|
|
||||||
|
if (requestType == RequestType::POST) {
|
||||||
|
curl_easy_setopt(curl, CURLOPT_POST, 1L);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
CURLcode res = curl_easy_perform(curl);
|
||||||
|
if (res != CURLE_OK) {
|
||||||
|
error("Request failed: " + std::string(curl_easy_strerror(res)));
|
||||||
|
response = "KYNERR";
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value modules::request(std::vector<Value> args) {
|
||||||
|
if (args.size() < 2) {
|
||||||
|
error("Not enough args for request module");
|
||||||
|
return Value("Error");
|
||||||
|
}
|
||||||
|
if (args[1].valtype != ValueType::String) {
|
||||||
|
error("Expecting a string to identify request URL");
|
||||||
|
return Value("Error");
|
||||||
|
}
|
||||||
|
if (args[0].valtype == ValueType::Identifier) {
|
||||||
|
if (args[0].string_val == "get") {
|
||||||
|
if (args.size() == 3) {
|
||||||
|
if (args[2].valtype == ValueType::List) {
|
||||||
|
std::vector<std::string> headers;
|
||||||
|
for (const Value& value : args[2].list) {
|
||||||
|
if (value.valtype == ValueType::String) {
|
||||||
|
headers.push_back(value.string_val);
|
||||||
|
} else {
|
||||||
|
error("Expecting second argument of submodule get to be a list of strings");
|
||||||
|
return Value("Error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::string response = curlWrapper(args[1].string_val, RequestType::GET, headers);
|
||||||
|
if (response == "KYNERR") return Value("Error");
|
||||||
|
return Value(response);
|
||||||
|
} else {
|
||||||
|
error("Expecting second argument of submodule get to be a list of strings");
|
||||||
|
return Value("Error");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::string response = curlWrapper(args[1].string_val, RequestType::GET);
|
||||||
|
if (response == "KYNERR") return Value("Error");
|
||||||
|
return Value(response);
|
||||||
|
}
|
||||||
|
} else if (args[0].string_val == "post") {
|
||||||
|
if (args.size() < 3 || args[2].valtype != ValueType::String) {
|
||||||
|
error("post submodule of request expects a string as 2nd arg");
|
||||||
|
return Value("Error");
|
||||||
|
}
|
||||||
|
if (args.size() == 4) {
|
||||||
|
if (args[3].valtype == ValueType::List) {
|
||||||
|
std::vector<std::string> headers;
|
||||||
|
for (const Value& value : args[3].list) {
|
||||||
|
if (value.valtype == ValueType::String) {
|
||||||
|
headers.push_back(value.string_val);
|
||||||
|
} else {
|
||||||
|
error("Expecting third argument of submodule post to be a list of strings");
|
||||||
|
return Value("Error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::string response = curlWrapper(args[1].string_val, RequestType::POST, headers, args[2].string_val);
|
||||||
|
if (response == "KYNERR") return Value("Error");
|
||||||
|
return Value(response);
|
||||||
|
} else {
|
||||||
|
error("Expecting third argument of submodule post to be a list of strings");
|
||||||
|
return Value("Error");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::string response = curlWrapper(args[1].string_val, RequestType::POST, {"Content-Type: text/plain"}, args[2].string_val);
|
||||||
|
if (response == "KYNERR") return Value("Error");
|
||||||
|
return Value(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
error("Unknown submodule for request. Only 'get' and 'post' are available.");
|
||||||
|
return Value("Error");
|
||||||
|
}
|
||||||
8
src/modules/request/request.h
Normal file
8
src/modules/request/request.h
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include "../../defs/defs.h"
|
||||||
|
|
||||||
|
namespace modules {
|
||||||
|
Value request(std::vector<Value> args);
|
||||||
|
}
|
||||||
@@ -1,8 +1,45 @@
|
|||||||
#include "split.h"
|
#include "split.h"
|
||||||
#include "../../defs/defs.h"
|
#include "../../defs/defs.h"
|
||||||
|
#include "../../error/error.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
Value modules::split(std::vector<Value> args) {
|
Value modules::split(std::vector<Value> args) {
|
||||||
return Value("Work in progress!");
|
if (args.size() < 1) {
|
||||||
|
error("Not enough args for split module");
|
||||||
|
}
|
||||||
|
if (args[0].valtype != ValueType::String) {
|
||||||
|
error("First argument of split must be a string");
|
||||||
|
}
|
||||||
|
std::string delimiter = " ";
|
||||||
|
if (args.size() > 1) {
|
||||||
|
if (args[1].valtype == ValueType::String) {
|
||||||
|
delimiter = args[1].string_val;
|
||||||
|
} else {
|
||||||
|
error("Expected a string delimiter as second argument of split");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string buf;
|
||||||
|
std::vector<Value> list;
|
||||||
|
|
||||||
|
for (const char& chr : args[0].string_val) {
|
||||||
|
buf += chr;
|
||||||
|
|
||||||
|
if (buf.size() >= delimiter.size()) {
|
||||||
|
std::string checker = buf.substr(buf.size() - delimiter.size());
|
||||||
|
|
||||||
|
if (checker == delimiter) {
|
||||||
|
list.push_back(Value("\"" + buf.substr(0, buf.size() - delimiter.size()) + "\""));
|
||||||
|
buf.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!buf.empty()) {
|
||||||
|
list.push_back(Value("\"" + buf + "\""));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return Value(list);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,10 @@ std::vector<Instruction> parse(std::string program) {
|
|||||||
std::vector<std::string> lines;
|
std::vector<std::string> lines;
|
||||||
std::string buf;
|
std::string buf;
|
||||||
int blockTracker = 0;
|
int blockTracker = 0;
|
||||||
|
int lineNum = 0;
|
||||||
|
|
||||||
for (char chr : program) {
|
for (char chr : program) {
|
||||||
|
if (chr == '\n') lineNum ++;
|
||||||
if (chr == '{') {
|
if (chr == '{') {
|
||||||
blockTracker++;
|
blockTracker++;
|
||||||
} else if (chr == '}') {
|
} else if (chr == '}') {
|
||||||
@@ -23,7 +25,7 @@ std::vector<Instruction> parse(std::string program) {
|
|||||||
|
|
||||||
if ((chr == '\n' || chr == ';') && blockTracker == 0) {
|
if ((chr == '\n' || chr == ';') && blockTracker == 0) {
|
||||||
if (!buf.empty()) {
|
if (!buf.empty()) {
|
||||||
lines.push_back(buf);
|
lines.push_back(trim(buf));
|
||||||
buf.clear();
|
buf.clear();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -90,9 +92,18 @@ std::vector<Instruction> parse(std::string program) {
|
|||||||
error("Expected '{' for else statement");
|
error("Expected '{' for else statement");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
size_t else_block_end = remaining_line.find_last_of('}');
|
size_t else_block_end = 0;
|
||||||
if (else_block_end == std::string::npos) {
|
int brace_level = 0;
|
||||||
error("Expected '}' for else statement");
|
for (size_t i = else_block_start; i < remaining_line.length(); ++i) {
|
||||||
|
if (remaining_line[i] == '{') brace_level++;
|
||||||
|
else if (remaining_line[i] == '}') brace_level--;
|
||||||
|
if (brace_level == 0) {
|
||||||
|
else_block_end = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (else_block_end == 0) {
|
||||||
|
error("Mismatched braces in else statement");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
std::string else_content = remaining_line.substr(else_block_start + 1, else_block_end - else_block_start - 1);
|
std::string else_content = remaining_line.substr(else_block_start + 1, else_block_end - else_block_start - 1);
|
||||||
|
|||||||
@@ -5,11 +5,12 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "../data/data.h"
|
#include "../data/data.h"
|
||||||
|
#include "../vendor/linenoise/linenoise.hpp"
|
||||||
|
|
||||||
void repl() {
|
void repl() {
|
||||||
data::initScopes();
|
data::initScopes();
|
||||||
inReplMode = true;
|
inReplMode = true;
|
||||||
std::cout << "Kyn REPL v0.0.1" << std::endl;
|
std::cout << "Kyn REPL v0.0.2" << std::endl;
|
||||||
std::cout << "Type 'exit' to exit" << std::endl;
|
std::cout << "Type 'exit' to exit" << std::endl;
|
||||||
|
|
||||||
std::string full_command;
|
std::string full_command;
|
||||||
@@ -17,14 +18,11 @@ void repl() {
|
|||||||
int brace_level = 0;
|
int brace_level = 0;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (brace_level == 0) {
|
auto prompt = brace_level == 0 ? "kyn> " : ".. > ";
|
||||||
std::cout << "kyn> ";
|
auto quit = linenoise::Readline(prompt, line_buf);
|
||||||
} else {
|
|
||||||
std::cout << ".. > ";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!getline(std::cin, line_buf)) {
|
if (quit) {
|
||||||
break; // Handle EOF (Ctrl+D)
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (char c : line_buf) {
|
for (char c : line_buf) {
|
||||||
@@ -44,9 +42,13 @@ void repl() {
|
|||||||
if (full_command.find_first_not_of(" \t\n\r") != std::string::npos) {
|
if (full_command.find_first_not_of(" \t\n\r") != std::string::npos) {
|
||||||
std::vector<Instruction> parsed = parse(full_command);
|
std::vector<Instruction> parsed = parse(full_command);
|
||||||
for (Instruction inst : parsed) {
|
for (Instruction inst : parsed) {
|
||||||
execute(inst);
|
Value instruction = execute(inst);
|
||||||
|
if (!(instruction.valtype == ValueType::None)) {
|
||||||
|
std::cout << instruction.toString() << std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
linenoise::AddHistory(full_command.c_str());
|
||||||
full_command.clear();
|
full_command.clear();
|
||||||
brace_level = 0;
|
brace_level = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
34
src/utils/escapes/escapes.cpp
Normal file
34
src/utils/escapes/escapes.cpp
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#include "escapes.h"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
std::string interpretEscapeSequences(std::string input) {
|
||||||
|
std::string output;
|
||||||
|
for (size_t i = 0; i < input.length(); ++i) {
|
||||||
|
if (input[i] == '\\' && i + 1 < input.length()) {
|
||||||
|
char next = input[i + 1];
|
||||||
|
switch (next) {
|
||||||
|
case 'n': output += '\n'; break;
|
||||||
|
case 't': output += '\t'; break;
|
||||||
|
case 'r': output += '\r'; break;
|
||||||
|
case 'b': output += '\b'; break;
|
||||||
|
case 'f': output += '\f'; break;
|
||||||
|
case 'a': output += '\a'; break;
|
||||||
|
case 'v': output += '\v'; break;
|
||||||
|
case '\\': output += '\\'; break;
|
||||||
|
case '\'': output += '\''; break;
|
||||||
|
case '\"': output += '\"'; break;
|
||||||
|
case '0': output += '\0'; break;
|
||||||
|
default:
|
||||||
|
output += '\\';
|
||||||
|
output += next;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++i;
|
||||||
|
} else {
|
||||||
|
output += input[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
5
src/utils/escapes/escapes.h
Normal file
5
src/utils/escapes/escapes.h
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
std::string interpretEscapeSequences(std::string input);
|
||||||
@@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
// Helper to trim whitespace from the start and end of a string
|
// Helper to trim whitespace from the start and end of a string
|
||||||
std::string trim(const std::string& str) {
|
std::string trim(const std::string& str) {
|
||||||
size_t first = str.find_first_not_of(" \t\n\r");
|
size_t first = str.find_first_not_of(" \t\n");
|
||||||
if (std::string::npos == first) {
|
if (std::string::npos == first) {
|
||||||
return str;
|
return "";
|
||||||
}
|
}
|
||||||
size_t last = str.find_last_not_of(" \t\n\r");
|
size_t last = str.find_last_not_of(" \t\n");
|
||||||
return str.substr(first, (last - first + 1));
|
return str.substr(first, (last - first + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
0
src/vendor/linenoise/linenoise.h
vendored
Normal file
0
src/vendor/linenoise/linenoise.h
vendored
Normal file
2587
src/vendor/linenoise/linenoise.hpp
vendored
Normal file
2587
src/vendor/linenoise/linenoise.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
let counter = 0
|
let counter = 0
|
||||||
|
|
||||||
while compare $counter <= 1000 {
|
while compare $counter <= 10000 {
|
||||||
println $counter
|
println $counter
|
||||||
counter = (math 1 + $counter)
|
counter = (math 1 + $counter)
|
||||||
}
|
}
|
||||||
|
|||||||
33
tests/test.kyn
Normal file
33
tests/test.kyn
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
println "thing"
|
||||||
|
|
||||||
|
if compare 5 == 10 {
|
||||||
|
println "uh oh its broken"
|
||||||
|
} else {
|
||||||
|
println "its working"
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Dingus {
|
||||||
|
name = <String>
|
||||||
|
age = <Int>
|
||||||
|
fun init name age {
|
||||||
|
self name = name
|
||||||
|
self age = age
|
||||||
|
}
|
||||||
|
|
||||||
|
fun tostring {
|
||||||
|
return (concat name ", " age)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let myDingus = (Dingus "dingus" 23)
|
||||||
|
|
||||||
|
fun myFn param1 param2 andSoOn {
|
||||||
|
println param1
|
||||||
|
return param2
|
||||||
|
}
|
||||||
|
|
||||||
|
println (myFn 123 "hi" 3.14)
|
||||||
|
|
||||||
|
let favNumbers = [7, 14, 21]
|
||||||
|
|
||||||
|
println (file read "README.md")
|
||||||
47
tests/toInt.kyn
Normal file
47
tests/toInt.kyn
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
fun toInt in {
|
||||||
|
assert $in is <String>
|
||||||
|
let retint = 0
|
||||||
|
let counter = 0
|
||||||
|
let power = ($in size)
|
||||||
|
let size = ($in size)
|
||||||
|
while compare $counter < $size {
|
||||||
|
let char = ($in $counter)
|
||||||
|
power = (math $power - 1)
|
||||||
|
if compare $char == "1" {
|
||||||
|
retint = (math $retint + (math 1 * 10 ^ $power))
|
||||||
|
}
|
||||||
|
if compare $char == "2" {
|
||||||
|
retint = (math $retint + (math 2 * 10 ^ $power))
|
||||||
|
}
|
||||||
|
if compare $char == "3" {
|
||||||
|
retint = (math $retint + (math 3 * 10 ^ $power))
|
||||||
|
}
|
||||||
|
if compare $char == "4" {
|
||||||
|
retint = (math $retint + (math 4 * 10 ^ $power))
|
||||||
|
}
|
||||||
|
if compare $char == "5" {
|
||||||
|
retint = (math $retint + (math 5 * 10 ^ $power))
|
||||||
|
}
|
||||||
|
if compare $char == "6" {
|
||||||
|
retint = (math $retint + (math 6 * 10 ^ $power))
|
||||||
|
}
|
||||||
|
if compare $char == "7" {
|
||||||
|
retint = (math $retint + (math 7 * 10 ^ $power))
|
||||||
|
}
|
||||||
|
if compare $char == "8" {
|
||||||
|
retint = (math $retint + (math 8 * 10 ^ $power))
|
||||||
|
}
|
||||||
|
if compare $char == "9" {
|
||||||
|
retint = (math $retint + (math 9 * 10 ^ $power))
|
||||||
|
}
|
||||||
|
if compare $char == "0" {
|
||||||
|
# whole lotta nothing
|
||||||
|
}
|
||||||
|
counter = (math $counter + 1)
|
||||||
|
|
||||||
|
}
|
||||||
|
return $retint
|
||||||
|
}
|
||||||
|
let myInt = (toInt "4738927643289")
|
||||||
|
|
||||||
|
println $myInt
|
||||||
Reference in New Issue
Block a user