Rewrite
This commit is contained in:
+7
-1
@@ -1,2 +1,8 @@
|
|||||||
|
a.out
|
||||||
|
coverage_html/
|
||||||
|
test
|
||||||
|
test.exe
|
||||||
|
test_cov
|
||||||
wl
|
wl
|
||||||
*.exe
|
wl.exe
|
||||||
|
wl_cov
|
||||||
@@ -1,5 +1,29 @@
|
|||||||
all: WL.c
|
|
||||||
gcc WL.c main.c -o wl -Wall -Wextra -O0 -g3
|
|
||||||
|
|
||||||
WL.c: $(wildcard src/*.c src/*.h)
|
.PHONY: all coverage coverage_report install uninstall
|
||||||
python amalg.py
|
|
||||||
|
all: wl
|
||||||
|
|
||||||
|
wl: wl.c wl.h main.c
|
||||||
|
gcc main.c wl.c -o wl -g3 -O0
|
||||||
|
|
||||||
|
test: wl.c wl.h test.c
|
||||||
|
gcc test.c wl.c -o test -g3 -O0
|
||||||
|
|
||||||
|
coverage:
|
||||||
|
gcc main.c wl.c -o wl_cov -g3 -O0 --coverage -fprofile-arcs -ftest-coverage
|
||||||
|
gcc test.c wl.c -o test_cov -g3 -O0 --coverage -fprofile-arcs -ftest-coverage
|
||||||
|
|
||||||
|
coverage_report:
|
||||||
|
gcov wl.c main.c test.c
|
||||||
|
lcov --capture --directory . --output-file coverage.info
|
||||||
|
lcov --remove coverage.info '/usr/*' --output-file coverage_filtered.info
|
||||||
|
genhtml coverage_filtered.info --output-directory coverage_html --title "WL Template Engine Coverage"
|
||||||
|
|
||||||
|
install: wl
|
||||||
|
sudo cp wl /usr/local/bin
|
||||||
|
|
||||||
|
uninstall:
|
||||||
|
sudo rm /usr/local/bin/wl
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm *.gcda *.gcno *.info
|
||||||
|
|||||||
@@ -1,59 +1,10 @@
|
|||||||
# WL
|
# WL
|
||||||
WL is the proof of concept for a powerful and flexible templating language for the web. All functionality is implemented, but is still missing some polish.
|
WL is an HTML templating engine written in and for C.
|
||||||
|
|
||||||
Here's an example template in WL with a recursive comment view element:
|
It supports:
|
||||||
```html
|
1. Zero dependencies
|
||||||
let posts = [
|
2. Single-file implementation
|
||||||
{
|
3. HTML-first design
|
||||||
author: "UserA",
|
4. Complete scripting language
|
||||||
title: "I'm the first post",
|
5. Easy integration
|
||||||
content: "Sup everyone!",
|
6. Performs no I/O or dynamic allocations
|
||||||
date: "1 Jan 2025",
|
|
||||||
comments: [
|
|
||||||
{
|
|
||||||
author: "UserB",
|
|
||||||
content: "Hello! This is a comment!",
|
|
||||||
comments: [
|
|
||||||
{
|
|
||||||
author: "UserA",
|
|
||||||
content: "Hello to you!",
|
|
||||||
comments: []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
fun comment(c)
|
|
||||||
<div>
|
|
||||||
<a>\c.author</a>
|
|
||||||
<p>\c.content</p>
|
|
||||||
\for subc in c.comments:
|
|
||||||
comment(subc)
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>List of posts</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
\for post in posts:
|
|
||||||
<div class="post">
|
|
||||||
<h2>
|
|
||||||
\post.title
|
|
||||||
</h2>
|
|
||||||
<span>
|
|
||||||
Posted on \post.date by \post.author
|
|
||||||
</span>
|
|
||||||
<p>
|
|
||||||
\post.content
|
|
||||||
</p>
|
|
||||||
<div class="comments">
|
|
||||||
\for c in post.comments:
|
|
||||||
comment(c)
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
@@ -1,20 +1,7 @@
|
|||||||
#ifndef WL_AMALGAMATION
|
#include <stdint.h>
|
||||||
#define WL_AMALGAMATION
|
|
||||||
|
|
||||||
// This file was generated automatically. Do not modify directly!
|
typedef struct WL_Runtime WL_Runtime;
|
||||||
|
typedef struct WL_Compiler WL_Compiler;
|
||||||
////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// src/compile.h
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#ifndef WL_PUBLIC_INCLUDED
|
|
||||||
#define WL_PUBLIC_INCLUDED
|
|
||||||
|
|
||||||
#ifndef WL_AMALGAMATION
|
|
||||||
#include "includes.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct WL_State WL_State;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char *ptr;
|
char *ptr;
|
||||||
@@ -24,70 +11,79 @@ typedef struct {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
char *ptr;
|
char *ptr;
|
||||||
int len;
|
int len;
|
||||||
} WL_Program;
|
int cur;
|
||||||
|
} WL_Arena;
|
||||||
typedef enum {
|
|
||||||
WL_DONE,
|
|
||||||
WL_ERROR,
|
|
||||||
WL_VAR,
|
|
||||||
WL_CALL,
|
|
||||||
WL_OUTPUT,
|
|
||||||
} WL_ResultType;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
WL_ResultType type;
|
|
||||||
WL_String str;
|
|
||||||
} WL_Result;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char *ptr;
|
char *ptr;
|
||||||
int len;
|
int len;
|
||||||
int cur;
|
} WL_Program;
|
||||||
} WL_Arena;
|
|
||||||
|
|
||||||
typedef struct WL_Compiler WL_Compiler;
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
WL_COMPILE_RESULT_DONE,
|
WL_ADD_ERROR,
|
||||||
WL_COMPILE_RESULT_FILE,
|
WL_ADD_AGAIN,
|
||||||
WL_COMPILE_RESULT_ERROR,
|
WL_ADD_LINK,
|
||||||
} WL_CompileResultType;
|
} WL_AddResultType;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
WL_CompileResultType type;
|
WL_AddResultType type;
|
||||||
WL_Program program;
|
WL_String path;
|
||||||
WL_String path;
|
} WL_AddResult;
|
||||||
} WL_CompileResult;
|
|
||||||
|
|
||||||
WL_Compiler* WL_Compiler_init (WL_Arena *arena);
|
typedef enum {
|
||||||
void WL_Compiler_free (WL_Compiler *compiler);
|
WL_EVAL_NONE,
|
||||||
WL_CompileResult WL_compile (WL_Compiler *compiler, WL_String file, WL_String content);
|
WL_EVAL_DONE,
|
||||||
WL_State* WL_State_init (WL_Arena *a, WL_Program p, char *err, int errmax);
|
WL_EVAL_ERROR,
|
||||||
void WL_State_free (WL_State *state);
|
WL_EVAL_OUTPUT,
|
||||||
void WL_State_trace (WL_State *state, int trace);
|
WL_EVAL_SYSVAR,
|
||||||
WL_Result WL_eval (WL_State *state);
|
WL_EVAL_SYSCALL,
|
||||||
|
} WL_EvalResultType;
|
||||||
|
|
||||||
void WL_dump_program(WL_Program program);
|
typedef struct {
|
||||||
|
WL_EvalResultType type;
|
||||||
|
WL_String str;
|
||||||
|
} WL_EvalResult;
|
||||||
|
|
||||||
int WL_streq (WL_String a, char *b, int blen);
|
WL_Compiler* wl_compiler_init (WL_Arena *arena);
|
||||||
int WL_peeknone (WL_State *state, int off);
|
WL_AddResult wl_compiler_add (WL_Compiler *compiler, WL_String content);
|
||||||
int WL_peekint (WL_State *state, int off, long long *x);
|
int wl_compiler_link (WL_Compiler *compiler, WL_Program *program);
|
||||||
int WL_peekfloat (WL_State *state, int off, float *x);
|
WL_String wl_compiler_error (WL_Compiler *compiler);
|
||||||
int WL_peekstr (WL_State *state, int off, WL_String *str);
|
int wl_dump_ast (WL_Compiler *compiler, char *dst, int cap);
|
||||||
int WL_popnone (WL_State *state);
|
void wl_dump_program (WL_Program program);
|
||||||
int WL_popint (WL_State *state, long long *x);
|
|
||||||
int WL_popfloat (WL_State *state, float *x);
|
|
||||||
int WL_popstr (WL_State *state, WL_String *str);
|
|
||||||
int WL_popany (WL_State *state);
|
|
||||||
void WL_select (WL_State *state);
|
|
||||||
void WL_pushnone (WL_State *state);
|
|
||||||
void WL_pushint (WL_State *state, long long x);
|
|
||||||
void WL_pushfloat (WL_State *state, float x);
|
|
||||||
void WL_pushstr (WL_State *state, WL_String str);
|
|
||||||
void WL_pusharray (WL_State *state, int cap);
|
|
||||||
void WL_pushmap (WL_State *state, int cap);
|
|
||||||
void WL_insert (WL_State *state);
|
|
||||||
void WL_append (WL_State *state);
|
|
||||||
|
|
||||||
#endif // WL_PUBLIC_INCLUDED
|
WL_Runtime* wl_runtime_init (WL_Arena *arena, WL_Program program);
|
||||||
#endif // WL_AMALGAMATION
|
WL_EvalResult wl_runtime_eval (WL_Runtime *rt);
|
||||||
|
WL_String wl_runtime_error (WL_Runtime *rt);
|
||||||
|
void wl_runtime_dump (WL_Runtime *rt);
|
||||||
|
|
||||||
|
bool wl_streq (WL_String a, char *b, int blen);
|
||||||
|
int wl_arg_count (WL_Runtime *rt);
|
||||||
|
bool wl_arg_none (WL_Runtime *rt, int idx);
|
||||||
|
bool wl_arg_bool (WL_Runtime *rt, int idx, bool *x);
|
||||||
|
bool wl_arg_s64 (WL_Runtime *rt, int idx, int64_t *x);
|
||||||
|
bool wl_arg_f64 (WL_Runtime *rt, int idx, double *x);
|
||||||
|
bool wl_arg_str (WL_Runtime *rt, int idx, WL_String *x);
|
||||||
|
bool wl_arg_array (WL_Runtime *rt, int idx);
|
||||||
|
bool wl_arg_map (WL_Runtime *rt, int idx);
|
||||||
|
bool wl_peek_none (WL_Runtime *rt, int off);
|
||||||
|
bool wl_peek_bool (WL_Runtime *rt, int off, bool *x);
|
||||||
|
bool wl_peek_s64 (WL_Runtime *rt, int off, int64_t *x);
|
||||||
|
bool wl_peek_f64 (WL_Runtime *rt, int off, double *x);
|
||||||
|
bool wl_peek_str (WL_Runtime *rt, int off, WL_String *x);
|
||||||
|
bool wl_pop_any (WL_Runtime *rt);
|
||||||
|
bool wl_pop_none (WL_Runtime *rt);
|
||||||
|
bool wl_pop_bool (WL_Runtime *rt, bool *x);
|
||||||
|
bool wl_pop_s64 (WL_Runtime *rt, int64_t *x);
|
||||||
|
bool wl_pop_f64 (WL_Runtime *rt, double *x);
|
||||||
|
bool wl_pop_str (WL_Runtime *rt, WL_String *x);
|
||||||
|
void wl_push_none (WL_Runtime *rt);
|
||||||
|
void wl_push_true (WL_Runtime *rt);
|
||||||
|
void wl_push_false (WL_Runtime *rt);
|
||||||
|
void wl_push_s64 (WL_Runtime *rt, int64_t x);
|
||||||
|
void wl_push_f64 (WL_Runtime *rt, double x);
|
||||||
|
void wl_push_str (WL_Runtime *rt, WL_String x);
|
||||||
|
void wl_push_array (WL_Runtime *rt, int cap);
|
||||||
|
void wl_push_map (WL_Runtime *rt, int cap);
|
||||||
|
void wl_push_arg (WL_Runtime *rt, int idx);
|
||||||
|
void wl_insert (WL_Runtime *rt);
|
||||||
|
void wl_append (WL_Runtime *rt);
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
|
|
||||||
class Amalgamator:
|
|
||||||
def __init__(self):
|
|
||||||
self.out = ""
|
|
||||||
|
|
||||||
def append_text(self, text):
|
|
||||||
self.out += text
|
|
||||||
|
|
||||||
def append_file(self, file):
|
|
||||||
|
|
||||||
self.out += "\n"
|
|
||||||
self.out += "////////////////////////////////////////////////////////////////////////////////////////\n"
|
|
||||||
self.out += "// " + file + "\n"
|
|
||||||
self.out += "////////////////////////////////////////////////////////////////////////////////////////\n"
|
|
||||||
self.out += "\n"
|
|
||||||
# self.out += "#line 1 \"" + file + "\"\n"
|
|
||||||
self.out += open(file).read()
|
|
||||||
|
|
||||||
if len(self.out) > 0 and self.out[len(self.out)-1] != '\n':
|
|
||||||
self.out += "\n"
|
|
||||||
|
|
||||||
def save(self, file):
|
|
||||||
open(file, 'w').write(self.out)
|
|
||||||
|
|
||||||
desc = """
|
|
||||||
// This file was generated automatically. Do not modify directly!
|
|
||||||
"""
|
|
||||||
|
|
||||||
header = Amalgamator()
|
|
||||||
header.append_text("#ifndef WL_AMALGAMATION\n")
|
|
||||||
header.append_text("#define WL_AMALGAMATION\n")
|
|
||||||
header.append_text(desc)
|
|
||||||
header.append_file("src/compile.h")
|
|
||||||
header.append_text("#endif // WL_AMALGAMATION\n")
|
|
||||||
header.save("WL.h")
|
|
||||||
|
|
||||||
source = Amalgamator()
|
|
||||||
source.append_text("#include \"WL.h\"\n")
|
|
||||||
source.append_file("src/includes.h")
|
|
||||||
source.append_file("src/basic.h")
|
|
||||||
source.append_file("src/basic.c")
|
|
||||||
source.append_file("src/file.h")
|
|
||||||
source.append_file("src/file.c")
|
|
||||||
source.append_file("src/parse.h")
|
|
||||||
source.append_file("src/parse.c")
|
|
||||||
source.append_file("src/assemble.h")
|
|
||||||
source.append_file("src/assemble.c")
|
|
||||||
source.append_file("src/value.h")
|
|
||||||
source.append_file("src/value.c")
|
|
||||||
source.append_file("src/eval.h")
|
|
||||||
source.append_file("src/eval.c")
|
|
||||||
source.append_file("src/compile.c")
|
|
||||||
source.save("WL.c")
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
let ugu = 78
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
<!-- This is a comment. Comments have no effect! -->
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
<!--
|
|
||||||
You can have regular HTML elements in WL.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<a head="some_page.html">I'm a link</a>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
You can declare variables and use them in the HTML by
|
|
||||||
escaping the name
|
|
||||||
-->
|
|
||||||
|
|
||||||
let name = "cozis"
|
|
||||||
|
|
||||||
<p>Hello from \name</p>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
HTML attributes are evaluated as WL expressions.
|
|
||||||
|
|
||||||
The following evaluates to
|
|
||||||
|
|
||||||
<p A=Some value B=7></p>
|
|
||||||
|
|
||||||
which, to be fair, isn't right. There shoud be quotes
|
|
||||||
around "Some value"
|
|
||||||
-->
|
|
||||||
|
|
||||||
let valueA = "Some value"
|
|
||||||
|
|
||||||
<p A=valueA B=1+2*3></p>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
HTML attributes are just expressions, and therefore
|
|
||||||
can be assigned to variables
|
|
||||||
-->
|
|
||||||
|
|
||||||
let link = <a href="page.html">Click me</a>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
And then can be printed by simply stating the name
|
|
||||||
of the variable or by embedding it in a lager element
|
|
||||||
-->
|
|
||||||
|
|
||||||
link
|
|
||||||
|
|
||||||
<p>You should click this link: \link</p>
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
<!--
|
|
||||||
You can embed elements based on conditions
|
|
||||||
using plain if-else statements
|
|
||||||
-->
|
|
||||||
|
|
||||||
let a = 2
|
|
||||||
|
|
||||||
if a == 1: {
|
|
||||||
<a>The condition is true</a>
|
|
||||||
} else {
|
|
||||||
<a>It is false</a>
|
|
||||||
}
|
|
||||||
|
|
||||||
<!--
|
|
||||||
If the branch occurs inside an HTML element,
|
|
||||||
you must escape the if keyword with a backslash
|
|
||||||
-->
|
|
||||||
|
|
||||||
<p>
|
|
||||||
\if a == 1: {
|
|
||||||
<a>The condition is true</a>
|
|
||||||
} else {
|
|
||||||
<a>It is false</a>
|
|
||||||
}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Similarly, you can execute code multiple times
|
|
||||||
based on a condition using a while statement.
|
|
||||||
|
|
||||||
The following loop generates the output
|
|
||||||
|
|
||||||
<a>I'm link number 1</a>
|
|
||||||
<a>I'm link number 2</a>
|
|
||||||
<a>I'm link number 3</a>
|
|
||||||
|
|
||||||
Note how the i variable is printed after adding
|
|
||||||
1 to it.
|
|
||||||
-->
|
|
||||||
|
|
||||||
let i = 0
|
|
||||||
while i < 3: {
|
|
||||||
<a>I'm link number \i + 1</a>
|
|
||||||
i = i + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
<!--
|
|
||||||
To insert the loop in an HTML element, you need
|
|
||||||
to escape it
|
|
||||||
-->
|
|
||||||
|
|
||||||
i = 0
|
|
||||||
<ul>
|
|
||||||
\while i < 3: {
|
|
||||||
<li>I'm link number \i + 1</li>
|
|
||||||
i = i + 1
|
|
||||||
}
|
|
||||||
</ul>
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
|
|
||||||
<!--
|
|
||||||
WL supports integers, floats, strings, array and map
|
|
||||||
values. Arrays are what you expect. They allow one to
|
|
||||||
store sequences of elements.
|
|
||||||
|
|
||||||
The following snippet prints
|
|
||||||
|
|
||||||
123
|
|
||||||
|
|
||||||
which is the string obtained by concatenating all
|
|
||||||
the elements
|
|
||||||
-->
|
|
||||||
|
|
||||||
let my_var = [1, 2, 3]
|
|
||||||
|
|
||||||
my_var
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Maps are similar to Python dicts and Javascript
|
|
||||||
objects. They store associations between values
|
|
||||||
|
|
||||||
The following prints
|
|
||||||
|
|
||||||
Second
|
|
||||||
-->
|
|
||||||
|
|
||||||
let my_map = { 1: "First", 2: "Second", 3: "Third" }
|
|
||||||
|
|
||||||
my_map[2]
|
|
||||||
|
|
||||||
<!--
|
|
||||||
You can have any type as a map key, and if the
|
|
||||||
key is a string that is also a valid variable
|
|
||||||
name, you can drop the double quotes
|
|
||||||
-->
|
|
||||||
|
|
||||||
let person = { "name": "Cozis" }
|
|
||||||
let person_no_quotes = { name: "Cozis" }
|
|
||||||
|
|
||||||
<!--
|
|
||||||
You can iterate over the keys of a map using the
|
|
||||||
for loop. The following prints the string
|
|
||||||
|
|
||||||
ABC
|
|
||||||
|
|
||||||
-->
|
|
||||||
|
|
||||||
for key in { A: 1, B: 2, C: 3 }: {
|
|
||||||
key
|
|
||||||
}
|
|
||||||
|
|
||||||
for key, i in { A: 1, B: 2, C: 3 }: {
|
|
||||||
<!--
|
|
||||||
You can keep track of the current index by adding
|
|
||||||
a second interation variable
|
|
||||||
-->
|
|
||||||
}
|
|
||||||
|
|
||||||
<!--
|
|
||||||
When using a for loop over a map, the first
|
|
||||||
iteration variable holds its keys. When the
|
|
||||||
iterated value is an array, the first variable
|
|
||||||
returns its values
|
|
||||||
-->
|
|
||||||
|
|
||||||
for val in [5, 3, 7]:
|
|
||||||
val
|
|
||||||
|
|
||||||
for val, i in [5, 3, 7]:
|
|
||||||
i
|
|
||||||
|
|
||||||
<!--
|
|
||||||
As usual, you can have for statements in HTML
|
|
||||||
by escaping them
|
|
||||||
-->
|
|
||||||
|
|
||||||
let links = ["http://github.com", "http://reddit.com"]
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
\for link, i in links:
|
|
||||||
<li><a href=link>I'm link number \i</a></li>
|
|
||||||
</ul>
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
|
|
||||||
<!--
|
|
||||||
You can declare functions too.
|
|
||||||
|
|
||||||
Unlike the global scope, expressions
|
|
||||||
are not printed by default, so you need
|
|
||||||
to use the print statement to do so.
|
|
||||||
-->
|
|
||||||
|
|
||||||
fun say_hello(name) {
|
|
||||||
print "Hello to "
|
|
||||||
print name
|
|
||||||
}
|
|
||||||
|
|
||||||
say_hello("cozis")
|
|
||||||
|
|
||||||
<!--
|
|
||||||
If a function is implemented with a single
|
|
||||||
expression, you can omit the curly braces
|
|
||||||
to return it
|
|
||||||
-->
|
|
||||||
|
|
||||||
fun say_hello_2(name)
|
|
||||||
<a>Hello, \name!</a>
|
|
||||||
|
|
||||||
say_hello_2("cozis")
|
|
||||||
@@ -1,121 +1,189 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include "wl.h"
|
||||||
|
|
||||||
#include "WL.h"
|
typedef struct FileData FileData;
|
||||||
|
|
||||||
|
struct FileData {
|
||||||
|
FileData *next;
|
||||||
|
int size;
|
||||||
|
char data[];
|
||||||
|
};
|
||||||
|
|
||||||
|
FileData *load_file(WL_String path)
|
||||||
|
{
|
||||||
|
char buf[1<<10];
|
||||||
|
if (path.len >= (int) sizeof(buf))
|
||||||
|
return NULL;
|
||||||
|
memcpy(buf, path.ptr, path.len);
|
||||||
|
buf[path.len] = '\0';
|
||||||
|
|
||||||
|
FILE *f = fopen(buf, "rb");
|
||||||
|
if (f == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
fseek(f, 0, SEEK_END);
|
||||||
|
long size = ftell(f);
|
||||||
|
fseek(f, 0, SEEK_SET);
|
||||||
|
|
||||||
|
FileData *data = malloc(sizeof(FileData) + size);
|
||||||
|
if (data == NULL) {
|
||||||
|
fclose(f);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
data->size = size;
|
||||||
|
data->next = NULL;
|
||||||
|
|
||||||
|
fread(data->data, 1, size, f);
|
||||||
|
fclose(f);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
if (argc < 2) {
|
char *entry_file = NULL;
|
||||||
printf("Missing file path\n");
|
|
||||||
|
bool bc = false;
|
||||||
|
bool ast = false;
|
||||||
|
bool run = true;
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
if (!strcmp(argv[i], "--bc"))
|
||||||
|
bc = true;
|
||||||
|
else if (!strcmp(argv[i], "--ast"))
|
||||||
|
ast = true;
|
||||||
|
else if (!strcmp(argv[i], "--no-run"))
|
||||||
|
run = false;
|
||||||
|
else
|
||||||
|
entry_file = argv[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry_file == NULL) {
|
||||||
|
fprintf(stderr, "Usage: %s file.wl\n", argv[0]);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
char *file = argv[1];
|
|
||||||
|
|
||||||
char err[1<<9];
|
int cap = 1<<20;
|
||||||
char *mem = malloc(1<<20);
|
char *mem = malloc(cap);
|
||||||
WL_Arena a = { mem, 1<<20, 0 };
|
if (mem == NULL) {
|
||||||
|
fprintf(stderr, "Error: Allocation failure\n");
|
||||||
WL_Compiler *compiler = WL_Compiler_init(&a);
|
return -1;
|
||||||
if (compiler == NULL) {
|
|
||||||
assert(0); // TODO
|
|
||||||
}
|
}
|
||||||
|
WL_Arena arena = { mem, cap, 0 };
|
||||||
|
|
||||||
int num_loaded_files = 0;
|
WL_Program program;
|
||||||
char *loaded_files[128];
|
{
|
||||||
|
WL_Compiler *c = wl_compiler_init(&arena);
|
||||||
WL_CompileResult result;
|
if (c == NULL) {
|
||||||
WL_String path = { file, strlen(file) };
|
fprintf(stderr, "Error: Out of memory");
|
||||||
for (int i = 0;; i++) {
|
|
||||||
|
|
||||||
char buf[1<<10];
|
|
||||||
if (path.len >= (int) sizeof(buf)) {
|
|
||||||
assert(0); // TODO
|
|
||||||
}
|
|
||||||
memcpy(buf, path.ptr, path.len);
|
|
||||||
buf[path.len] = '\0';
|
|
||||||
|
|
||||||
FILE *f = fopen(buf, "rb");
|
|
||||||
if (f == NULL) {
|
|
||||||
printf("File not found '%.*s'\n", path.len, path.ptr);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fseek(f, 0, SEEK_END);
|
FileData *file_head;
|
||||||
long file_size = ftell(f);
|
FileData **file_tail = &file_head;
|
||||||
fseek(f, 0, SEEK_SET);
|
|
||||||
|
|
||||||
char *file_data = malloc(file_size);
|
WL_String path = { entry_file, strlen(entry_file) };
|
||||||
|
for (;;) {
|
||||||
|
|
||||||
fread(file_data, 1, file_size, f);
|
FileData *file = load_file(path);
|
||||||
fclose(f);
|
if (file == NULL) {
|
||||||
|
printf("Couldn't open '%.*s'\n", path.len, path.ptr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
*file_tail = file;
|
||||||
|
file_tail = &file->next;
|
||||||
|
|
||||||
result = WL_compile(compiler, path, (WL_String) { file_data, file_size });
|
WL_AddResult res = wl_compiler_add(c, (WL_String) { file->data, file->size });
|
||||||
|
if (res.type == WL_ADD_ERROR) {
|
||||||
loaded_files[num_loaded_files++] = file_data;
|
fprintf(stderr, "Error: %s\n", wl_compiler_error(c).ptr);
|
||||||
|
return -1;
|
||||||
if (result.type == WL_COMPILE_RESULT_ERROR) {
|
}
|
||||||
printf("Compilation of '%.*s' failed\n", path.len, path.ptr);
|
if (res.type == WL_ADD_AGAIN) {
|
||||||
|
path = res.path;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
assert(res.type == WL_ADD_LINK);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.type == WL_COMPILE_RESULT_DONE)
|
*file_tail = NULL;
|
||||||
break;
|
|
||||||
|
|
||||||
assert(result.type == WL_COMPILE_RESULT_FILE);
|
if (ast) {
|
||||||
path = result.path;
|
char buf[1<<10];
|
||||||
}
|
int len = wl_dump_ast(c, buf, sizeof(buf));
|
||||||
|
if (len > sizeof(buf)-1)
|
||||||
|
len = sizeof(buf)-1;
|
||||||
|
buf[len] = '\0';
|
||||||
|
|
||||||
for (int i = 0; i < num_loaded_files; i++)
|
printf("%s\n", buf);
|
||||||
free(loaded_files[i]);
|
}
|
||||||
|
|
||||||
WL_Compiler_free(compiler);
|
int ret = wl_compiler_link(c, &program);
|
||||||
|
if (ret < 0) {
|
||||||
|
WL_String err = wl_compiler_error(c);
|
||||||
|
fprintf(stderr, "Error: %s\n", err.ptr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (result.type == WL_COMPILE_RESULT_ERROR) {
|
if (bc)
|
||||||
printf("Compilation error\n");
|
wl_dump_program(program);
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
WL_Program program = result.program;
|
|
||||||
|
|
||||||
WL_State *state = WL_State_init(&a, program, err, (int) sizeof(err));
|
FileData *file = file_head;
|
||||||
|
while (file) {
|
||||||
WL_State_trace(state, 0);
|
FileData *next = file->next;
|
||||||
|
free(file);
|
||||||
for (bool done = false; !done; ) {
|
file = next;
|
||||||
|
}
|
||||||
WL_Result result = WL_eval(state);
|
}
|
||||||
switch (result.type) {
|
|
||||||
|
if (run) {
|
||||||
case WL_DONE:
|
WL_Runtime *rt = wl_runtime_init(&arena, program);
|
||||||
done = true;
|
if (rt == NULL) {
|
||||||
break;
|
fprintf(stderr, "Error: Invalid program or out of memory\n");
|
||||||
|
return -1;
|
||||||
case WL_ERROR:
|
}
|
||||||
done = true;
|
|
||||||
printf("%s\n", err);
|
FILE *output = stdout;
|
||||||
break;
|
for (bool done = false; !done; ) {
|
||||||
|
WL_EvalResult res = wl_runtime_eval(rt);
|
||||||
case WL_VAR:
|
|
||||||
if (0) {}
|
wl_runtime_dump(rt);
|
||||||
else if (WL_streq(result.str, "varA", -1)) WL_pushint(state, 1);
|
|
||||||
else if (WL_streq(result.str, "varB", -1)) WL_pushint(state, 1);
|
switch (res.type) {
|
||||||
else if (WL_streq(result.str, "varC", -1)) WL_pushint(state, 1);
|
|
||||||
break;
|
case WL_EVAL_NONE:
|
||||||
|
break;
|
||||||
case WL_CALL:
|
|
||||||
// TODO
|
case WL_EVAL_DONE:
|
||||||
printf("(called [%.*s])\n", result.str.len, result.str.ptr);
|
done = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WL_OUTPUT:
|
case WL_EVAL_ERROR:
|
||||||
fwrite(result.str.ptr, 1, result.str.len, stdout);
|
printf("Error: %s\n", wl_runtime_error(rt).ptr);
|
||||||
break;
|
return -1;
|
||||||
|
|
||||||
|
case WL_EVAL_OUTPUT:
|
||||||
|
fwrite(res.str.ptr, 1, res.str.len, output);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WL_EVAL_SYSVAR:
|
||||||
|
if (wl_streq(res.str, "varA", -1)) wl_push_s64(rt, 1);
|
||||||
|
if (wl_streq(res.str, "varB", -1)) wl_push_s64(rt, 7);
|
||||||
|
if (wl_streq(res.str, "varC", -1)) wl_push_s64(rt, 13);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WL_EVAL_SYSCALL:
|
||||||
|
if (wl_streq(res.str, "testfn", -1)) {
|
||||||
|
for (int i = 0; i < wl_arg_count(rt); i++)
|
||||||
|
wl_push_arg(rt, i);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WL_State_free(state);
|
|
||||||
free(mem);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
include "example.wl"
|
|
||||||
|
|
||||||
ugu
|
|
||||||
|
|
||||||
let posts = [
|
|
||||||
{
|
|
||||||
author: "UserA",
|
|
||||||
title: "I'm the first post",
|
|
||||||
content: "Sup everyone!",
|
|
||||||
date: "1 Jan 2025",
|
|
||||||
comments: [
|
|
||||||
{
|
|
||||||
author: "UserB",
|
|
||||||
content: "Hello! This is a comment!",
|
|
||||||
comments: [
|
|
||||||
{
|
|
||||||
author: "UserA",
|
|
||||||
content: "Hello to you!",
|
|
||||||
comments: []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
fun comment(c)
|
|
||||||
<div>
|
|
||||||
<a>\c.author</a>
|
|
||||||
<p>\c.content</p>
|
|
||||||
\for subc in c.comments:
|
|
||||||
comment(subc)
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>List of posts</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
\for post in posts:
|
|
||||||
<div class="post">
|
|
||||||
<h2>
|
|
||||||
\post.title
|
|
||||||
</h2>
|
|
||||||
<span>
|
|
||||||
Posted on \post.date by \post.author
|
|
||||||
</span>
|
|
||||||
<p>
|
|
||||||
\post.content
|
|
||||||
</p>
|
|
||||||
<div class="comments">
|
|
||||||
\for c in post.comments:
|
|
||||||
comment(c)
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -0,0 +1,198 @@
|
|||||||
|
# Expressions
|
||||||
|
|
||||||
|
A WL file is a sequence of statements. One type of statement is expressions:
|
||||||
|
|
||||||
|
```
|
||||||
|
1 + 2
|
||||||
|
```
|
||||||
|
|
||||||
|
All expressions statements are evaluated and written to output.
|
||||||
|
|
||||||
|
## Supported Types
|
||||||
|
|
||||||
|
WL supports these type of values:
|
||||||
|
|
||||||
|
1. None: A value which is only equal to itself
|
||||||
|
2. Booleans
|
||||||
|
3. Integers: Equivalent to `int64_t` in C
|
||||||
|
4. Floats: Equivalent to `double` in C
|
||||||
|
5. Strings: Sequence of bytes
|
||||||
|
6. Arrays: Etherogeneous sequences of values
|
||||||
|
7. Maps: Associations between arbitrary key-value paris
|
||||||
|
|
||||||
|
This is how the literals are used:
|
||||||
|
|
||||||
|
```
|
||||||
|
none
|
||||||
|
true
|
||||||
|
false
|
||||||
|
"I'm a string"
|
||||||
|
'I'm a string too!'
|
||||||
|
[1, 2, 3]
|
||||||
|
+{ "I'm the first key": 1, "I'm the second key": 2 }
|
||||||
|
```
|
||||||
|
|
||||||
|
## Unary Operators
|
||||||
|
|
||||||
|
The supported unary operators are
|
||||||
|
|
||||||
|
1. `+`: Allowed on any type and returns the operand unchanged
|
||||||
|
2. `-`: Negates an integer or float value
|
||||||
|
3. `len`: Returns the number of items stored into an array or the number of key-value pairs in a map
|
||||||
|
|
||||||
|
## Binary Operators
|
||||||
|
|
||||||
|
The supported binary operators are
|
||||||
|
|
||||||
|
1. `+`: Sums two numeric values. If a float value is involved, the result is a float too.
|
||||||
|
1. `-`: Subtracts two numeric values. If a float value is involved, the result is a float too.
|
||||||
|
1. `*`: Multiplies two numeric values. If a float value is involved, the result is a float too.
|
||||||
|
1. `/`: Divides two numeric values. If a float value is involved, the result is a float too.
|
||||||
|
1. `%`: Returns the division's remainder. The operands must be integers.
|
||||||
|
1. `==`: Returns `true` if the operands are the same, else returns `false`.
|
||||||
|
1. `!=`: Returns `true` if the operands are different, else returns `false`
|
||||||
|
1. `<`: Returns `true` if the first operand is lower than the second one, else returns `false`. The operands must be numeric.
|
||||||
|
1. `>`: Returns `true` if the first operand is greater than the second one, else returns `false`. The operands must be numeric.
|
||||||
|
|
||||||
|
Note that there are no implicit conversions, so for instance the integer `1` is different from the floating point `1.0`.
|
||||||
|
|
||||||
|
## Escaping Characters In String Literals
|
||||||
|
|
||||||
|
String literals can only contain printable ASCII characters (codepoints 32 to 127). Any other byte value must be escaped.
|
||||||
|
|
||||||
|
You can use `\n`, `\t`, `\r` to represent the line feed, horizontal tab, and carriage return characters.
|
||||||
|
|
||||||
|
Since single `'` or double `"` quotes are used as string delimiters, you must escape any quote that's part of the value using a backslash: `\'`, `\"`.
|
||||||
|
|
||||||
|
If a string contains a backslash, the backslash itself must be escaped `\\`.
|
||||||
|
|
||||||
|
Any byte value can be encoded using the `\x` notation
|
||||||
|
|
||||||
|
```
|
||||||
|
"This byte \xFF is not valid ASCII"
|
||||||
|
```
|
||||||
|
|
||||||
|
It allows to encode any byte value with its uppercase or lowercase hexadecimal representation. There must always be two hex digits, even if the high bits are zero.
|
||||||
|
|
||||||
|
## Variables & Scopes
|
||||||
|
|
||||||
|
You can bind expression results to variables
|
||||||
|
|
||||||
|
```
|
||||||
|
let a = 1+2
|
||||||
|
```
|
||||||
|
|
||||||
|
This will bind the result of the expression to the name "a". Variable names can contain digits, letters, and underscores, but the first character can't be a digit. When an expression is bound to a variable, it is not written to output.
|
||||||
|
|
||||||
|
You can later reuse the bound value by its variable name
|
||||||
|
|
||||||
|
```
|
||||||
|
a + 3
|
||||||
|
```
|
||||||
|
|
||||||
|
This will output `6`.
|
||||||
|
|
||||||
|
You can't declare two variables with the same time. The following is invalid:
|
||||||
|
|
||||||
|
```
|
||||||
|
let a = 1
|
||||||
|
let a = 2
|
||||||
|
```
|
||||||
|
|
||||||
|
You can reuse the same variable name by declaring a new scope:
|
||||||
|
|
||||||
|
```
|
||||||
|
let a = 1
|
||||||
|
|
||||||
|
{
|
||||||
|
let a = 2
|
||||||
|
a
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Grouping statements into scopes this way allows one to reuse variable names. Whenever a variable is referenced, the one in the nearest scope is used. So the previous example will output `2`.
|
||||||
|
|
||||||
|
# If-else statements
|
||||||
|
|
||||||
|
You can optionally run some code based on an expression result using if-else statements:
|
||||||
|
|
||||||
|
```
|
||||||
|
if 1 > 2: {
|
||||||
|
"First branch taken"
|
||||||
|
} else {
|
||||||
|
"Second branch taken"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
As usual, you can omit the else branch
|
||||||
|
|
||||||
|
```
|
||||||
|
if 1 > 2: {
|
||||||
|
"Branch taken"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If the branch only contains one statement, you can omit the curly braces
|
||||||
|
|
||||||
|
```
|
||||||
|
if 1 > 2:
|
||||||
|
"First branch taken"
|
||||||
|
else
|
||||||
|
"Second branch taken"
|
||||||
|
```
|
||||||
|
|
||||||
|
A consequence of this is that you can chain if-else statements
|
||||||
|
|
||||||
|
```
|
||||||
|
let a = 4
|
||||||
|
if a == 1: {
|
||||||
|
"a is 1"
|
||||||
|
} else if a == 2: {
|
||||||
|
"a is 2"
|
||||||
|
} else if a == 3: {
|
||||||
|
"a is 3"
|
||||||
|
} else {
|
||||||
|
"a is something else"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# While statements
|
||||||
|
|
||||||
|
You can loop while a certain condition is true using a while statement
|
||||||
|
|
||||||
|
```
|
||||||
|
let i = 0
|
||||||
|
while i < 3: {
|
||||||
|
"i="
|
||||||
|
i
|
||||||
|
"\n"
|
||||||
|
i = i+1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Which will print:
|
||||||
|
|
||||||
|
```
|
||||||
|
i=0
|
||||||
|
i=1
|
||||||
|
i=2
|
||||||
|
```
|
||||||
|
|
||||||
|
# For statements
|
||||||
|
|
||||||
|
You can iterate over the items of an array using the for statement
|
||||||
|
|
||||||
|
```
|
||||||
|
for item in ["A", "B", "C"]: {
|
||||||
|
item
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This will print
|
||||||
|
|
||||||
|
```
|
||||||
|
ABC
|
||||||
|
```
|
||||||
|
|
||||||
|
By adding a second iteration variable, you will be able to read the current index
|
||||||
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
---3;
|
|
||||||
|
|
||||||
hello;
|
|
||||||
|
|
||||||
world;
|
|
||||||
|
|
||||||
if 1 + 2: {
|
|
||||||
a;
|
|
||||||
b + 3 * 4;
|
|
||||||
c;
|
|
||||||
}
|
|
||||||
|
|
||||||
while 1 + 2:
|
|
||||||
"This is a string";
|
|
||||||
|
|
||||||
<html>
|
|
||||||
<head name=hello xxx>
|
|
||||||
aaa
|
|
||||||
<title>Some Title</title>
|
|
||||||
bbb
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<ul>
|
|
||||||
$ while 1:
|
|
||||||
<li>$post.name; $post.date;</li>;
|
|
||||||
</ul>
|
|
||||||
</body>
|
|
||||||
</html>;
|
|
||||||
-1637
File diff suppressed because it is too large
Load Diff
@@ -1,65 +0,0 @@
|
|||||||
#ifndef WL_ASSEMBLE_INCLUDED
|
|
||||||
#define WL_ASSEMBLE_INCLUDED
|
|
||||||
|
|
||||||
#ifndef WL_AMALGAMATION
|
|
||||||
#include "public.h"
|
|
||||||
#include "parse.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
enum {
|
|
||||||
OPCODE_NOPE = 0x00,
|
|
||||||
OPCODE_EXIT = 0x23,
|
|
||||||
OPCODE_GROUP = 0x25,
|
|
||||||
OPCODE_GPOP = 0x26,
|
|
||||||
OPCODE_GPRINT = 0x27,
|
|
||||||
OPCODE_GTRUNC = 0x28,
|
|
||||||
OPCODE_GCOALESCE = 0x29,
|
|
||||||
OPCODE_GOVERWRITE = 0x2A,
|
|
||||||
OPCODE_GPACK = 0x2B,
|
|
||||||
OPCODE_PUSHI = 0x01,
|
|
||||||
OPCODE_PUSHF = 0x02,
|
|
||||||
OPCODE_PUSHS = 0x03,
|
|
||||||
OPCODE_PUSHV = 0x04,
|
|
||||||
OPCODE_PUSHA = 0x05,
|
|
||||||
OPCODE_PUSHM = 0x06,
|
|
||||||
OPCODE_PUSHN = 0x21,
|
|
||||||
OPCODE_POP = 0x07,
|
|
||||||
OPCODE_NEG = 0x08,
|
|
||||||
OPCODE_EQL = 0x09,
|
|
||||||
OPCODE_NQL = 0x0A,
|
|
||||||
OPCODE_LSS = 0x0B,
|
|
||||||
OPCODE_GRT = 0x0C,
|
|
||||||
OPCODE_ADD = 0x0D,
|
|
||||||
OPCODE_SUB = 0x0E,
|
|
||||||
OPCODE_MUL = 0x0F,
|
|
||||||
OPCODE_DIV = 0x10,
|
|
||||||
OPCODE_MOD = 0x11,
|
|
||||||
OPCODE_SETV = 0x12,
|
|
||||||
OPCODE_JUMP = 0x13,
|
|
||||||
OPCODE_JIFP = 0x14,
|
|
||||||
OPCODE_CALL = 0x15,
|
|
||||||
OPCODE_RET = 0x16,
|
|
||||||
OPCODE_APPEND = 0x17,
|
|
||||||
OPCODE_INSERT1 = 0x18,
|
|
||||||
OPCODE_INSERT2 = 0x19,
|
|
||||||
OPCODE_SELECT = 0x20,
|
|
||||||
OPCODE_PRINT = 0x24,
|
|
||||||
OPCODE_SYSVAR = 0x2C,
|
|
||||||
OPCODE_SYSCALL = 0x2D,
|
|
||||||
OPCODE_FOR = 0x2E,
|
|
||||||
OPCODE_PUSHT = 0x2F,
|
|
||||||
OPCODE_PUSHFL = 0x30,
|
|
||||||
OPCODE_LEN = 0x31,
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
WL_Program program;
|
|
||||||
int errlen;
|
|
||||||
} AssembleResult;
|
|
||||||
|
|
||||||
int parse_program_header(WL_Program p, String *code, String *data, char *errbuf, int errmax);
|
|
||||||
void print_program(WL_Program program);
|
|
||||||
char *print_instruction(char *p, char *data);
|
|
||||||
AssembleResult assemble(Node *root, WL_Arena *arena, char *errbuf, int errmax);
|
|
||||||
|
|
||||||
#endif // WL_ASSEMBLE_INCLUDED
|
|
||||||
-98
@@ -1,98 +0,0 @@
|
|||||||
|
|
||||||
#ifndef WL_AMALGAMATION
|
|
||||||
#include "includes.h"
|
|
||||||
#include "basic.h"
|
|
||||||
#include "public.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool is_space(char c)
|
|
||||||
{
|
|
||||||
return c == ' ' || c == '\t' || c == '\r' || c == '\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_digit(char c)
|
|
||||||
{
|
|
||||||
return c >= '0' && c <= '9';
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_alpha(char c)
|
|
||||||
{
|
|
||||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_printable(char c)
|
|
||||||
{
|
|
||||||
return c >= ' ' && c <= '~';
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_hex_digit(char c)
|
|
||||||
{
|
|
||||||
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
|
|
||||||
}
|
|
||||||
|
|
||||||
char to_lower(char c)
|
|
||||||
{
|
|
||||||
if (c >= 'A' && c <= 'Z')
|
|
||||||
return c - 'A' + 10;
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
int hex_digit_to_int(char c)
|
|
||||||
{
|
|
||||||
if (c >= 'a' && c <= 'f')
|
|
||||||
return c - 'a' + 10;
|
|
||||||
|
|
||||||
if (c >= 'A' && c <= 'F')
|
|
||||||
return c - 'A' + 10;
|
|
||||||
|
|
||||||
return c - '0';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool streq(String a, String b)
|
|
||||||
{
|
|
||||||
if (a.len != b.len)
|
|
||||||
return false;
|
|
||||||
for (int i = 0; i < a.len; i++)
|
|
||||||
if (a.ptr[i] != b.ptr[i])
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool streqcase(String a, String b)
|
|
||||||
{
|
|
||||||
if (a.len != b.len)
|
|
||||||
return false;
|
|
||||||
for (int i = 0; i < a.len; i++)
|
|
||||||
if (to_lower(a.ptr[i]) != to_lower(b.ptr[i]))
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *alloc(WL_Arena *a, int len, int align)
|
|
||||||
{
|
|
||||||
int pad = -(intptr_t) (a->ptr + a->cur) & (align-1);
|
|
||||||
if (a->len - a->cur < len + pad)
|
|
||||||
return NULL;
|
|
||||||
void *ret = a->ptr + a->cur + pad;
|
|
||||||
a->cur += pad + len;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool grow_alloc(WL_Arena *a, char *p, int new_len)
|
|
||||||
{
|
|
||||||
int new_cur = (p - a->ptr) + new_len;
|
|
||||||
if (new_cur > a->len)
|
|
||||||
return false;
|
|
||||||
a->cur = new_cur;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
String copystr(String s, WL_Arena *a)
|
|
||||||
{
|
|
||||||
char *p = alloc(a, s.len, 1);
|
|
||||||
if (p == NULL)
|
|
||||||
return (String) { NULL, 0 };
|
|
||||||
memcpy(p, s.ptr, s.len);
|
|
||||||
return (String) { p, s.len };
|
|
||||||
}
|
|
||||||
-43
@@ -1,43 +0,0 @@
|
|||||||
#ifndef WL_BASIC_INCLUDED
|
|
||||||
#define WL_BASIC_INCLUDED
|
|
||||||
|
|
||||||
#ifndef WL_AMALGAMATION
|
|
||||||
#include "public.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
char *ptr;
|
|
||||||
int len;
|
|
||||||
} String;
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#define LLU "llu"
|
|
||||||
#define LLD "lld"
|
|
||||||
#else
|
|
||||||
#define LLU "lu"
|
|
||||||
#define LLD "ld"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define S(X) (String) { (X), (int) sizeof(X)-1 }
|
|
||||||
|
|
||||||
#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
|
|
||||||
#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
|
|
||||||
|
|
||||||
#define COUNT(X) (int) (sizeof(X) / sizeof((X)[0]))
|
|
||||||
|
|
||||||
bool is_space(char c);
|
|
||||||
bool is_digit(char c);
|
|
||||||
bool is_alpha(char c);
|
|
||||||
bool is_printable(char c);
|
|
||||||
char to_lower(char c);
|
|
||||||
bool is_hex_digit(char c);
|
|
||||||
int hex_digit_to_int(char c);
|
|
||||||
|
|
||||||
bool streq(String a, String b);
|
|
||||||
bool streqcase(String a, String b);
|
|
||||||
String copystr(String s, WL_Arena *a);
|
|
||||||
|
|
||||||
void *alloc(WL_Arena *a, int len, int align);
|
|
||||||
bool grow_alloc(WL_Arena *a, char *p, int new_len);
|
|
||||||
|
|
||||||
#endif // WL_BASIC_INCLUDED
|
|
||||||
-123
@@ -1,123 +0,0 @@
|
|||||||
|
|
||||||
#ifndef WL_AMALGAMATION
|
|
||||||
#include "eval.h"
|
|
||||||
#include "parse.h"
|
|
||||||
#include "assemble.h"
|
|
||||||
#include "compile.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define FILE_LIMIT 32
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
String file;
|
|
||||||
Node* root;
|
|
||||||
Node* includes;
|
|
||||||
} CompiledFile;
|
|
||||||
|
|
||||||
struct WL_Compiler {
|
|
||||||
WL_Arena* arena;
|
|
||||||
CompiledFile files[FILE_LIMIT];
|
|
||||||
int num_files;
|
|
||||||
String waiting_file;
|
|
||||||
};
|
|
||||||
|
|
||||||
int WL_streq(WL_String a, char *b, int blen)
|
|
||||||
{
|
|
||||||
if (b == NULL) b = "";
|
|
||||||
if (blen < 0) blen = strlen(b);
|
|
||||||
|
|
||||||
if (a.len != blen)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < a.len; i++)
|
|
||||||
if (a.ptr[i] != b[i])
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
WL_Compiler *WL_Compiler_init(WL_Arena *arena)
|
|
||||||
{
|
|
||||||
WL_Compiler *compiler = alloc(arena, (int) sizeof(WL_Compiler), _Alignof(WL_Compiler));
|
|
||||||
if (compiler == NULL)
|
|
||||||
return NULL;
|
|
||||||
compiler->arena = arena;
|
|
||||||
compiler->num_files = 0;
|
|
||||||
compiler->waiting_file = (String) { NULL, 0 };
|
|
||||||
return compiler;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WL_Compiler_free(WL_Compiler *compiler)
|
|
||||||
{
|
|
||||||
(void) compiler;
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
WL_CompileResult WL_compile(WL_Compiler *compiler, WL_String file, WL_String content)
|
|
||||||
{
|
|
||||||
if (compiler->waiting_file.len > 0)
|
|
||||||
file = (WL_String) { compiler->waiting_file.ptr, compiler->waiting_file.len };
|
|
||||||
else {
|
|
||||||
// TODO: copy file path
|
|
||||||
// file = strdup(file, compiler->arena)
|
|
||||||
}
|
|
||||||
|
|
||||||
char err[1<<9];
|
|
||||||
ParseResult pres = parse((String) { content.ptr, content.len }, compiler->arena, err, (int) sizeof(err));
|
|
||||||
if (pres.node == NULL) {
|
|
||||||
printf("%s\n", err); // TODO
|
|
||||||
return (WL_CompileResult) { .type=WL_COMPILE_RESULT_ERROR };
|
|
||||||
}
|
|
||||||
|
|
||||||
CompiledFile compiled_file = {
|
|
||||||
.file = { file.ptr, file.len },
|
|
||||||
.root = pres.node,
|
|
||||||
.includes = pres.includes,
|
|
||||||
};
|
|
||||||
compiler->files[compiler->num_files++] = compiled_file;
|
|
||||||
|
|
||||||
for (int i = 0; i < compiler->num_files; i++) {
|
|
||||||
|
|
||||||
Node *include = compiler->files[i].includes;
|
|
||||||
while (include) {
|
|
||||||
|
|
||||||
assert(include->type == NODE_INCLUDE);
|
|
||||||
|
|
||||||
if (include->include_root == NULL) {
|
|
||||||
for (int j = 0; j < compiler->num_files; j++) {
|
|
||||||
if (streq(include->include_path, compiler->files[j].file)) {
|
|
||||||
include->include_root = compiler->files[j].root;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (include->include_root == NULL) {
|
|
||||||
|
|
||||||
if (compiler->num_files == FILE_LIMIT) {
|
|
||||||
assert(0); // TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Make the path relative to the compiled file
|
|
||||||
|
|
||||||
compiler->waiting_file = include->include_path;
|
|
||||||
return (WL_CompileResult) { .type=WL_COMPILE_RESULT_FILE, .path={ include->include_path.ptr, include->include_path.len } };
|
|
||||||
}
|
|
||||||
|
|
||||||
include = include->include_next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AssembleResult ares = assemble(compiler->files[0].root, compiler->arena, err, (int) sizeof(err));
|
|
||||||
if (ares.errlen) {
|
|
||||||
printf("%s\n", err); // TODO
|
|
||||||
return (WL_CompileResult) { .type=WL_COMPILE_RESULT_ERROR };
|
|
||||||
}
|
|
||||||
|
|
||||||
return (WL_CompileResult) { .type=WL_COMPILE_RESULT_DONE, .program=ares.program };
|
|
||||||
}
|
|
||||||
|
|
||||||
void WL_dump_program(WL_Program program)
|
|
||||||
{
|
|
||||||
print_program(program);
|
|
||||||
}
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
#ifndef WL_PUBLIC_INCLUDED
|
|
||||||
#define WL_PUBLIC_INCLUDED
|
|
||||||
|
|
||||||
#ifndef WL_AMALGAMATION
|
|
||||||
#include "includes.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct WL_State WL_State;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
char *ptr;
|
|
||||||
int len;
|
|
||||||
} WL_String;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
char *ptr;
|
|
||||||
int len;
|
|
||||||
} WL_Program;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
WL_DONE,
|
|
||||||
WL_ERROR,
|
|
||||||
WL_VAR,
|
|
||||||
WL_CALL,
|
|
||||||
WL_OUTPUT,
|
|
||||||
} WL_ResultType;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
WL_ResultType type;
|
|
||||||
WL_String str;
|
|
||||||
} WL_Result;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
char *ptr;
|
|
||||||
int len;
|
|
||||||
int cur;
|
|
||||||
} WL_Arena;
|
|
||||||
|
|
||||||
typedef struct WL_Compiler WL_Compiler;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
WL_COMPILE_RESULT_DONE,
|
|
||||||
WL_COMPILE_RESULT_FILE,
|
|
||||||
WL_COMPILE_RESULT_ERROR,
|
|
||||||
} WL_CompileResultType;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
WL_CompileResultType type;
|
|
||||||
WL_Program program;
|
|
||||||
WL_String path;
|
|
||||||
} WL_CompileResult;
|
|
||||||
|
|
||||||
WL_Compiler* WL_Compiler_init (WL_Arena *arena);
|
|
||||||
void WL_Compiler_free (WL_Compiler *compiler);
|
|
||||||
WL_CompileResult WL_compile (WL_Compiler *compiler, WL_String file, WL_String content);
|
|
||||||
WL_State* WL_State_init (WL_Arena *a, WL_Program p, char *err, int errmax);
|
|
||||||
void WL_State_free (WL_State *state);
|
|
||||||
void WL_State_trace (WL_State *state, int trace);
|
|
||||||
WL_Result WL_eval (WL_State *state);
|
|
||||||
|
|
||||||
void WL_dump_program(WL_Program program);
|
|
||||||
|
|
||||||
int WL_streq (WL_String a, char *b, int blen);
|
|
||||||
int WL_peeknone (WL_State *state, int off);
|
|
||||||
int WL_peekint (WL_State *state, int off, long long *x);
|
|
||||||
int WL_peekfloat (WL_State *state, int off, float *x);
|
|
||||||
int WL_peekstr (WL_State *state, int off, WL_String *str);
|
|
||||||
int WL_popnone (WL_State *state);
|
|
||||||
int WL_popint (WL_State *state, long long *x);
|
|
||||||
int WL_popfloat (WL_State *state, float *x);
|
|
||||||
int WL_popstr (WL_State *state, WL_String *str);
|
|
||||||
int WL_popany (WL_State *state);
|
|
||||||
void WL_select (WL_State *state);
|
|
||||||
void WL_pushnone (WL_State *state);
|
|
||||||
void WL_pushint (WL_State *state, long long x);
|
|
||||||
void WL_pushfloat (WL_State *state, float x);
|
|
||||||
void WL_pushstr (WL_State *state, WL_String str);
|
|
||||||
void WL_pusharray (WL_State *state, int cap);
|
|
||||||
void WL_pushmap (WL_State *state, int cap);
|
|
||||||
void WL_insert (WL_State *state);
|
|
||||||
void WL_append (WL_State *state);
|
|
||||||
|
|
||||||
#endif // WL_PUBLIC_INCLUDED
|
|
||||||
-1389
File diff suppressed because it is too large
Load Diff
-11
@@ -1,11 +0,0 @@
|
|||||||
#ifndef WL_EVAL_INCLUDED
|
|
||||||
#define WL_EVAL_INCLUDED
|
|
||||||
|
|
||||||
#ifndef WL_AMALGAMATION
|
|
||||||
#include "assemble.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// TODO: pretty sure this is unused
|
|
||||||
int eval(WL_Program p, WL_Arena *a, char *errbuf, int errmax);
|
|
||||||
|
|
||||||
#endif // WL_EVAL_INCLUDED
|
|
||||||
-114
@@ -1,114 +0,0 @@
|
|||||||
#ifndef WL_AMALGAMATION
|
|
||||||
#include "includes.h"
|
|
||||||
#include "file.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int file_open(String path, File *handle, int *size)
|
|
||||||
{
|
|
||||||
char zt[1<<10];
|
|
||||||
if (path.len >= COUNT(zt))
|
|
||||||
return -1;
|
|
||||||
memcpy(zt, path.ptr, path.len);
|
|
||||||
zt[path.len] = '\0';
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
*handle = CreateFileA(
|
|
||||||
zt,
|
|
||||||
GENERIC_READ,
|
|
||||||
FILE_SHARE_READ,
|
|
||||||
NULL,
|
|
||||||
OPEN_EXISTING,
|
|
||||||
FILE_ATTRIBUTE_NORMAL,
|
|
||||||
NULL
|
|
||||||
);
|
|
||||||
if (*handle == INVALID_HANDLE_VALUE) {
|
|
||||||
DWORD error = GetLastError();
|
|
||||||
if (error == ERROR_FILE_NOT_FOUND ||
|
|
||||||
error == ERROR_ACCESS_DENIED)
|
|
||||||
return 1;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
LARGE_INTEGER fileSize;
|
|
||||||
if (!GetFileSizeEx(*handle, &fileSize)) {
|
|
||||||
CloseHandle(*handle);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (fileSize.QuadPart > INT_MAX) {
|
|
||||||
CloseHandle(*handle);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
*size = (int) fileSize.QuadPart;
|
|
||||||
#else
|
|
||||||
*handle = open(zt, O_RDONLY);
|
|
||||||
if (*handle < 0) {
|
|
||||||
if (errno == ENOENT)
|
|
||||||
return 1;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
struct stat info;
|
|
||||||
if (fstat(*handle, &info) < 0) {
|
|
||||||
close(*handle);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (S_ISDIR(info.st_mode)) {
|
|
||||||
close(*handle);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (info.st_size > INT_MAX) {
|
|
||||||
close(*handle);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
*size = (int) info.st_size;
|
|
||||||
#endif
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void file_close(File file)
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
CloseHandle(file);
|
|
||||||
#else
|
|
||||||
close(file);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
int file_read(File file, char *dst, int max)
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
DWORD num;
|
|
||||||
BOOL ok = ReadFile(file, dst, max, &num, NULL);
|
|
||||||
if (!ok)
|
|
||||||
return -1;
|
|
||||||
return (int) num;
|
|
||||||
#else
|
|
||||||
return read(file, dst, max);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
int file_read_all(String path, String *dst)
|
|
||||||
{
|
|
||||||
int len;
|
|
||||||
File handle;
|
|
||||||
if (file_open(path, &handle, &len) < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
char *ptr = malloc(len+1);
|
|
||||||
if (ptr == NULL) {
|
|
||||||
file_close(handle);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int copied = 0; copied < len; ) {
|
|
||||||
int ret = file_read(handle, ptr + copied, len - copied);
|
|
||||||
if (ret <= 0) {
|
|
||||||
free(ptr);
|
|
||||||
file_close(handle);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
copied += ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
*dst = (String) { ptr, len };
|
|
||||||
file_close(handle);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
-20
@@ -1,20 +0,0 @@
|
|||||||
#ifndef WL_FILE_INCLUDED
|
|
||||||
#define WL_FILE_INCLUDED
|
|
||||||
|
|
||||||
#ifndef WL_AMALGAMATION
|
|
||||||
#include "includes.h"
|
|
||||||
#include "basic.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
typedef HANDLE File;
|
|
||||||
#else
|
|
||||||
typedef int File;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int file_open(String path, File *handle, int *size);
|
|
||||||
void file_close(File file);
|
|
||||||
int file_read(File file, char *dst, int max);
|
|
||||||
int file_read_all(String path, String *dst);
|
|
||||||
|
|
||||||
#endif // WL_FILE_INCLUDED
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
#ifndef WL_INCLUDES_INCLUDED
|
|
||||||
#define WL_INCLUDES_INCLUDED
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#include <windows.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef _WIN32
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // WL_INCLUDES_INCLUDED
|
|
||||||
-1958
File diff suppressed because it is too large
Load Diff
-99
@@ -1,99 +0,0 @@
|
|||||||
#ifndef WL_PARSE_INCLUDED
|
|
||||||
#define WL_PARSE_INCLUDED
|
|
||||||
|
|
||||||
#ifndef WL_AMALGAMATION
|
|
||||||
#include "includes.h"
|
|
||||||
#include "basic.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
NODE_FUNC_DECL,
|
|
||||||
NODE_FUNC_ARG,
|
|
||||||
NODE_FUNC_CALL,
|
|
||||||
NODE_VAR_DECL,
|
|
||||||
NODE_PRINT,
|
|
||||||
NODE_BLOCK,
|
|
||||||
NODE_GLOBAL_BLOCK,
|
|
||||||
NODE_IFELSE,
|
|
||||||
NODE_FOR,
|
|
||||||
NODE_WHILE,
|
|
||||||
NODE_INCLUDE,
|
|
||||||
NODE_SELECT,
|
|
||||||
NODE_NESTED,
|
|
||||||
NODE_OPER_LEN,
|
|
||||||
NODE_OPER_POS,
|
|
||||||
NODE_OPER_NEG,
|
|
||||||
NODE_OPER_ASS,
|
|
||||||
NODE_OPER_EQL,
|
|
||||||
NODE_OPER_NQL,
|
|
||||||
NODE_OPER_LSS,
|
|
||||||
NODE_OPER_GRT,
|
|
||||||
NODE_OPER_ADD,
|
|
||||||
NODE_OPER_SUB,
|
|
||||||
NODE_OPER_MUL,
|
|
||||||
NODE_OPER_DIV,
|
|
||||||
NODE_OPER_MOD,
|
|
||||||
NODE_VALUE_INT,
|
|
||||||
NODE_VALUE_FLOAT,
|
|
||||||
NODE_VALUE_STR,
|
|
||||||
NODE_VALUE_NONE,
|
|
||||||
NODE_VALUE_TRUE,
|
|
||||||
NODE_VALUE_FALSE,
|
|
||||||
NODE_VALUE_VAR,
|
|
||||||
NODE_VALUE_SYSVAR,
|
|
||||||
NODE_VALUE_HTML,
|
|
||||||
NODE_VALUE_ARRAY,
|
|
||||||
NODE_VALUE_MAP,
|
|
||||||
NODE_HTML_PARAM,
|
|
||||||
} NodeType;
|
|
||||||
|
|
||||||
typedef struct Node Node;
|
|
||||||
struct Node {
|
|
||||||
NodeType type;
|
|
||||||
Node *next;
|
|
||||||
|
|
||||||
Node *key;
|
|
||||||
|
|
||||||
Node *left;
|
|
||||||
Node *right;
|
|
||||||
|
|
||||||
uint64_t ival;
|
|
||||||
double dval;
|
|
||||||
String sval;
|
|
||||||
|
|
||||||
Node *params;
|
|
||||||
Node *child;
|
|
||||||
bool no_body;
|
|
||||||
|
|
||||||
Node *cond;
|
|
||||||
|
|
||||||
String tagname;
|
|
||||||
String attr_name;
|
|
||||||
Node *attr_value;
|
|
||||||
|
|
||||||
String for_var1;
|
|
||||||
String for_var2;
|
|
||||||
Node *for_set;
|
|
||||||
|
|
||||||
String func_name;
|
|
||||||
Node *func_args;
|
|
||||||
Node *func_body;
|
|
||||||
|
|
||||||
String var_name;
|
|
||||||
Node *var_value;
|
|
||||||
|
|
||||||
String include_path;
|
|
||||||
Node* include_next;
|
|
||||||
Node* include_root;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
Node *node;
|
|
||||||
Node *includes;
|
|
||||||
int errlen;
|
|
||||||
} ParseResult;
|
|
||||||
|
|
||||||
void print_node(Node *node);
|
|
||||||
ParseResult parse(String src, WL_Arena *a, char *errbuf, int errmax);
|
|
||||||
|
|
||||||
#endif // WL_PARSE_INCLUDED
|
|
||||||
-501
@@ -1,501 +0,0 @@
|
|||||||
|
|
||||||
#ifndef WL_AMALGAMATION
|
|
||||||
#include "value.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define ITEMS_PER_MAP_BATCH 8
|
|
||||||
#define ITEMS_PER_ARRAY_BATCH 16
|
|
||||||
|
|
||||||
typedef struct MapItems MapItems;
|
|
||||||
struct MapItems {
|
|
||||||
MapItems *next;
|
|
||||||
Value keys [ITEMS_PER_MAP_BATCH];
|
|
||||||
Value items[ITEMS_PER_MAP_BATCH];
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
Type type;
|
|
||||||
int count;
|
|
||||||
int tail_count;
|
|
||||||
MapItems head;
|
|
||||||
MapItems *tail;
|
|
||||||
} MapValue;
|
|
||||||
|
|
||||||
typedef struct ArrayItems ArrayItems;
|
|
||||||
struct ArrayItems {
|
|
||||||
ArrayItems *next;
|
|
||||||
Value items[ITEMS_PER_ARRAY_BATCH];
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
Type type;
|
|
||||||
int count;
|
|
||||||
int tail_count;
|
|
||||||
ArrayItems head;
|
|
||||||
ArrayItems *tail;
|
|
||||||
} ArrayValue;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
Type type;
|
|
||||||
double raw;
|
|
||||||
} FloatValue;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
Type type;
|
|
||||||
int64_t raw;
|
|
||||||
} IntValue;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
Type type;
|
|
||||||
int len;
|
|
||||||
char data[];
|
|
||||||
} StringValue;
|
|
||||||
|
|
||||||
Type type_of(Value v)
|
|
||||||
{
|
|
||||||
// 000 none
|
|
||||||
// 001 true
|
|
||||||
// 010 false
|
|
||||||
// 011 int
|
|
||||||
// 100
|
|
||||||
// 101
|
|
||||||
// 110 error
|
|
||||||
// 111 pointer
|
|
||||||
|
|
||||||
switch (v & 7) {
|
|
||||||
case 0: return TYPE_NONE;
|
|
||||||
case 1: return TYPE_BOOL;
|
|
||||||
case 2: return TYPE_BOOL;
|
|
||||||
case 3: return TYPE_INT;
|
|
||||||
case 4: break;
|
|
||||||
case 5: break;
|
|
||||||
case 6: return TYPE_ERROR;
|
|
||||||
case 7: return *(Type*) ((uintptr_t) v & ~(uintptr_t) 7);
|
|
||||||
}
|
|
||||||
|
|
||||||
return TYPE_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t get_int(Value v)
|
|
||||||
{
|
|
||||||
if ((v & 7) == 3)
|
|
||||||
return (int64_t) (v >> 3);
|
|
||||||
|
|
||||||
IntValue *p = (IntValue*) v;
|
|
||||||
return p->raw;
|
|
||||||
}
|
|
||||||
|
|
||||||
float get_float(Value v)
|
|
||||||
{
|
|
||||||
FloatValue *p = (FloatValue*) v;
|
|
||||||
return p->raw;
|
|
||||||
}
|
|
||||||
|
|
||||||
String get_str(Value v)
|
|
||||||
{
|
|
||||||
StringValue *p = (StringValue*) (v & ~(uintptr_t) 7);
|
|
||||||
return (String) { p->data, p->len };
|
|
||||||
}
|
|
||||||
|
|
||||||
static MapValue *get_map(Value v)
|
|
||||||
{
|
|
||||||
return (MapValue*) (v & ~(uintptr_t) 7);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ArrayValue *get_array(Value v)
|
|
||||||
{
|
|
||||||
return (ArrayValue*) (v & ~(uintptr_t) 7);
|
|
||||||
}
|
|
||||||
|
|
||||||
Value make_int(WL_Arena *a, int64_t x)
|
|
||||||
{
|
|
||||||
if (x <= (int64_t) (1ULL << 60)-1 && x >= (int64_t) -(1ULL << 60))
|
|
||||||
return ((Value) x << 3) | 3;
|
|
||||||
|
|
||||||
IntValue *v = alloc(a, (int) sizeof(IntValue), _Alignof(IntValue));
|
|
||||||
if (v == NULL)
|
|
||||||
return VALUE_ERROR;
|
|
||||||
|
|
||||||
v->type = TYPE_INT;
|
|
||||||
v->raw = x;
|
|
||||||
|
|
||||||
assert(((uintptr_t) v & 7) == 0);
|
|
||||||
return ((Value) v) | 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
Value make_float(WL_Arena *a, float x)
|
|
||||||
{
|
|
||||||
FloatValue *v = alloc(a, (int) sizeof(FloatValue), _Alignof(FloatValue));
|
|
||||||
if (v == NULL)
|
|
||||||
return VALUE_ERROR;
|
|
||||||
|
|
||||||
v->type = TYPE_FLOAT;
|
|
||||||
v->raw = x;
|
|
||||||
|
|
||||||
assert(((uintptr_t) v & 7) == 0);
|
|
||||||
return ((Value) v) | 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
Value make_str(WL_Arena *a, String x) // TODO: This should reuse the string contents when possible
|
|
||||||
{
|
|
||||||
StringValue *v = alloc(a, (int) sizeof(StringValue) + x.len, 8);
|
|
||||||
if (v == NULL)
|
|
||||||
return VALUE_ERROR;
|
|
||||||
|
|
||||||
v->type = TYPE_STRING;
|
|
||||||
v->len = x.len;
|
|
||||||
memcpy(v->data, x.ptr, x.len);
|
|
||||||
|
|
||||||
assert(((uintptr_t) v & 7) == 0);
|
|
||||||
return ((Value) v) | 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
Value make_map(WL_Arena *a)
|
|
||||||
{
|
|
||||||
MapValue *m = alloc(a, (int) sizeof(MapValue), _Alignof(MapValue));
|
|
||||||
if (m == NULL)
|
|
||||||
return VALUE_ERROR;
|
|
||||||
|
|
||||||
m->type = TYPE_MAP;
|
|
||||||
m->count = 0;
|
|
||||||
m->tail_count = 0;
|
|
||||||
m->tail = &m->head;
|
|
||||||
m->head.next = NULL;
|
|
||||||
|
|
||||||
return (Value) m | 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
Value make_array(WL_Arena *a)
|
|
||||||
{
|
|
||||||
ArrayValue *v = alloc(a, (int) sizeof(ArrayValue), _Alignof(ArrayValue));
|
|
||||||
if (v == NULL)
|
|
||||||
return VALUE_ERROR;
|
|
||||||
|
|
||||||
v->type = TYPE_ARRAY;
|
|
||||||
v->count = 0;
|
|
||||||
v->tail_count = 0;
|
|
||||||
v->tail = &v->head;
|
|
||||||
v->head.next = NULL;
|
|
||||||
|
|
||||||
return (Value) v | 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
int map_select(Value map, Value key, Value *val)
|
|
||||||
{
|
|
||||||
MapValue *p = get_map(map);
|
|
||||||
MapItems *batch = &p->head;
|
|
||||||
while (batch) {
|
|
||||||
|
|
||||||
int num = ITEMS_PER_MAP_BATCH;
|
|
||||||
if (batch->next == NULL)
|
|
||||||
num = p->tail_count;
|
|
||||||
|
|
||||||
for (int i = 0; i < num; i++)
|
|
||||||
if (valeq(batch->keys[i], key)) {
|
|
||||||
*val = batch->items[i];
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
batch = batch->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Value *map_select_by_index(Value map, int key)
|
|
||||||
{
|
|
||||||
MapValue *p = get_map(map);
|
|
||||||
MapItems *batch = &p->head;
|
|
||||||
int cursor = 0;
|
|
||||||
while (batch) {
|
|
||||||
|
|
||||||
int num = ITEMS_PER_MAP_BATCH;
|
|
||||||
if (batch->next == NULL)
|
|
||||||
num = p->tail_count;
|
|
||||||
|
|
||||||
if (cursor <= key && key < cursor + num)
|
|
||||||
return &batch->keys[key - cursor];
|
|
||||||
|
|
||||||
batch = batch->next;
|
|
||||||
cursor += num;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int map_insert(WL_Arena *a, Value map, Value key, Value val)
|
|
||||||
{
|
|
||||||
MapValue *p = get_map(map);
|
|
||||||
if (p->tail_count == ITEMS_PER_MAP_BATCH) {
|
|
||||||
|
|
||||||
MapItems *batch = alloc(a, (int) sizeof(MapItems), _Alignof(MapItems));
|
|
||||||
if (batch == NULL)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
batch->next = NULL;
|
|
||||||
if (p->tail)
|
|
||||||
p->tail->next = batch;
|
|
||||||
p->tail = batch;
|
|
||||||
p->tail_count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
p->tail->keys[p->tail_count] = key;
|
|
||||||
p->tail->items[p->tail_count] = val;
|
|
||||||
p->tail_count++;
|
|
||||||
p->count++;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Value *array_select(Value array, int key)
|
|
||||||
{
|
|
||||||
ArrayValue *p = get_array(array);
|
|
||||||
ArrayItems *batch = &p->head;
|
|
||||||
int cursor = 0;
|
|
||||||
while (batch) {
|
|
||||||
|
|
||||||
int num = ITEMS_PER_ARRAY_BATCH;
|
|
||||||
if (batch->next == NULL)
|
|
||||||
num = p->tail_count;
|
|
||||||
|
|
||||||
if (cursor <= key && key < cursor + num)
|
|
||||||
return &batch->items[key - cursor];
|
|
||||||
|
|
||||||
batch = batch->next;
|
|
||||||
cursor += num;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int array_append(WL_Arena *a, Value array, Value val)
|
|
||||||
{
|
|
||||||
ArrayValue *p = get_array(array);
|
|
||||||
if (p->tail_count == ITEMS_PER_ARRAY_BATCH) {
|
|
||||||
|
|
||||||
ArrayItems *batch = alloc(a, (int) sizeof(ArrayItems), _Alignof(ArrayItems));
|
|
||||||
if (batch == NULL)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
batch->next = NULL;
|
|
||||||
|
|
||||||
if (p->tail)
|
|
||||||
p->tail->next = batch;
|
|
||||||
p->tail = batch;
|
|
||||||
p->tail_count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
p->tail->items[p->tail_count] = val;
|
|
||||||
p->tail_count++;
|
|
||||||
p->count++;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool valeq(Value a, Value b)
|
|
||||||
{
|
|
||||||
Type t1 = type_of(a);
|
|
||||||
Type t2 = type_of(b);
|
|
||||||
|
|
||||||
if (t1 != t2)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
switch (t1) {
|
|
||||||
|
|
||||||
case TYPE_NONE:
|
|
||||||
return VALUE_TRUE;
|
|
||||||
|
|
||||||
case TYPE_BOOL:
|
|
||||||
return a == b;
|
|
||||||
|
|
||||||
case TYPE_INT:
|
|
||||||
return get_int(a) == get_int(b);
|
|
||||||
|
|
||||||
case TYPE_FLOAT:
|
|
||||||
return get_float(a) == get_float(b);
|
|
||||||
|
|
||||||
case TYPE_MAP:
|
|
||||||
return false; // TODO
|
|
||||||
|
|
||||||
case TYPE_ARRAY:
|
|
||||||
return false; // TODO
|
|
||||||
|
|
||||||
case TYPE_STRING:
|
|
||||||
return streq(get_str(a), get_str(b));
|
|
||||||
|
|
||||||
case TYPE_ERROR:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool valgrt(Value a, Value b)
|
|
||||||
{
|
|
||||||
Type t1 = type_of(a);
|
|
||||||
Type t2 = type_of(b);
|
|
||||||
|
|
||||||
if (t1 != t2)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
switch (t1) {
|
|
||||||
|
|
||||||
case TYPE_NONE:
|
|
||||||
return VALUE_FALSE;
|
|
||||||
|
|
||||||
case TYPE_BOOL:
|
|
||||||
return VALUE_FALSE;
|
|
||||||
|
|
||||||
case TYPE_INT:
|
|
||||||
return get_int(a) > get_int(b);
|
|
||||||
|
|
||||||
case TYPE_FLOAT:
|
|
||||||
return get_float(a) > get_float(b);
|
|
||||||
|
|
||||||
case TYPE_MAP:
|
|
||||||
return false;
|
|
||||||
|
|
||||||
case TYPE_ARRAY:
|
|
||||||
return false;
|
|
||||||
|
|
||||||
case TYPE_STRING:
|
|
||||||
return false;
|
|
||||||
|
|
||||||
case TYPE_ERROR:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int value_length(Value v)
|
|
||||||
{
|
|
||||||
Type type = type_of(v);
|
|
||||||
|
|
||||||
if (type == TYPE_ARRAY)
|
|
||||||
return get_array(v)->count;
|
|
||||||
|
|
||||||
if (type == TYPE_MAP)
|
|
||||||
return get_map(v)->count;
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
char *dst;
|
|
||||||
int max;
|
|
||||||
int len;
|
|
||||||
} ToStringContext;
|
|
||||||
|
|
||||||
static void tostr_appends(ToStringContext *tostr, String x)
|
|
||||||
{
|
|
||||||
if (tostr->max > tostr->len) {
|
|
||||||
int cpy = tostr->max - tostr->len;
|
|
||||||
if (cpy > x.len)
|
|
||||||
cpy = x.len;
|
|
||||||
memcpy(tostr->dst + tostr->len, x.ptr, cpy);
|
|
||||||
}
|
|
||||||
tostr->len += x.len;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void tostr_appendi(ToStringContext *tostr, int64_t x)
|
|
||||||
{
|
|
||||||
int len;
|
|
||||||
if (tostr->max >= tostr->len)
|
|
||||||
len = snprintf(tostr->dst + tostr->len, tostr->max - tostr->len, "%" LLD, x);
|
|
||||||
else
|
|
||||||
len = snprintf(NULL, 0, "%" LLD, x);
|
|
||||||
tostr->len += len;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void tostr_appendf(ToStringContext *tostr, double x)
|
|
||||||
{
|
|
||||||
int len;
|
|
||||||
if (tostr->max >= tostr->len)
|
|
||||||
len = snprintf(tostr->dst + tostr->len, tostr->max - tostr->len, "%f", x);
|
|
||||||
else
|
|
||||||
len = snprintf(NULL, 0, "%f", x);
|
|
||||||
tostr->len += len;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void value_to_string_inner(Value v, ToStringContext *tostr)
|
|
||||||
{
|
|
||||||
switch (type_of(v)) {
|
|
||||||
|
|
||||||
case TYPE_NONE:
|
|
||||||
//tostr_appends(tostr, S("none"));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TYPE_BOOL:
|
|
||||||
// TODO
|
|
||||||
//tostr_appends(tostr, get_bool(v) ? S("true") : S("false"));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TYPE_INT:
|
|
||||||
tostr_appendi(tostr, get_int(v));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TYPE_FLOAT:
|
|
||||||
tostr_appendf(tostr, get_float(v));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TYPE_MAP:
|
|
||||||
{
|
|
||||||
tostr_appends(tostr, S("{ "));
|
|
||||||
MapValue *m = get_map(v);
|
|
||||||
MapItems *batch = &m->head;
|
|
||||||
while (batch) {
|
|
||||||
|
|
||||||
int num = ITEMS_PER_MAP_BATCH;
|
|
||||||
if (batch->next == NULL)
|
|
||||||
num = m->tail_count;
|
|
||||||
|
|
||||||
for (int i = 0; i < num; i++) {
|
|
||||||
value_to_string_inner(batch->keys[i], tostr);
|
|
||||||
tostr_appends(tostr, S(": "));
|
|
||||||
value_to_string_inner(batch->items[i], tostr);
|
|
||||||
|
|
||||||
if (batch->next != NULL || i+1 < num)
|
|
||||||
tostr_appends(tostr, S(", "));
|
|
||||||
}
|
|
||||||
|
|
||||||
batch = batch->next;
|
|
||||||
}
|
|
||||||
tostr_appends(tostr, S(" }"));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TYPE_ARRAY:
|
|
||||||
{
|
|
||||||
ArrayValue *a = get_array(v);
|
|
||||||
ArrayItems *batch = &a->head;
|
|
||||||
int cursor = 0;
|
|
||||||
while (batch) {
|
|
||||||
|
|
||||||
int num = ITEMS_PER_ARRAY_BATCH;
|
|
||||||
if (batch->next == NULL)
|
|
||||||
num = a->tail_count;
|
|
||||||
|
|
||||||
for (int i = 0; i < num; i++)
|
|
||||||
value_to_string_inner(batch->items[i], tostr);
|
|
||||||
|
|
||||||
batch = batch->next;
|
|
||||||
cursor += num;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TYPE_STRING:
|
|
||||||
tostr_appends(tostr, get_str(v));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TYPE_ERROR:
|
|
||||||
tostr_appends(tostr, S("error"));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int value_to_string(Value v, char *dst, int max)
|
|
||||||
{
|
|
||||||
ToStringContext tostr = { dst, max, 0 };
|
|
||||||
value_to_string_inner(v, &tostr);
|
|
||||||
return tostr.len;
|
|
||||||
}
|
|
||||||
-46
@@ -1,46 +0,0 @@
|
|||||||
#ifndef WL_VALUE_INCLUDED
|
|
||||||
#define WL_VALUE_INCLUDED
|
|
||||||
|
|
||||||
#ifndef WL_AMALGAMATION
|
|
||||||
#include "basic.h"
|
|
||||||
#include "includes.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define VALUE_NONE ((Value) 0)
|
|
||||||
#define VALUE_TRUE ((Value) 1)
|
|
||||||
#define VALUE_FALSE ((Value) 2)
|
|
||||||
#define VALUE_ERROR ((Value) 6)
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
TYPE_NONE,
|
|
||||||
TYPE_BOOL,
|
|
||||||
TYPE_INT,
|
|
||||||
TYPE_FLOAT,
|
|
||||||
TYPE_MAP,
|
|
||||||
TYPE_ARRAY,
|
|
||||||
TYPE_STRING,
|
|
||||||
TYPE_ERROR,
|
|
||||||
} Type;
|
|
||||||
|
|
||||||
typedef uint64_t Value;
|
|
||||||
|
|
||||||
Type type_of (Value v);
|
|
||||||
int64_t get_int (Value v);
|
|
||||||
float get_float (Value v);
|
|
||||||
String get_str (Value v);
|
|
||||||
Value make_int (WL_Arena *a, int64_t x);
|
|
||||||
Value make_float (WL_Arena *a, float x);
|
|
||||||
Value make_str (WL_Arena *a, String x);
|
|
||||||
Value make_map (WL_Arena *a);
|
|
||||||
Value make_array (WL_Arena *a);
|
|
||||||
int map_select (Value map, Value key, Value *val);
|
|
||||||
Value* map_select_by_index(Value map, int key);
|
|
||||||
int map_insert (WL_Arena *a, Value map, Value key, Value val);
|
|
||||||
Value* array_select (Value array, int key);
|
|
||||||
int array_append (WL_Arena *a, Value array, Value val);
|
|
||||||
bool valeq (Value a, Value b);
|
|
||||||
bool valgrt (Value a, Value b);
|
|
||||||
int value_length (Value v);
|
|
||||||
int value_to_string(Value v, char *dst, int max);
|
|
||||||
|
|
||||||
#endif // WL_VALUE_INCLUDED
|
|
||||||
@@ -0,0 +1,237 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "wl.h"
|
||||||
|
|
||||||
|
#define COUNT(X) (int) (sizeof(X) / sizeof((X)[0]))
|
||||||
|
|
||||||
|
struct {
|
||||||
|
int line;
|
||||||
|
char *in;
|
||||||
|
char *out;
|
||||||
|
} tests[] = {
|
||||||
|
{__LINE__, "", ""},
|
||||||
|
{__LINE__, "1", "1"},
|
||||||
|
{__LINE__, "none", ""},
|
||||||
|
{__LINE__, "true", "true"},
|
||||||
|
{__LINE__, "false", "false"},
|
||||||
|
{__LINE__, "\"Hello, world!\"", "Hello, world!"},
|
||||||
|
{__LINE__, "'Hello, world!'", "Hello, world!"},
|
||||||
|
{__LINE__, "\"\\\\\"", "\\"},
|
||||||
|
{__LINE__, "\"\\n\"", "\n"},
|
||||||
|
{__LINE__, "\"\\t\"", "\t"},
|
||||||
|
{__LINE__, "\"\\r\"", "\r"},
|
||||||
|
{__LINE__, "\"\\\"\"", "\""},
|
||||||
|
{__LINE__, "\"\\'\"", "'"},
|
||||||
|
{__LINE__, "\"\\xFF\"", "\xFF"},
|
||||||
|
{__LINE__, "\"\\x0F\"", "\x0F"},
|
||||||
|
{__LINE__, "\"\\xF0\"", "\xF0"},
|
||||||
|
{__LINE__, "[1, 2, 3]", "123"},
|
||||||
|
{__LINE__, "+{}", "{}"},
|
||||||
|
{__LINE__, "+{a:1}", "{a: 1}"},
|
||||||
|
{__LINE__, "+{a:1,b:2,c:3}", "{a: 1, b: 2, c: 3}"},
|
||||||
|
{__LINE__, "10-1-2", "7"},
|
||||||
|
{__LINE__, "1+2*3", "7"},
|
||||||
|
{__LINE__, "(1+2)*3", "9"},
|
||||||
|
{__LINE__, "1+(2*3)", "7"},
|
||||||
|
{__LINE__, "1+2", "3"},
|
||||||
|
{__LINE__, "2-1", "1"},
|
||||||
|
{__LINE__, "1-2", "-1"},
|
||||||
|
{__LINE__, "2*3", "6"},
|
||||||
|
{__LINE__, "2/3", "0"},
|
||||||
|
{__LINE__, "4/2", "2"},
|
||||||
|
{__LINE__, "0%2", "0"},
|
||||||
|
{__LINE__, "3%2", "1"},
|
||||||
|
{__LINE__, "1.0+2.0", "3.00"},
|
||||||
|
{__LINE__, "2.0-1.0", "1.00"},
|
||||||
|
{__LINE__, "1.0-2.0", "-1.00"},
|
||||||
|
{__LINE__, "2.0*3.0", "6.00"},
|
||||||
|
{__LINE__, "2.0/3.0", "0.67"},
|
||||||
|
{__LINE__, "4.0/2.0", "2.00"},
|
||||||
|
{__LINE__, "1+2.0", "3.00"},
|
||||||
|
{__LINE__, "2-1.0", "1.00"},
|
||||||
|
{__LINE__, "1-2.0", "-1.00"},
|
||||||
|
{__LINE__, "2*3.0", "6.00"},
|
||||||
|
{__LINE__, "2/3.0", "0.67"},
|
||||||
|
{__LINE__, "4/2.0", "2.00"},
|
||||||
|
{__LINE__, "1.0+2", "3.00"},
|
||||||
|
{__LINE__, "2.0-1", "1.00"},
|
||||||
|
{__LINE__, "1.0-2", "-1.00"},
|
||||||
|
{__LINE__, "2.0*3", "6.00"},
|
||||||
|
{__LINE__, "2.0/3", "0.67"},
|
||||||
|
{__LINE__, "4.0/2", "2.00"},
|
||||||
|
{__LINE__, "len []", "0"},
|
||||||
|
{__LINE__, "len [1, 2, 3]", "3"},
|
||||||
|
{__LINE__, "len {}", "0"},
|
||||||
|
{__LINE__, "len {a:1}", "1"},
|
||||||
|
{__LINE__, "len {a:1,b:2,c:3}", "3"},
|
||||||
|
{__LINE__, "-12", "-12"},
|
||||||
|
{__LINE__, "-12.34", "-12.34"},
|
||||||
|
{__LINE__, "1<2", "true"},
|
||||||
|
{__LINE__, "2<1", "false"},
|
||||||
|
{__LINE__, "1>2", "false"},
|
||||||
|
{__LINE__, "2>1", "true"},
|
||||||
|
{__LINE__, "1==1", "true"},
|
||||||
|
{__LINE__, "1==2", "false"},
|
||||||
|
{__LINE__, "1!=1", "false"},
|
||||||
|
{__LINE__, "1!=2", "true"},
|
||||||
|
{__LINE__, "let a = 1\na", "1"},
|
||||||
|
{__LINE__, "let a = 1\na = 2\na", "2"},
|
||||||
|
{__LINE__, "[5, 6, 7][0]", "5"},
|
||||||
|
{__LINE__, "[5, 6, 7][1]", "6"},
|
||||||
|
{__LINE__, "[5, 6, 7][2]", "7"},
|
||||||
|
{__LINE__, "+{a:5,b:6,c:7}.a", "5"},
|
||||||
|
{__LINE__, "+{a:5,b:6,c:7}.b", "6"},
|
||||||
|
{__LINE__, "+{a:5,b:6,c:7}.c", "7"},
|
||||||
|
{__LINE__, "+{a:5,b:6,c:7}['a']", "5"},
|
||||||
|
{__LINE__, "+{a:5,b:6,c:7}['b']", "6"},
|
||||||
|
{__LINE__, "+{a:5,b:6,c:7}['c']", "7"},
|
||||||
|
{__LINE__, "let x = 1\n{\nlet x = 2\nx\n}", "2"},
|
||||||
|
{__LINE__, "let x = {a:5,b:6,c:7}\nx.a = true\nx", "{a: true, b: 6, c: 7}"},
|
||||||
|
{__LINE__, "let x = {a:5,b:6,c:7}\nx.b = true\nx", "{a: 5, b: true, c: 7}"},
|
||||||
|
{__LINE__, "let x = {a:5,b:6,c:7}\nx.c = true\nx", "{a: 5, b: 6, c: true}"},
|
||||||
|
{__LINE__, "let x = {a:5,b:6,c:7}\nx['a'] = true\nx", "{a: true, b: 6, c: 7}"},
|
||||||
|
{__LINE__, "let x = {a:5,b:6,c:7}\nx['b'] = true\nx", "{a: 5, b: true, c: 7}"},
|
||||||
|
{__LINE__, "let x = {a:5,b:6,c:7}\nx['c'] = true\nx", "{a: 5, b: 6, c: true}"},
|
||||||
|
{__LINE__, "let a\nlet b\nlet c\na = b = c = 5\n", ""},
|
||||||
|
{__LINE__, "let a\nlet b\nlet c\na = b = c = 5\na\nb\nc", "555"},
|
||||||
|
{__LINE__, "if 1 < 2: 1\n2", "12"},
|
||||||
|
{__LINE__, "if 1 > 2: 1\n2", "2"},
|
||||||
|
{__LINE__, "if 1 < 2: 1 else 2\n3", "13"},
|
||||||
|
{__LINE__, "if 1 > 2: 1 else 2\n3", "23"},
|
||||||
|
{__LINE__, "let i = 0\nwhile i < 3: {\ntrue\ni = i + 1\n}\n", "truetruetrue"},
|
||||||
|
{__LINE__, "for a in ['A', 'B', 'C']: a", "ABC"},
|
||||||
|
{__LINE__, "for a, b in ['A', 'B', 'C']: { a\n b }", "A0B1C2"},
|
||||||
|
{__LINE__, "for a in {x:1,y:2,z:3}: a", "xyz"},
|
||||||
|
{__LINE__, "for a, b in {x:1,y:2,z:3}: { a\n b }", "x0y1z2"},
|
||||||
|
{__LINE__, "procedure P() 0", ""},
|
||||||
|
{__LINE__, "procedure P(a) a", ""},
|
||||||
|
{__LINE__, "procedure P(a, b, c) { a\nb\nc }", ""},
|
||||||
|
{__LINE__, "procedure P() 0\nP()", "0"},
|
||||||
|
{__LINE__, "procedure P(a, b, c) { a\nb\nc }\nP()", ""},
|
||||||
|
{__LINE__, "procedure P(a, b, c) { a\nb\nc }\nP(1)", "1"},
|
||||||
|
{__LINE__, "procedure P(a, b, c) { a\nb\nc }\nP(1, 2)", "12"},
|
||||||
|
{__LINE__, "procedure P(a, b, c) { a\nb\nc }\nP(1, 2, 3)", "123"},
|
||||||
|
{__LINE__, "procedure N(n) n\nN(1) + N(2)", "3"},
|
||||||
|
{__LINE__, "P()\nprocedure P() 1", "1"},
|
||||||
|
{__LINE__, "P()\nprocedure P() 1\n{\nprocedure P() 2\n}", "1"},
|
||||||
|
{__LINE__, "procedure P() 1\nP()\n{\nprocedure P() 2\n}", "1"},
|
||||||
|
{__LINE__, "procedure P() 1\n{\nP()\nprocedure P() 2\n}", "2"},
|
||||||
|
{__LINE__, "procedure P() 1\n{\nprocedure P() 2\nP()\n}", "2"},
|
||||||
|
{__LINE__, "procedure P() 1\n{\nprocedure P() 2\n}\nP()", "1"},
|
||||||
|
{__LINE__, "<a>Hello, world!</a>", "<a>Hello, world!</a>"},
|
||||||
|
{__LINE__, "<ul><li>A</li><li>B</li><li>C</li></ul>", "<ul><li>A</li><li>B</li><li>C</li></ul>"},
|
||||||
|
{__LINE__, "let a = <ul><li>A</li><li>B</li><li>C</li></ul>", ""},
|
||||||
|
{__LINE__, "let a = <ul><li>A</li><li>B</li><li>C</li></ul>\na", "<ul><li>A</li><li>B</li><li>C</li></ul>"},
|
||||||
|
};
|
||||||
|
|
||||||
|
int run_test(char *in, char *out, void *mem, int cap, int test_line)
|
||||||
|
{
|
||||||
|
WL_Arena arena = { mem, cap, 0 };
|
||||||
|
WL_Program program;
|
||||||
|
|
||||||
|
WL_Compiler *c = wl_compiler_init(&arena);
|
||||||
|
if (c == NULL) {
|
||||||
|
fprintf(stderr, "Error: Out of memory");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
WL_AddResult res = wl_compiler_add(c, (WL_String) { in, strlen(in) });
|
||||||
|
if (res.type == WL_ADD_ERROR) {
|
||||||
|
fprintf(stderr, "Error: %s\n", wl_compiler_error(c).ptr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (res.type != WL_ADD_LINK) {
|
||||||
|
fprintf(stderr, "Error: Unexpected compiler state\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = wl_compiler_link(c, &program);
|
||||||
|
if (ret < 0) {
|
||||||
|
WL_String err = wl_compiler_error(c);
|
||||||
|
fprintf(stderr, "Error: %s\n", err.ptr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
WL_Runtime *rt = wl_runtime_init(&arena, program);
|
||||||
|
if (rt == NULL) {
|
||||||
|
fprintf(stderr, "Error: Invalid program or out of memory\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char output[1<<10];
|
||||||
|
int outlen = 0;
|
||||||
|
|
||||||
|
for (bool done = false; !done; ) {
|
||||||
|
WL_EvalResult res = wl_runtime_eval(rt);
|
||||||
|
switch (res.type) {
|
||||||
|
|
||||||
|
case WL_EVAL_NONE:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WL_EVAL_DONE:
|
||||||
|
done = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WL_EVAL_ERROR:
|
||||||
|
printf("Error: %s (test at line %d)\n", wl_runtime_error(rt).ptr, test_line);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case WL_EVAL_OUTPUT:
|
||||||
|
if ((int) sizeof(output) - outlen < res.str.len) {
|
||||||
|
printf("Error: Output is too long\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memcpy(output + outlen, res.str.ptr, res.str.len);
|
||||||
|
outlen += res.str.len;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WL_EVAL_SYSVAR:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WL_EVAL_SYSCALL:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//printf("[%s] -> [%.*s]\n", in, outlen, output);
|
||||||
|
|
||||||
|
if (outlen != strlen(out) || memcmp(out, output, outlen)) {
|
||||||
|
fprintf(stderr, "Error: Output mistmatch\n");
|
||||||
|
fprintf(stderr, " Source : [%s]\n", in);
|
||||||
|
fprintf(stderr, " Output : [%.*s]\n", outlen, output);
|
||||||
|
fprintf(stderr, " Expected: [%s]\n", out);
|
||||||
|
#if 1
|
||||||
|
fprintf(stderr, " Program:\n");
|
||||||
|
char buf[1<<10];
|
||||||
|
int len = wl_dump_program(program, buf, sizeof(buf));
|
||||||
|
if (len < 0)
|
||||||
|
fprintf(stderr, " (Invalid program)\n");
|
||||||
|
else if (len >= sizeof(buf)) {
|
||||||
|
fprintf(stderr, " (Program disassembly is too big)\n");
|
||||||
|
} else {
|
||||||
|
buf[len] = '\0';
|
||||||
|
fprintf(stderr, "%s", buf);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
int cap = 1<<20;
|
||||||
|
char *mem = malloc(cap);
|
||||||
|
if (mem == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
for (int i = 0; i < COUNT(tests); i++)
|
||||||
|
run_test(tests[i].in, tests[i].out, mem, cap, tests[i].line);
|
||||||
|
|
||||||
|
free(mem);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user