Initial upload

This commit is contained in:
ryanfleury
2021-01-20 11:19:34 -07:00
commit 90fc1de709
30 changed files with 5769 additions and 0 deletions
+12
View File
@@ -0,0 +1,12 @@
*.dll
*.exe
*.ilk
*.obj
*.out
*.zip
*.pdb
*.dbg
*.lib
*.exp
build
build/*
+50
View File
@@ -0,0 +1,50 @@
@echo off
echo ~~~ Metadesk Build ~~~
set compile_flags= /nologo /Zi /FC /MP /I..\source\
if not exist build mkdir build
pushd build
echo.
echo ~~~ Build All Samples ~~~
cl %compile_flags% ..\samples\old_style_custom_layer.c
cl %compile_flags% ..\samples\static_site_generator\static_site_generator.c
cl %compile_flags% ..\samples\output_parse\output_parse.c
echo.
echo ~~~ Build All Tests ~~~
cl %compile_flags% ..\tests\sanity_tests.c
cl %compile_flags% ..\tests\unicode_test.c
cl %compile_flags% ..\tests\cpp_build_test.cpp
popd
echo.
echo ~~~ Running Sanity Tests ~~~
pushd build
sanity_tests.exe
popd
echo.
echo ~~~ Running Static Site Generator Sample ~~~
pushd samples
pushd static_site_generator
pushd example_site
if not exist generated mkdir generated
pushd generated
..\..\..\..\build\static_site_generator.exe --siteinfo ..\site_info.md --pagedir ..\
popd
popd
popd
popd
echo.
echo ~~~ Running Output Parse Sample ~~~
pushd samples
pushd output_parse
pushd examples
if not exist output mkdir output
pushd output
..\..\..\..\build\output_parse.exe ..\example.md ..\example2.md
popd
popd
popd
popd
+44
View File
@@ -0,0 +1,44 @@
version(1);
project_name = "metadesk";
patterns =
{
"*.c",
"*.cpp",
"*.md",
"*.h",
"*.bat",
"*.sh",
"*.4coder",
"*.txt",
};
blacklist_patterns =
{
".*",
};
load_paths =
{
{
{ {"."}, .recursive = true, .relative = true }, .os = "win"
},
};
command_list =
{
{
.name = "build",
.out = "*compilation*",
.footer_panel = true,
.save_dirty_files = true,
.cursor_at_end = false,
.cmd =
{
{ "build.bat", .os = "win" },
},
},
};
fkey_command[1] = "build";
+47
View File
@@ -0,0 +1,47 @@
// Sample code for old Data Desk style custom layer, adapted to Metadesk.
#include "md.h"
#include "md.c"
static void
Initialize(void)
{
// Called when the program starts.
}
static void
TopLevel(MD_Node *node)
{
// Called for every top-level node that is parsed from passed files.
}
static void
CleanUp(void)
{
// Runs before the program ends.
}
int main(int argument_count, char **arguments)
{
// NOTE(rjf): Parse all the files passed in via command line.
MD_Node *first = MD_NilNode();
MD_Node *last = MD_NilNode();
for(int i = 1; i < argument_count; i += 1)
{
MD_Node *root = MD_ParseWholeFile(MD_S8CString(arguments[i]));
MD_PushSibling(&first, &last, MD_NilNode(), root);
}
// NOTE(rjf): Call "custom layer" back.
Initialize();
for(MD_EachNode(root, first))
{
for(MD_EachNode(node, root->first_child))
{
TopLevel(node);
}
}
CleanUp();
return 0;
}
+8
View File
@@ -0,0 +1,8 @@
@struct foo : {
x : ([MAX_PATH]char),
}
@struct @test_tag bar : {
y : float,
z: i32,
}
+20
View File
@@ -0,0 +1,20 @@
@sets a_set : {
named_set : {1, 3, 7},
{not, named, set},
@tag_with_params(p1, p2, p3) { e1, e2, e3 },
@tag_with_params(p1, p2, p3) @tag2_with_params(p, pp, ppp) { e1, e2, e3 },
}
@empty_set empty : {
}
@tagged_unnamed_set {
a_label: {unnamed_set_element}
}
@showcase_mixed_set_scoping {
@symbol_label
+: (1 2 3],
@symbol_label
*: [x y z),
}
@@ -0,0 +1,73 @@
Node {
Kind: Label,
Flags: 00001100000100000000000000000000,
Flag Names: BraceLeft, BraceRight, Identifier,
String: foo,
Whole String: foo,
Tag: @struct
Node {
Kind: Label,
Flags: 11000001000100000000000000000000,
Flag Names: ParenLeft, ParenRight, BeforeComma, Identifier,
String: x,
Whole String: x,
Node {
Kind: UnnamedSet,
Flags: 00110000000000000000000000000000,
Flag Names: BracketLeft, BracketRight,
Node {
Kind: Label,
Flags: 00000000000100000000000000000000,
Flag Names: Identifier,
String: MAX_PATH,
Whole String: MAX_PATH,
}
}
Node {
Kind: Label,
Flags: 00000000000100000000000000000000,
Flag Names: Identifier,
String: char,
Whole String: char,
}
}
}
Node {
Kind: Label,
Flags: 00001100000100000000000000000000,
Flag Names: BraceLeft, BraceRight, Identifier,
String: bar,
Whole String: bar,
Tag: @struct
Tag: @test_tag
Node {
Kind: Label,
Flags: 00000000000100000000000000000000,
Flag Names: Identifier,
String: y,
Whole String: y,
Node {
Kind: Label,
Flags: 00000001000100000000000000000000,
Flag Names: BeforeComma, Identifier,
String: float,
Whole String: float,
}
}
Node {
Kind: Label,
Flags: 00000000000100000000000000000000,
Flag Names: Identifier,
String: z,
Whole String: z,
Node {
Kind: Label,
Flags: 00000001000100000000000000000000,
Flag Names: BeforeComma, Identifier,
String: i32,
Whole String: i32,
}
}
}
@@ -0,0 +1,282 @@
Node {
Kind: Label,
Flags: 00001100000100000000000000000000,
Flag Names: BraceLeft, BraceRight, Identifier,
String: a_set,
Whole String: a_set,
Tag: @sets
Node {
Kind: Label,
Flags: 00001101000100000000000000000000,
Flag Names: BraceLeft, BraceRight, BeforeComma, Identifier,
String: named_set,
Whole String: named_set,
Node {
Kind: Label,
Flags: 00000001001000000000000000000000,
Flag Names: BeforeComma, Numeric,
String: 1,
Whole String: 1,
}
Node {
Kind: Label,
Flags: 00000001011000000000000000000000,
Flag Names: BeforeComma, AfterComma, Numeric,
String: 3,
Whole String: 3,
}
Node {
Kind: Label,
Flags: 00000000011000000000000000000000,
Flag Names: AfterComma, Numeric,
String: 7,
Whole String: 7,
}
}
Node {
Kind: UnnamedSet,
Flags: 00001101010000000000000000000000,
Flag Names: BraceLeft, BraceRight, BeforeComma, AfterComma,
Node {
Kind: Label,
Flags: 00000001000100000000000000000000,
Flag Names: BeforeComma, Identifier,
String: not,
Whole String: not,
}
Node {
Kind: Label,
Flags: 00000001010100000000000000000000,
Flag Names: BeforeComma, AfterComma, Identifier,
String: named,
Whole String: named,
}
Node {
Kind: Label,
Flags: 00000000010100000000000000000000,
Flag Names: AfterComma, Identifier,
String: set,
Whole String: set,
}
}
Node {
Kind: UnnamedSet,
Flags: 00001101010000000000000000000000,
Flag Names: BraceLeft, BraceRight, BeforeComma, AfterComma,
Tag: @tag_with_params
Tag Children{
Node {
Kind: Label,
Flags: 00000001000100000000000000000000,
Flag Names: BeforeComma, Identifier,
String: p1,
Whole String: p1,
}
Node {
Kind: Label,
Flags: 00000001010100000000000000000000,
Flag Names: BeforeComma, AfterComma, Identifier,
String: p2,
Whole String: p2,
}
Node {
Kind: Label,
Flags: 00000000010100000000000000000000,
Flag Names: AfterComma, Identifier,
String: p3,
Whole String: p3,
}
}
Node {
Kind: Label,
Flags: 00000001000100000000000000000000,
Flag Names: BeforeComma, Identifier,
String: e1,
Whole String: e1,
}
Node {
Kind: Label,
Flags: 00000001010100000000000000000000,
Flag Names: BeforeComma, AfterComma, Identifier,
String: e2,
Whole String: e2,
}
Node {
Kind: Label,
Flags: 00000000010100000000000000000000,
Flag Names: AfterComma, Identifier,
String: e3,
Whole String: e3,
}
}
Node {
Kind: UnnamedSet,
Flags: 00001101010000000000000000000000,
Flag Names: BraceLeft, BraceRight, BeforeComma, AfterComma,
Tag: @tag_with_params
Tag Children{
Node {
Kind: Label,
Flags: 00000001000100000000000000000000,
Flag Names: BeforeComma, Identifier,
String: p1,
Whole String: p1,
}
Node {
Kind: Label,
Flags: 00000001010100000000000000000000,
Flag Names: BeforeComma, AfterComma, Identifier,
String: p2,
Whole String: p2,
}
Node {
Kind: Label,
Flags: 00000000010100000000000000000000,
Flag Names: AfterComma, Identifier,
String: p3,
Whole String: p3,
}
}
Tag: @tag2_with_params
Tag Children{
Node {
Kind: Label,
Flags: 00000001000100000000000000000000,
Flag Names: BeforeComma, Identifier,
String: p,
Whole String: p,
}
Node {
Kind: Label,
Flags: 00000001010100000000000000000000,
Flag Names: BeforeComma, AfterComma, Identifier,
String: pp,
Whole String: pp,
}
Node {
Kind: Label,
Flags: 00000000010100000000000000000000,
Flag Names: AfterComma, Identifier,
String: ppp,
Whole String: ppp,
}
}
Node {
Kind: Label,
Flags: 00000001000100000000000000000000,
Flag Names: BeforeComma, Identifier,
String: e1,
Whole String: e1,
}
Node {
Kind: Label,
Flags: 00000001010100000000000000000000,
Flag Names: BeforeComma, AfterComma, Identifier,
String: e2,
Whole String: e2,
}
Node {
Kind: Label,
Flags: 00000000010100000000000000000000,
Flag Names: AfterComma, Identifier,
String: e3,
Whole String: e3,
}
}
}
Node {
Kind: Label,
Flags: 00001100000100000000000000000000,
Flag Names: BraceLeft, BraceRight, Identifier,
String: empty,
Whole String: empty,
Tag: @empty_set
}
Node {
Kind: UnnamedSet,
Flags: 00001100000000000000000000000000,
Flag Names: BraceLeft, BraceRight,
Tag: @tagged_unnamed_set
Node {
Kind: Label,
Flags: 00001100000100000000000000000000,
Flag Names: BraceLeft, BraceRight, Identifier,
String: a_label,
Whole String: a_label,
Node {
Kind: Label,
Flags: 00000000000100000000000000000000,
Flag Names: Identifier,
String: unnamed_set_element,
Whole String: unnamed_set_element,
}
}
}
Node {
Kind: UnnamedSet,
Flags: 00001100000000000000000000000000,
Flag Names: BraceLeft, BraceRight,
Tag: @showcase_mixed_set_scoping
Node {
Kind: Label,
Flags: 10010001000000000000000000000000,
Flag Names: ParenLeft, BracketRight, BeforeComma,
String: +,
Whole String: +,
Tag: @symbol_label
Node {
Kind: Label,
Flags: 00000000001000000000000000000000,
Flag Names: Numeric,
String: 1,
Whole String: 1,
}
Node {
Kind: Label,
Flags: 00000000001000000000000000000000,
Flag Names: Numeric,
String: 2,
Whole String: 2,
}
Node {
Kind: Label,
Flags: 00000000001000000000000000000000,
Flag Names: Numeric,
String: 3,
Whole String: 3,
}
}
Node {
Kind: Label,
Flags: 01100001010000000000000000000000,
Flag Names: ParenRight, BracketLeft, BeforeComma, AfterComma,
String: *,
Whole String: *,
Tag: @symbol_label
Node {
Kind: Label,
Flags: 00000000000100000000000000000000,
Flag Names: Identifier,
String: x,
Whole String: x,
}
Node {
Kind: Label,
Flags: 00000000000100000000000000000000,
Flag Names: Identifier,
String: y,
Whole String: y,
}
Node {
Kind: Label,
Flags: 00000000000100000000000000000000,
Flag Names: Identifier,
String: z,
Whole String: z,
}
}
}
+86
View File
@@ -0,0 +1,86 @@
// Sample code to print out the data from MD_ParseWholeFile
#include "md.h"
#include "md.c"
#define INDENT_SPACES 2
static void Print(FILE* file, int indent_count, char* fmt, ...) {
for(int i = 0; i < indent_count*INDENT_SPACES; i += 1)
{
fprintf(file, " ");
}
va_list args;
va_start(args, fmt);
vfprintf(file, fmt, args);
va_end(args);
}
static void PrintNode(MD_Node* node, FILE* file, int indent_count) {
Print(file, indent_count, "Node {\n");
Print(file, indent_count+1, "Kind: %.*s,\n", MD_StringExpand(MD_StringFromNodeKind(node->kind)));
int flags_bits = sizeof(node->flags)*8;
char binary_flags[sizeof(node->flags)*8+1];
binary_flags[flags_bits] = '\0';
int flag_index = 0;
MD_NodeFlags flags = node->flags;
for (int i = 0; i < flags_bits; i++) {
binary_flags[i] = (flags&1) ? '1' : '0';
flag_index++;
flags >>= 1;
}
Print(file, indent_count+1, "Flags: %s,\n", binary_flags);
Print(file, indent_count+1, "Flag Names: ", binary_flags);
MD_String8List flags_list = MD_StringListFromNodeFlags(node->flags);
MD_String8 flag_names = MD_JoinStringListWithSeparator(flags_list, MD_S8CString(", "));
fprintf(file, "%.*s,\n", MD_StringExpand(flag_names));
if(node->string.size > 0) Print(file, indent_count+1, "String: %.*s,\n", MD_StringExpand(node->string));
if(node->whole_string.size > 0) Print(file, indent_count+1, "Whole String: %.*s,\n", MD_StringExpand(node->whole_string));
if (node->first_tag->kind != MD_NodeKind_Nil) {
for (MD_EachNode(tag, node->first_tag)) {
Print(file, indent_count+1, "Tag: @%.*s\n", MD_StringExpand(tag->string));
if (tag->first_child->kind != MD_NodeKind_Nil) {
Print(file, indent_count+2, "Tag Children{\n");
for (MD_EachNode(arg, tag->first_child)) {
PrintNode(arg, file, indent_count+3);
}
Print(file, indent_count+2, "}\n");
}
}
}
for(MD_EachNode(child, node->first_child)) {
PrintNode(child, file, indent_count+1);
}
Print(file, indent_count, "}\n");
}
int main(int argument_count, char **arguments)
{
// NOTE(pmh): Parse all the files passed in via command line.
MD_Node *first = MD_NilNode();
MD_Node *last = MD_NilNode();
for(int i = 1; i < argument_count; i += 1)
{
MD_Node *root = MD_ParseWholeFile(MD_S8CString(arguments[i]));
MD_PushSibling(&first, &last, MD_NilNode(), root);
}
for(MD_EachNode(root, first))
{
MD_String8 code_filename = MD_TrimExtension(MD_TrimFolder(root->filename));
MD_String8 info_filename = MD_PushStringF("parsed_%.*s.txt", MD_StringExpand(code_filename));
printf("Parse Input -> Output: %.*s -> %.*s\n", MD_StringExpand(code_filename), MD_StringExpand(info_filename));
FILE* file = fopen((char *)info_filename.str, "w");
for(MD_EachNode(node, root->first_child))
{
PrintNode(node, file, 0);
fprintf(file, "\n");
}
fclose(file);
}
return 0;
}
@@ -0,0 +1,62 @@
title: "Test Blog #1"
desc: "This is my test blog."
date: (2020/11/30)
index: {"blog"}
parent: "blog_main"
@subtitle "Section 1"
"""
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse ut ornare neque, vitae finibus leo. Praesent pulvinar, urna id lobortis iaculis, velit turpis luctus tortor, quis malesuada neque neque vitae turpis. Phasellus id molestie elit. Sed elementum ipsum a ligula semper, at varius augue rutrum. Pellentesque fringilla, orci nec fringilla finibus, turpis lacus vehicula elit, nec bibendum magna est quis massa. Aliquam efficitur facilisis nibh, ut sodales sapien scelerisque nec. Integer justo sem, pellentesque et ante imperdiet, interdum placerat sem.
Nunc hendrerit lobortis commodo. Morbi felis quam, fermentum vitae libero vitae, fermentum sodales quam. Nulla bibendum tellus quis lorem sollicitudin pretium. Etiam commodo ex eget aliquet porta. In sit amet dui eleifend, mattis sem nec, tristique erat. Morbi malesuada fringilla bibendum. Integer odio ex, cursus a sodales quis, vehicula non dolor.
Praesent vitae pharetra felis. Curabitur non ex non nunc pretium feugiat eu sit amet turpis. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Aenean blandit ante leo. Vivamus iaculis mi vel sem tincidunt maximus. Aenean elementum ipsum non laoreet semper. Morbi non felis interdum lectus pulvinar mollis id non purus. Nullam eu ipsum ut turpis aliquam feugiat at non tellus. Suspendisse ornare erat quis enim ullamcorper, at rhoncus nulla suscipit.
"""
@img {"test.png", "My funny image."}
@subtitle "Section 2"
"""
Aliquam quis diam at sem interdum imperdiet. Nunc imperdiet ligula tempus nibh semper mattis. Suspendisse fringilla molestie semper. Sed felis dolor, vehicula et tempus sed, porttitor finibus lacus. Pellentesque egestas ex finibus, facilisis tellus suscipit, accumsan turpis. Ut imperdiet vitae nisl eget vestibulum. Donec eu bibendum erat, id maximus magna. Vivamus quis rhoncus justo. Morbi eget commodo lorem, vulputate varius ipsum. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer accumsan, sem vel rutrum cursus, odio enim sodales leo, sit amet rutrum odio purus at nisi. Fusce iaculis ante id nunc volutpat tincidunt. Duis nec tincidunt ipsum. Fusce vitae odio ac velit sollicitudin maximus. Vestibulum ante dui, varius auctor ante eu, gravida laoreet justo.
Vivamus dignissim mauris nec turpis convallis, vitae pellentesque sapien faucibus. Phasellus eu euismod elit, sit amet vehicula diam. Curabitur sit amet leo magna. In est erat, congue vel euismod id, venenatis vitae mauris. Vestibulum sit amet leo eget leo ornare feugiat. Interdum et malesuada fames ac ante ipsum primis in faucibus. Vivamus at efficitur ex, a mattis lorem. Maecenas blandit tincidunt tortor vel cursus. In in purus a nisi pretium venenatis in ut lacus. Maecenas venenatis rutrum laoreet. Donec aliquam leo vel risus cursus, placerat pretium lacus fringilla. Suspendisse ut porttitor mauris.
"""
@youtube {"BjdY1eXB6cY"}
@subtitle "Section 3"
@list
{
"hello",
"world",
"foo",
@list
{
"a",
"b",
"c",
},
"another list item",
}
@subtitle "Section 4"
@code
"""
char *html_tag = "p";
if(DD_NodeHasTag(node, DD_S8Lit("title")))
{
html_tag = "h1";
}
else if(DD_NodeHasTag(node, DD_S8Lit("subtitle")))
{
html_tag = "h2";
}
else if(DD_NodeHasTag(node, DD_S8Lit("code")))
{
html_tag = "pre";
}
"""
@@ -0,0 +1,17 @@
title: "Hello, Again!"
desc: "This is another test blog."
date: (2020/11/28)
index: {"blog"}
parent: "blog_main"
@subtitle "Section 1"
"""
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse ut ornare neque, vitae finibus leo. Praesent pulvinar, urna id lobortis iaculis, velit turpis luctus tortor, quis malesuada neque neque vitae turpis. Phasellus id molestie elit. Sed elementum ipsum a ligula semper, at varius augue rutrum. Pellentesque fringilla, orci nec fringilla finibus, turpis lacus vehicula elit, nec bibendum magna est quis massa. Aliquam efficitur facilisis nibh, ut sodales sapien scelerisque nec. Integer justo sem, pellentesque et ante imperdiet, interdum placerat sem.
Nunc hendrerit lobortis commodo. Morbi felis quam, fermentum vitae libero vitae, fermentum sodales quam. Nulla bibendum tellus quis lorem sollicitudin pretium. Etiam commodo ex eget aliquet porta. In sit amet dui eleifend, mattis sem nec, tristique erat. Morbi malesuada fringilla bibendum. Integer odio ex, cursus a sodales quis, vehicula non dolor.
Praesent vitae pharetra felis. Curabitur non ex non nunc pretium feugiat eu sit amet turpis. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Aenean blandit ante leo. Vivamus iaculis mi vel sem tincidunt maximus. Aenean elementum ipsum non laoreet semper. Morbi non felis interdum lectus pulvinar mollis id non purus. Nullam eu ipsum ut turpis aliquam feugiat at non tellus. Suspendisse ornare erat quis enim ullamcorper, at rhoncus nulla suscipit.
"""
@img {"test.png", "My funny image."}
@@ -0,0 +1,3 @@
title: "Blog"
@lister {"blog"}
@@ -0,0 +1,5 @@
@echo off
if not exist generated mkdir generated
pushd generated
..\..\..\build\static_site_generator.exe --siteinfo ..\site_info.md --pagedir ..\
popd
@@ -0,0 +1,91 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"><meta name="author" content="Ryan Fleury">
<meta property="og:title" content="Test Blog #1">
<meta name="twitter:title" content="Test Blog #1">
<link rel="canonical" href="https://example.site">
<meta property="og:type" content="website">
<meta property="og:url" content="https://example.site">
<meta property="og:site_name" content="Example Site">
<meta name="twitter:card" content="summary">
<meta name="twitter:site" content="@ryanjfleury">
<link rel="stylesheet" type="text/css" href="style.css">
<script src="site.js"></script>
<title>Test Blog #1 | Example Site</title>
</head>
<body>
<div class="header">
<h1>Example Site</h1>
<a href="#">Test Link 1</a>
<a href="#">Test Link 2</a>
<a href="#">Test Link 3</a>
</div>
<div class="page_content">
<div class="standalone_link_container"><a class="link" href="blog_main.html">← Back</a></div><h1 class="title">Test Blog #1</h1><h2 class="subtitle">This is my test blog.</h2><h3 class="date">30 November 2020</h3><h2 class="subtitle">Section 1</h2>
<p class="paragraph">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse ut ornare neque, vitae finibus leo. Praesent pulvinar, urna id lobortis iaculis, velit turpis luctus tortor, quis malesuada neque neque vitae turpis. Phasellus id molestie elit. Sed elementum ipsum a ligula semper, at varius augue rutrum. Pellentesque fringilla, orci nec fringilla finibus, turpis lacus vehicula elit, nec bibendum magna est quis massa. Aliquam efficitur facilisis nibh, ut sodales sapien scelerisque nec. Integer justo sem, pellentesque et ante imperdiet, interdum placerat sem.</p>
<p class="paragraph">Nunc hendrerit lobortis commodo. Morbi felis quam, fermentum vitae libero vitae, fermentum sodales quam. Nulla bibendum tellus quis lorem sollicitudin pretium. Etiam commodo ex eget aliquet porta. In sit amet dui eleifend, mattis sem nec, tristique erat. Morbi malesuada fringilla bibendum. Integer odio ex, cursus a sodales quis, vehicula non dolor.</p>
<p class="paragraph">Praesent vitae pharetra felis. Curabitur non ex non nunc pretium feugiat eu sit amet turpis. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Aenean blandit ante leo. Vivamus iaculis mi vel sem tincidunt maximus. Aenean elementum ipsum non laoreet semper. Morbi non felis interdum lectus pulvinar mollis id non purus. Nullam eu ipsum ut turpis aliquam feugiat at non tellus. Suspendisse ornare erat quis enim ullamcorper, at rhoncus nulla suscipit.
</p>
<div class="img_container"><img class="img" src="test.png"></img></div>
<h2 class="subtitle">Section 2</h2>
<p class="paragraph">
Aliquam quis diam at sem interdum imperdiet. Nunc imperdiet ligula tempus nibh semper mattis. Suspendisse fringilla molestie semper. Sed felis dolor, vehicula et tempus sed, porttitor finibus lacus. Pellentesque egestas ex finibus, facilisis tellus suscipit, accumsan turpis. Ut imperdiet vitae nisl eget vestibulum. Donec eu bibendum erat, id maximus magna. Vivamus quis rhoncus justo. Morbi eget commodo lorem, vulputate varius ipsum. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer accumsan, sem vel rutrum cursus, odio enim sodales leo, sit amet rutrum odio purus at nisi. Fusce iaculis ante id nunc volutpat tincidunt. Duis nec tincidunt ipsum. Fusce vitae odio ac velit sollicitudin maximus. Vestibulum ante dui, varius auctor ante eu, gravida laoreet justo.</p>
<p class="paragraph">Vivamus dignissim mauris nec turpis convallis, vitae pellentesque sapien faucibus. Phasellus eu euismod elit, sit amet vehicula diam. Curabitur sit amet leo magna. In est erat, congue vel euismod id, venenatis vitae mauris. Vestibulum sit amet leo eget leo ornare feugiat. Interdum et malesuada fames ac ante ipsum primis in faucibus. Vivamus at efficitur ex, a mattis lorem. Maecenas blandit tincidunt tortor vel cursus. In in purus a nisi pretium venenatis in ut lacus. Maecenas venenatis rutrum laoreet. Donec aliquam leo vel risus cursus, placerat pretium lacus fringilla. Suspendisse ut porttitor mauris.
</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/BjdY1eXB6cY" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<h2 class="subtitle">Section 3</h2>
<ul class="list">
<li class="list_item">
<p class="paragraph">hello</p>
</li>
<li class="list_item">
<p class="paragraph">world</p>
</li>
<li class="list_item">
<p class="paragraph">foo</p>
</li>
<ul class="list">
<li class="list_item">
<p class="paragraph">a</p>
</li>
<li class="list_item">
<p class="paragraph">b</p>
</li>
<li class="list_item">
<p class="paragraph">c</p>
</li>
</ul>
<li class="list_item">
<p class="paragraph">another list item</p>
</li>
</ul>
<h2 class="subtitle">Section 4</h2>
<pre class="code">
char *html_tag = "p";
if(DD_NodeHasTag(node, DD_S8Lit("title")))
{
html_tag = "h1";
}
else if(DD_NodeHasTag(node, DD_S8Lit("subtitle")))
{
html_tag = "h2";
}
else if(DD_NodeHasTag(node, DD_S8Lit("code")))
{
html_tag = "pre";
}
</pre>
</div>
<div class="footer">
<h4>Example Site Footer</h4>
<a href="#">Test Link 1</a>
<a href="#">Test Link 2</a>
<a href="#">Test Link 3</a>
</div>
</body>
</html>
@@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"><meta name="author" content="Ryan Fleury">
<meta property="og:title" content="Hello, Again!">
<meta name="twitter:title" content="Hello, Again!">
<link rel="canonical" href="https://example.site">
<meta property="og:type" content="website">
<meta property="og:url" content="https://example.site">
<meta property="og:site_name" content="Example Site">
<meta name="twitter:card" content="summary">
<meta name="twitter:site" content="@ryanjfleury">
<link rel="stylesheet" type="text/css" href="style.css">
<script src="site.js"></script>
<title>Hello, Again! | Example Site</title>
</head>
<body>
<div class="header">
<h1>Example Site</h1>
<a href="#">Test Link 1</a>
<a href="#">Test Link 2</a>
<a href="#">Test Link 3</a>
</div>
<div class="page_content">
<div class="standalone_link_container"><a class="link" href="blog_main.html">← Back</a></div><h1 class="title">Hello, Again!</h1><h2 class="subtitle">This is another test blog.</h2><h3 class="date">28 November 2020</h3><h2 class="subtitle">Section 1</h2>
<p class="paragraph">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse ut ornare neque, vitae finibus leo. Praesent pulvinar, urna id lobortis iaculis, velit turpis luctus tortor, quis malesuada neque neque vitae turpis. Phasellus id molestie elit. Sed elementum ipsum a ligula semper, at varius augue rutrum. Pellentesque fringilla, orci nec fringilla finibus, turpis lacus vehicula elit, nec bibendum magna est quis massa. Aliquam efficitur facilisis nibh, ut sodales sapien scelerisque nec. Integer justo sem, pellentesque et ante imperdiet, interdum placerat sem.</p>
<p class="paragraph">Nunc hendrerit lobortis commodo. Morbi felis quam, fermentum vitae libero vitae, fermentum sodales quam. Nulla bibendum tellus quis lorem sollicitudin pretium. Etiam commodo ex eget aliquet porta. In sit amet dui eleifend, mattis sem nec, tristique erat. Morbi malesuada fringilla bibendum. Integer odio ex, cursus a sodales quis, vehicula non dolor.</p>
<p class="paragraph">Praesent vitae pharetra felis. Curabitur non ex non nunc pretium feugiat eu sit amet turpis. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Aenean blandit ante leo. Vivamus iaculis mi vel sem tincidunt maximus. Aenean elementum ipsum non laoreet semper. Morbi non felis interdum lectus pulvinar mollis id non purus. Nullam eu ipsum ut turpis aliquam feugiat at non tellus. Suspendisse ornare erat quis enim ullamcorper, at rhoncus nulla suscipit.
</p>
<div class="img_container"><img class="img" src="test.png"></img></div>
</div>
<div class="footer">
<h4>Example Site Footer</h4>
<a href="#">Test Link 1</a>
<a href="#">Test Link 2</a>
<a href="#">Test Link 3</a>
</div>
</body>
</html>
@@ -0,0 +1,68 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"><meta name="author" content="Ryan Fleury">
<meta property="og:title" content="Blog">
<meta name="twitter:title" content="Blog">
<link rel="canonical" href="https://example.site">
<meta property="og:type" content="website">
<meta property="og:url" content="https://example.site">
<meta property="og:site_name" content="Example Site">
<meta name="twitter:card" content="summary">
<meta name="twitter:site" content="@ryanjfleury">
<link rel="stylesheet" type="text/css" href="style.css">
<script src="site.js"></script>
<title>Blog | Example Site</title>
</head>
<body>
<div class="header">
<h1>Example Site</h1>
<a href="#">Test Link 1</a>
<a href="#">Test Link 2</a>
<a href="#">Test Link 3</a>
</div>
<div class="page_content">
<h1 class="title">Blog</h1><input autofocus id="lister_search_0" class="lister_search" oninput="SearchInput(event, 0)" onkeydown="SearchKeyDown(event, 0)" placeholder="Filter..."></input><ul id="lister_0" class="lister">
<a class="lister_item_link" href="blog2.html">
<li class="lister_item">
<div class="lister_item_text">
<div class="lister_item_title">
Hello, Again!
</div>
<div class="lister_item_date">
28 November 2020
</div>
<div class="lister_item_desc">
This is another test blog.
</div>
</div>
</a>
</li>
<a class="lister_item_link" href="blog1.html">
<li class="lister_item">
<div class="lister_item_text">
<div class="lister_item_title">
Test Blog #1
</div>
<div class="lister_item_date">
30 November 2020
</div>
<div class="lister_item_desc">
This is my test blog.
</div>
</div>
</a>
</li>
</ul>
</div>
<div class="footer">
<h4>Example Site Footer</h4>
<a href="#">Test Link 1</a>
<a href="#">Test Link 2</a>
<a href="#">Test Link 3</a>
</div>
</body>
</html>
@@ -0,0 +1,61 @@
function StringFitsFilter(str, filter)
{
let match = true;
let filter_upper = filter.toUpperCase();
let filter_substrings = filter_upper.split(/[ _*]+/);
let str_upper = str.toUpperCase();
let minimum_index = 0;
for(let i = 0; i < filter_substrings.length; ++i)
{
if(filter_substrings[i].length > 0)
{
let index_of_substring = str_upper.indexOf(filter_substrings[i], minimum_index);
if(index_of_substring < 0 || index_of_substring < minimum_index)
{
match = false;
break;
}
minimum_index = index_of_substring + filter_substrings[i].length - 1;
}
}
return match;
}
function UpdateListByFilter(menu_id, filter_id)
{
let ul = document.getElementById(menu_id);
let li = ul.getElementsByTagName("li");
let input = document.getElementById(filter_id);
let filter = input.value.toUpperCase();
for(let i = 0; i < li.length; i++)
{
if(filter.length > 0)
{
let a = li[i].getElementsByTagName("a")[0];
if(StringFitsFilter(a.innerHTML, filter))
{
li[i].style.display = "";
}
else
{
li[i].style.display = "none";
}
}
else
{
li[i].style.display = "";
}
}
}
function SearchInput(event, lister_idx)
{
UpdateListByFilter("lister_"+lister_idx, "lister_search_"+lister_idx);
}
function SearchKeyDown(event, lister_idx)
{
UpdateListByFilter("lister_"+lister_idx, "lister_search_"+lister_idx);
}
@@ -0,0 +1,6 @@
*
{
padding: 0;
margins: 0;
}
@@ -0,0 +1,61 @@
function StringFitsFilter(str, filter)
{
let match = true;
let filter_upper = filter.toUpperCase();
let filter_substrings = filter_upper.split(/[ _*]+/);
let str_upper = str.toUpperCase();
let minimum_index = 0;
for(let i = 0; i < filter_substrings.length; ++i)
{
if(filter_substrings[i].length > 0)
{
let index_of_substring = str_upper.indexOf(filter_substrings[i], minimum_index);
if(index_of_substring < 0 || index_of_substring < minimum_index)
{
match = false;
break;
}
minimum_index = index_of_substring + filter_substrings[i].length - 1;
}
}
return match;
}
function UpdateListByFilter(menu_id, filter_id)
{
let ul = document.getElementById(menu_id);
let li = ul.getElementsByTagName("li");
let input = document.getElementById(filter_id);
let filter = input.value.toUpperCase();
for(let i = 0; i < li.length; i++)
{
if(filter.length > 0)
{
let a = li[i].getElementsByTagName("a")[0];
if(StringFitsFilter(a.innerHTML, filter))
{
li[i].style.display = "";
}
else
{
li[i].style.display = "none";
}
}
else
{
li[i].style.display = "";
}
}
}
function SearchInput(event, lister_idx)
{
UpdateListByFilter("lister_"+lister_idx, "lister_search_"+lister_idx);
}
function SearchKeyDown(event, lister_idx)
{
UpdateListByFilter("lister_"+lister_idx, "lister_search_"+lister_idx);
}
@@ -0,0 +1,34 @@
title: "Example Site"
desc: "My example site, used to demonstrate the static site generator example for Data Desk."
canonical_url: "https://example.site"
author: "Ryan Fleury"
twitter_handle: "@ryanjfleury"
header:
"""
<div class="header">
<h1>Example Site</h1>
<a href="#">Test Link 1</a>
<a href="#">Test Link 2</a>
<a href="#">Test Link 3</a>
</div>
"""
footer:
"""
<div class="footer">
<h4>Example Site Footer</h4>
<a href="#">Test Link 1</a>
<a href="#">Test Link 2</a>
<a href="#">Test Link 3</a>
</div>
"""
style:
"""
*
{
padding: 0;
margins: 0;
}
"""
@@ -0,0 +1,570 @@
#include "md.h"
#include "md.c"
typedef struct SiteInfo SiteInfo;
struct SiteInfo
{
MD_Node *title;
MD_Node *desc;
MD_Node *canonical_url;
MD_Node *author;
MD_Node *twitter_handle;
MD_Node *link_dictionary;
MD_Node *header;
MD_Node *footer;
MD_Node *style;
};
typedef struct PageInfo PageInfo;
struct PageInfo
{
MD_Node *title;
MD_Node *desc;
MD_Node *date;
MD_Node *parent;
MD_Node *header_image;
};
static PageInfo ParsePageInfo(MD_Node *page);
static SiteInfo ParseSiteInfo(MD_Node *site);
static MD_String8 MakeDateString(MD_Node *date);
static void GeneratePageContent(MD_NodeTable *index_table, SiteInfo *site_info, PageInfo *page_info, FILE *file, MD_Node *node);
int main(int argument_count, char **arguments)
{
//~ NOTE(rjf): Parse command line arguments.
MD_String8 site_info_path = {0};
MD_String8 page_dir_path = {0};
MD_CommandLine cmdln = MD_CommandLine_Start(argument_count, arguments);
if(!MD_CommandLine_FlagString(&cmdln, MD_S8Lit("--siteinfo"), &site_info_path) ||
!MD_CommandLine_FlagString(&cmdln, MD_S8Lit("--pagedir"), &page_dir_path))
{
fprintf(stderr, "USAGE: %s --siteinfo <path to site info file> --pagedir <path to directory with pages> ...\n", arguments[0]);
goto end;
}
//~ NOTE(rjf): Load JS.
MD_String8 js_string = MD_LoadEntireFile(MD_PushStringF("%.*s/site.js", MD_StringExpand(page_dir_path)));
//~ NOTE(rjf): Parse site info.
SiteInfo site_info = {0};
{
printf("Parsing site metadata at \"%.*s\"...\n", MD_StringExpand(site_info_path));
MD_Node *site_info_file = MD_ParseWholeFile(site_info_path);
site_info = ParseSiteInfo(site_info_file);
}
//~ NOTE(rjf): Parse pages.
MD_Node *first_root = MD_NilNode();
MD_Node *last_root = MD_NilNode();
{
printf("Searching for site pages at \"%.*s\"...\n", MD_StringExpand(page_dir_path));
MD_FileInfo file_info = {0};
for(MD_FileIter it = {0}; MD_FileIterIncrement(&it, page_dir_path, &file_info);)
{
if(MD_StringMatch(MD_ExtensionFromPath(file_info.filename), MD_S8Lit("md"), MD_StringMatchFlag_CaseInsensitive) &&
!MD_StringMatch(MD_TrimFolder(MD_TrimExtension(file_info.filename)),
MD_TrimFolder(MD_TrimExtension(site_info_path)),
MD_StringMatchFlag_CaseInsensitive |
MD_StringMatchFlag_SlashInsensitive))
{
printf("Processing site page at \"%.*s\"...\n", MD_StringExpand(file_info.filename));
MD_String8 folder = MD_FolderFromPath(page_dir_path);
MD_String8 path = MD_PushStringF("%.*s/%.*s",
MD_StringExpand(folder),
MD_StringExpand(file_info.filename));
MD_PushSibling(&first_root, &last_root, MD_NilNode(), MD_ParseWholeFile(path));
}
}
}
//~ NOTE(rjf): Generate index table.
MD_NodeTable index_table = {0};
{
for(MD_EachNode(root, first_root))
{
for(MD_EachNode(node, root->first_child))
{
if(!MD_NodeIsNil(node->first_child) && MD_StringMatch(node->string, MD_S8Lit("index"), MD_StringMatchFlag_CaseInsensitive))
{
for(MD_EachNode(index_string, node->first_child))
{
MD_NodeTable_Insert(&index_table, MD_NodeTableCollisionRule_Chain, index_string->string, root);
}
goto end_index_build;
}
}
end_index_build:;
}
}
//~ NOTE(rjf): Generate stylesheet.
if(site_info.style)
{
FILE *file = fopen("style.css", "w");
if(file)
{
fprintf(file, "%.*s", MD_StringExpand(site_info.style->string));
fclose(file);
}
}
//~ NOTE(rjf): Generate JS.
{
FILE *file = fopen("site.js", "w");
if(file)
{
fprintf(file, "%.*s", MD_StringExpand(js_string));
fclose(file);
}
}
//~ NOTE(rjf): Generate files for all roots.
for(MD_EachNode(root, first_root))
{
PageInfo page_info = ParsePageInfo(root);
MD_String8 name_without_extension = MD_TrimFolder(MD_TrimExtension(root->filename));
FILE *file = fopen(MD_PushStringF("%.*s.html", MD_StringExpand(name_without_extension)).str, "w");
if(file)
{
fprintf(file, "<!DOCTYPE html>\n");
fprintf(file, "<html lang=\"en\">\n");
MD_String8 site_title = !MD_NodeIsNil(site_info.title) ? site_info.title->string : MD_S8Lit("");
MD_String8 title = !MD_NodeIsNil(page_info.title) ? page_info.title->string : MD_S8Lit("");
MD_String8 url = !MD_NodeIsNil(site_info.canonical_url) ? site_info.canonical_url->string : MD_S8Lit("");
MD_String8 author = !MD_NodeIsNil(site_info.author) ? site_info.author->string : MD_S8Lit("");
MD_String8 twitter_handle = !MD_NodeIsNil(site_info.twitter_handle) ? site_info.twitter_handle->string : MD_S8Lit("");
// NOTE(rjf): Generate heading.
{
fprintf(file, "<head>\n");
fprintf(file, "<meta charset=\"utf-8\">\n");
fprintf(file, "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><meta name=\"author\" content=\"%.*s\">\n", MD_StringExpand(author));
fprintf(file, "<meta property=\"og:title\" content=\"%.*s\">\n", MD_StringExpand(title));
fprintf(file, "<meta name=\"twitter:title\" content=\"%.*s\">\n", MD_StringExpand(title));
fprintf(file, "<link rel=\"canonical\" href=\"%.*s\">\n", MD_StringExpand(url));
fprintf(file, "<meta property=\"og:type\" content=\"website\">\n");
fprintf(file, "<meta property=\"og:url\" content=\"%.*s\">\n", MD_StringExpand(url));
fprintf(file, "<meta property=\"og:site_name\" content=\"%.*s\">\n", MD_StringExpand(site_title));
fprintf(file, "<meta name=\"twitter:card\" content=\"summary\">\n");
fprintf(file, "<meta name=\"twitter:site\" content=\"%.*s\">\n", MD_StringExpand(twitter_handle));
fprintf(file, "<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\">\n");
fprintf(file, "<script src=\"site.js\"></script>\n");
if(title.size > 0)
{
if(site_title.size > 0)
{
fprintf(file, "<title>%.*s | %.*s</title>\n", MD_StringExpand(title), MD_StringExpand(site_title));
}
else
{
fprintf(file, "<title>%.*s</title>\n", MD_StringExpand(title));
}
}
else if(site_title.size > 0)
{
fprintf(file, "<title>%.*s</title>\n", MD_StringExpand(site_title));
}
fprintf(file, "</head>\n");
}
// NOTE(rjf): Generate body.
{
fprintf(file, "<body>\n");
// NOTE(rjf): Generate header.
if(site_info.header)
{
fprintf(file, "%.*s", MD_StringExpand(site_info.header->string));
}
fprintf(file, "<div class=\"page_content\">\n");
// NOTE(rjf): Parent page back button.
if(page_info.parent)
{
fprintf(file, "<div class=\"standalone_link_container\"><a class=\"link\" href=\"%.*s.html\">← Back</a></div>", MD_StringExpand(page_info.parent->string));
}
// NOTE(rjf): Banner.
if(page_info.header_image)
{
fprintf(file, "<div class=\"page_banner\" style=\"background-image: url('%.*s');\"></div>",
MD_StringExpand(page_info.header_image->string));
}
// NOTE(rjf): Title.
if(title.size > 0)
{
fprintf(file, "<h1 class=\"title\">%.*s</h1>", MD_StringExpand(title));
}
// NOTE(rjf): Main description/subtitle.
if(page_info.desc)
{
fprintf(file, "<h2 class=\"subtitle\">%.*s</h2>", MD_StringExpand(page_info.desc->string));
}
// NOTE(rjf): Date.
if(page_info.date)
{
MD_String8 date_string = MakeDateString(page_info.date);
if(date_string.size > 0)
{
fprintf(file, "<h3 class=\"date\">%.*s</h3>", MD_StringExpand(date_string));
}
}
// NOTE(rjf): The rest of the page content should be generated from the page nodes.
for(MD_EachNode(node, root->first_child))
{
GeneratePageContent(&index_table, &site_info, &page_info, file, node);
}
fprintf(file, "</div>\n");
// NOTE(rjf): Generate footer.
if(site_info.footer)
{
fprintf(file, "%.*s", MD_StringExpand(site_info.footer->string));
}
fprintf(file, "</body>\n");
}
fprintf(file, "</html>\n");
fclose(file);
}
}
end:;
return 0;
}
static PageInfo
ParsePageInfo(MD_Node *page)
{
PageInfo info = {0};
for(MD_EachNode(node, page->first_child))
{
if(!MD_NodeIsNil(node->first_child))
{
if(MD_StringMatch(node->string, MD_S8Lit("title"), MD_StringMatchFlag_CaseInsensitive))
{
info.title = node->first_child;
}
else if(MD_StringMatch(node->string, MD_S8Lit("desc"), MD_StringMatchFlag_CaseInsensitive))
{
info.desc = node->first_child;
}
else if(MD_StringMatch(node->string, MD_S8Lit("date"), MD_StringMatchFlag_CaseInsensitive))
{
info.date = node;
}
else if(MD_StringMatch(node->string, MD_S8Lit("parent"), MD_StringMatchFlag_CaseInsensitive))
{
info.parent = node->first_child;
}
else if(MD_StringMatch(node->string, MD_S8Lit("header_image"), MD_StringMatchFlag_CaseInsensitive))
{
info.header_image = node->first_child;
}
}
}
return info;
}
static SiteInfo
ParseSiteInfo(MD_Node *site)
{
SiteInfo info = {0};
for(MD_EachNode(node, site->first_child))
{
if(!MD_NodeIsNil(node->first_child))
{
if(MD_StringMatch(node->string, MD_S8Lit("title"), MD_StringMatchFlag_CaseInsensitive))
{
info.title = node->first_child;
}
else if(MD_StringMatch(node->string, MD_S8Lit("desc"), MD_StringMatchFlag_CaseInsensitive))
{
info.desc = node->first_child;
}
else if(MD_StringMatch(node->string, MD_S8Lit("canonical_url"), MD_StringMatchFlag_CaseInsensitive))
{
info.canonical_url = node->first_child;
}
else if(MD_StringMatch(node->string, MD_S8Lit("author"), MD_StringMatchFlag_CaseInsensitive))
{
info.author = node->first_child;
}
else if(MD_StringMatch(node->string, MD_S8Lit("twitter_handle"), MD_StringMatchFlag_CaseInsensitive))
{
info.twitter_handle = node->first_child;
}
else if(MD_StringMatch(node->string, MD_S8Lit("link_dictionary"), MD_StringMatchFlag_CaseInsensitive))
{
info.link_dictionary = node;
}
else if(MD_StringMatch(node->string, MD_S8Lit("header"), MD_StringMatchFlag_CaseInsensitive))
{
info.header = node->first_child;
}
else if(MD_StringMatch(node->string, MD_S8Lit("footer"), MD_StringMatchFlag_CaseInsensitive))
{
info.footer = node->first_child;
}
else if(MD_StringMatch(node->string, MD_S8Lit("style"), MD_StringMatchFlag_CaseInsensitive))
{
info.style = node->first_child;
}
}
}
return info;
}
static MD_String8
MakeDateString(MD_Node *date)
{
MD_String8 result = {0};
if(date)
{
MD_Node *year = 0;
MD_Node *month = 0;
MD_Node *day = 0;
for(MD_EachNode(child, date->first_child))
{
if(child->flags & MD_NodeFlag_Numeric)
{
if (year == 0) year = child;
else if (month == 0) month = child;
else if (day == 0) day = child;
else
{
break;
}
}
}
if(year && month && day)
{
char *month_names[] =
{
"January", "February", "March", "April", "May", "June", "July", "August",
"September", "October", "November", "December",
};
int month_idx = MD_I64FromString(month->string)-1;
if(month_idx >= 0 && month_idx < sizeof(month_names)/sizeof(month_names[0]))
{
result = MD_PushStringF("%.*s %s %.*s",
MD_StringExpand(day->string),
month_names[month_idx],
MD_StringExpand(year->string));
}
}
}
return result;
}
static void
GeneratePageContent(MD_NodeTable *index_table, SiteInfo *site_info, PageInfo *page_info, FILE *file, MD_Node *node)
{
//~ NOTE(rjf): Text blobs
if(MD_NodeIsNil(node->first_child) &&
(node->flags & MD_NodeFlag_StringLiteral ||
node->flags & MD_NodeFlag_CharLiteral))
{
char *html_tag = "p";
char *style = "paragraph";
if(MD_NodeHasTag(node, MD_S8Lit("title")))
{
html_tag = "h1";
style = "title";
}
else if(MD_NodeHasTag(node, MD_S8Lit("subtitle")))
{
html_tag = "h2";
style = "subtitle";
}
else if(MD_NodeHasTag(node, MD_S8Lit("code")))
{
html_tag = "pre";
style = "code";
}
MD_String8 splits[] =
{
MD_S8Lit("\n\n"),
};
MD_String8List strlist = MD_SplitString(node->string, sizeof(splits)/sizeof(splits[0]), splits);
for(MD_String8Node *strnode = strlist.first; strnode; strnode = strnode->next)
{
fprintf(file, "<%s class=\"%s\">", html_tag, style);
for(MD_u64 i = 0; i < strnode->string.size; i += 1)
{
if(strnode->string.str[i] == '@')
{
MD_ParseResult parse = MD_ParseOneNode(node->filename, MD_StringSubstring(strnode->string, i, strnode->string.size));
if(!MD_NodeIsNil(parse.node))
{
if(MD_NodeHasTag(node, MD_S8Lit("i")))
{
fprintf(file, "<i>%.*s</i>", MD_StringExpand(parse.node->string));
}
else if(MD_NodeHasTag(node, MD_S8Lit("b")))
{
fprintf(file, "<strong>%.*s</strong>", MD_StringExpand(parse.node->string));
}
else if(MD_NodeHasTag(node, MD_S8Lit("code")))
{
fprintf(file, "<span class=\"inline_code\">%.*s</span>", MD_StringExpand(parse.node->string));
}
else if(MD_NodeHasTag(node, MD_S8Lit("link")))
{
MD_Node *text = MD_ChildFromIndex(parse.node, 0);
MD_Node *link = MD_ChildFromIndex(parse.node, 1);
fprintf(file, "<a class=\"link\" href=\"%.*s\">%.*s</a>",
MD_StringExpand(link->string),
MD_StringExpand(text->string));
}
}
i += parse.bytes_parsed - 1;
}
else
{
MD_b32 dict_word = 0;
if(site_info->link_dictionary)
{
MD_Node *text = MD_NilNode();
MD_Node *link = MD_NilNode();
for(MD_EachNode(dict_link, site_info->link_dictionary->first_child))
{
text = MD_ChildFromIndex(dict_link, 0);
link = MD_ChildFromIndex(dict_link, 1);
MD_String8 substring = MD_StringSubstring(strnode->string, i, i+text->string.size);
if(MD_StringMatch(substring, text->string, 0))
{
fprintf(file, "<a class=\"link\" href=\"%.*s\">%.*s</a>",
MD_StringExpand(link->string),
MD_StringExpand(text->string));
dict_word = 1;
i += text->string.size-1;
break;
}
}
}
if(!dict_word)
{
fprintf(file, "%c", strnode->string.str[i]);
}
}
}
fprintf(file, "</%s>\n", html_tag);
}
}
if(!MD_NodeIsNil(node->first_child))
{
if(MD_NodeHasTag(node, MD_S8Lit("list")))
{
fprintf(file, "<ul class=\"list\">\n");
for(MD_EachNode(child, node->first_child))
{
if(MD_NodeIsNil(child->first_child))
{
fprintf(file, "<li class=\"list_item\">\n");
}
GeneratePageContent(index_table, site_info, page_info, file, child);
if(MD_NodeIsNil(child->first_child))
{
fprintf(file, "</li>\n");
}
}
fprintf(file, "</ul>\n");
}
else if(MD_NodeHasTag(node, MD_S8Lit("img")))
{
MD_Node *src = MD_ChildFromIndex(node, 0);
MD_Node *alt = MD_ChildFromIndex(node, 1);
fprintf(file, "<div class=\"img_container\"><img class=\"img\" src=\"%.*s\"></img></div>\n", MD_StringExpand(src->string));
}
else if(MD_NodeHasTag(node, MD_S8Lit("youtube")))
{
MD_Node *id = MD_ChildFromIndex(node, 0);
fprintf(file, "<iframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/%.*s\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n",
MD_StringExpand(id->string));
}
else if(MD_NodeHasTag(node, MD_S8Lit("lister")))
{
static int lister_idx = 0;
fprintf(file, "<input autofocus id=\"lister_search_%i\" class=\"lister_search\" oninput=\"SearchInput(event, %i)\" onkeydown=\"SearchKeyDown(event, %i)\" placeholder=\"Filter...\"></input>", lister_idx, lister_idx, lister_idx);
fprintf(file, "<ul id=\"lister_%i\" class=\"lister\">\n", lister_idx);
lister_idx += 1;
MD_Node *index_string = 0;
for(MD_u64 idx = 0; !MD_NodeIsNil(index_string = MD_ChildFromIndex(node, idx)); idx += 1)
{
for(MD_NodeTableSlot *slot = MD_NodeTable_Lookup(index_table, index_string->string);
slot; slot = slot->next)
{
if(slot->node)
{
PageInfo info = ParsePageInfo(slot->node);
MD_String8 filename = slot->node->filename;
MD_String8 filename_no_ext = MD_TrimExtension(MD_TrimFolder(filename));
MD_String8 link = MD_PushStringF("%.*s.html", MD_StringExpand(filename_no_ext));
MD_String8 name = info.title->string;
MD_String8 date = MakeDateString(info.date);
fprintf(file, "<a class=\"lister_item_link\" href=\"%.*s\">\n", MD_StringExpand(link));
fprintf(file, "<li class=\"lister_item\">\n");
if(info.header_image)
{
fprintf(file, "<div class=\"lister_item_img\" style=\"background-image:url('%.*s');\">",
MD_StringExpand(info.header_image->string));
}
fprintf(file, "<div class=\"lister_item_text\">\n");
fprintf(file, "<div class=\"lister_item_title\">\n");
fprintf(file, "%.*s\n", MD_StringExpand(name));
fprintf(file, "</div>\n");
if(date.size > 0)
{
fprintf(file, "<div class=\"lister_item_date\">\n");
fprintf(file, "%.*s\n", MD_StringExpand(date));
fprintf(file, "</div>\n");
}
if(info.desc)
{
fprintf(file, "<div class=\"lister_item_desc\">\n");
fprintf(file, "%.*s\n", MD_StringExpand(info.desc->string));
fprintf(file, "</div>\n");
}
if(info.header_image)
{
fprintf(file, "</div>\n");
}
fprintf(file, "</div>\n");
fprintf(file, "</a>\n");
fprintf(file, "</li>\n");
}
}
}
fprintf(file, "</ul>\n");
}
}
}
+16
View File
@@ -0,0 +1,16 @@
//~ Metadesk Library
// TODO(allen): Test including this after <Windows.h>
// NOTE(allen): Include the OS specific functions
#if MD_OS_WINDOWS
# include "md_win32.c"
#elif MD_OS_LINUX
# include "md_posix.c"
#else
# error No default implementation for this OS
#endif
#include "md_malloc.c"
#include "md_impl.c"
+780
View File
@@ -0,0 +1,780 @@
//~ Metadesk Library
// TODO List
//
// - Expression/Type helper
// - Parsing things as a type, getting basic type info
// - Radix for MD_I64FromString
// - Freeing calls, allow hooking the allocator with an arena or
// something so that someone can use this for work at an application/game's
// runtime
// - Outputting anything to MD or C code ideally would do something
// smart with auto-indentation, since some people have wanted readable
// layout (if they aren't using 4coder with virtual whitespace basically)
// - Building with Clang
// - Split out C-related stuff into helper language layers
// - Helpers for parsing NodeFlags, figuring out which nodes in a set are
// separated by a semicolon, something like MD_SeekNodeWithFlags(node) -> node ?
// NOTE(allen): "Plugin" functionality
//
// MD_b32 MD_IMPL_FileIterIncrement(MD_FileIter*, MD_String8, MD_FileInfo*) - optional
// void* MD_IMPL_Alloc(void*,MD_u64) - required
// void* MD_IMPL_GetCtx(void) - optional
//
// TODO(allen): Commentary about this system somewhere easy to discover when
// you go digging.
#ifndef MD_H
#define MD_H
// NOTE(rjf): Compiler cracking from the 4th dimension
#if defined(_MSC_VER)
# define MD_COMPILER_CL 1
# if defined(_WIN32)
# define MD_OS_WINDOWS 1
# else
# error This compiler/platform combo is not supported yet
# endif
# if defined(_M_AMD64)
# define MD_ARCH_X64 1
# elif defined(_M_IX86)
# define MD_ARCH_X86 1
# elif defined(_M_ARM64)
# define MD_ARCH_ARM64 1
# elif defined(_M_ARM)
# define MD_ARCH_ARM32 1
# else
# error architecture not supported yet
# endif
#if _MSC_VER >= 1920
#define MD_COMPILER_CL_YEAR 2019
#elif _MSC_VER >= 1910
#define MD_COMPILER_CL_YEAR 2017
#elif _MSC_VER >= 1900
#define MD_COMPILER_CL_YEAR 2015
#elif _MSC_VER >= 1800
#define MD_COMPILER_CL_YEAR 2013
#elif _MSC_VER >= 1700
#define MD_COMPILER_CL_YEAR 2012
#elif _MSC_VER >= 1600
#define MD_COMPILER_CL_YEAR 2010
#elif _MSC_VER >= 1500
#define MD_COMPILER_CL_YEAR 2008
#elif _MSC_VER >= 1400
#define MD_COMPILER_CL_YEAR 2005
#else
#define MD_COMPILER_CL_YEAR 0
#endif
#elif defined(__clang__)
# define MD_COMPILER_CLANG 1
# if defined(__APPLE__) && defined(__MACH__)
# define MD_OS_MAC 1
# elif defined(__gnu_linux__)
# define MD_OS_LINUX 1
# else
# error This compiler/platform combo is not supported yet
# endif
# if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64)
# define MD_ARCH_X64 1
# elif defined(i386) || defined(__i386) || defined(__i386__)
# define MD_ARCH_X86 1
# elif defined(__aarch64__)
# define MD_ARCH_ARM64 1
# elif defined(__arm__)
# define MD_ARCH_ARM32 1
# else
# error architecture not supported yet
# endif
#elif defined(__GNUC__) || defined(__GNUG__)
# define MD_COMPILER_GCC 1
# if defined(__gnu_linux__)
# define MD_OS_LINUX 1
# else
# error This compiler/platform combo is not supported yet
# endif
# if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64)
# define MD_ARCH_X64 1
# elif defined(i386) || defined(__i386) || defined(__i386__)
# define MD_ARCH_X86 1
# elif defined(__aarch64__)
# define MD_ARCH_ARM64 1
# elif defined(__arm__)
# define MD_ARCH_ARM32 1
# else
# error architecture not supported yet
# endif
#else
# error This compiler is not supported yet
#endif
#if defined(MD_ARCH_X64)
# define MD_ARCH_64BIT 1
#elif defined(MD_ARCH_X86)
# define MD_ARCH_32BIT 1
#endif
// NOTE(allen): Review @rjf; Building in C++
// Added language cracking. Handy for a few pesky problems that can't be solved
// strictly within the intersection of C & C++
#if defined(__cplusplus)
# define MD_LANG_CPP 1
#else
# define MD_LANG_C 1
#endif
// zeroify
#if !defined(MD_ARCH_32BIT)
#define MD_ARCH_32BIT 0
#endif
#if !defined(MD_ARCH_64BIT)
#define MD_ARCH_64BIT 0
#endif
#if !defined(MD_ARCH_X64)
#define MD_ARCH_X64 0
#endif
#if !defined(MD_ARCH_X86)
#define MD_ARCH_X86 0
#endif
#if !defined(MD_ARCH_ARM64)
#define MD_ARCH_ARM64 0
#endif
#if !defined(MD_ARCH_ARM32)
#define MD_ARCH_ARM32 0
#endif
#if !defined(MD_COMPILER_CL)
#define MD_COMPILER_CL 0
#endif
#if !defined(MD_COMPILER_GCC)
#define MD_COMPILER_GCC 0
#endif
#if !defined(MD_COMPILER_CLANG)
#define MD_COMPILER_CLANG 0
#endif
#if !defined(MD_OS_WINDOWS)
#define MD_OS_WINDOWS 0
#endif
#if !defined(MD_OS_LINUX)
#define MD_OS_LINUX 0
#endif
#if !defined(MD_OS_MAC)
#define MD_OS_MAC 0
#endif
#if !defined(MD_LANG_C)
#define MD_LANG_C 0
#endif
#if !defined(MD_LANG_CPP)
#define MD_LANG_CPP 0
#endif
#define MD_FUNCTION
#define MD_GLOBAL static
// NOTE(allen): Review @rjf; Building in C++
// In order to link to C functions from C++ code, we need to mark them as using
// C linkage. In particular I mean FindFirstFileA, FindNextFileA right now.
// We don't necessarily need to apply this to the DD functions if the user is
// building from source, so I haven't done that.
#if MD_LANG_C
# define MD_C_LINKAGE_BEGIN
# define MD_C_LINKAGE_END
#else
# define MD_C_LINKAGE_BEGIN extern "C"{
# define MD_C_LINKAGE_END }
#endif
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
// NOTE(allen): Review @rjf; Building in C++
// In C++ compiler I have to include this to get memset and memcpy to compile
#include <string.h>
typedef int8_t MD_i8;
typedef int16_t MD_i16;
typedef int32_t MD_i32;
typedef int64_t MD_i64;
typedef uint8_t MD_u8;
typedef uint16_t MD_u16;
typedef uint32_t MD_u32;
typedef uint64_t MD_u64;
typedef int8_t MD_b8;
typedef int16_t MD_b16;
typedef int32_t MD_b32;
typedef int64_t MD_b64;
typedef float MD_f32;
typedef double MD_f64;
//~ Basic UTF-8 string types.
typedef struct MD_String8 MD_String8;
struct MD_String8
{
MD_u8 *str;
MD_u64 size;
};
typedef struct MD_String16 MD_String16;
struct MD_String16
{
MD_u16 *str;
MD_u64 size;
};
typedef struct MD_String32 MD_String32;
struct MD_String32
{
MD_u32 *str;
MD_u64 size;
};
typedef struct MD_String8Node MD_String8Node;
struct MD_String8Node
{
MD_String8Node *next;
MD_String8 string;
};
typedef struct MD_String8List MD_String8List;
struct MD_String8List
{
MD_u64 node_count;
MD_u64 total_size;
MD_String8Node *first;
MD_String8Node *last;
};
typedef MD_u32 MD_StringMatchFlags;
enum
{
MD_StringMatchFlag_CaseInsensitive = (1<<0),
MD_StringMatchFlag_RightSideSloppy = (1<<1),
MD_StringMatchFlag_FindLast = (1<<2),
MD_StringMatchFlag_SlashInsensitive = (1<<3),
};
typedef struct MD_UnicodeConsume MD_UnicodeConsume;
struct MD_UnicodeConsume
{
MD_u32 codepoint;
MD_u32 advance;
};
typedef enum MD_WordStyle
{
MD_WordStyle_UpperCamelCase,
MD_WordStyle_LowerCamelCase,
MD_WordStyle_UpperCase,
MD_WordStyle_LowerCase,
}
MD_WordStyle;
//~ Node types that are used to build all ASTs.
typedef enum MD_NodeKind
{
MD_NodeKind_Nil,
MD_NodeKind_File,
MD_NodeKind_Label,
MD_NodeKind_UnnamedSet,
MD_NodeKind_Tag,
MD_NodeKind_MAX,
}
MD_NodeKind;
typedef MD_u32 MD_NodeFlags;
enum
{
MD_NodeFlag_ParenLeft = (1<<0),
MD_NodeFlag_ParenRight = (1<<1),
MD_NodeFlag_BracketLeft = (1<<2),
MD_NodeFlag_BracketRight = (1<<3),
MD_NodeFlag_BraceLeft = (1<<4),
MD_NodeFlag_BraceRight = (1<<5),
MD_NodeFlag_BeforeSemicolon = (1<<6),
MD_NodeFlag_BeforeComma = (1<<7),
MD_NodeFlag_AfterSemicolon = (1<<8),
MD_NodeFlag_AfterComma = (1<<9),
MD_NodeFlag_Numeric = (1<<10),
MD_NodeFlag_Identifier = (1<<11),
MD_NodeFlag_StringLiteral = (1<<12),
MD_NodeFlag_CharLiteral = (1<<13),
};
typedef MD_u32 MD_NodeMatchFlags;
enum
{
MD_NodeMatchFlag_Tags = (1<<0),
MD_NodeMatchFlag_TagArguments = (1<<1),
};
typedef struct MD_Node MD_Node;
struct MD_Node
{
// Tree relationship data.
MD_Node *next;
MD_Node *prev;
MD_Node *parent;
MD_Node *first_child;
MD_Node *last_child;
// Tag list.
MD_Node *first_tag;
MD_Node *last_tag;
// Node info.
MD_NodeKind kind;
MD_NodeFlags flags;
MD_String8 string;
MD_String8 whole_string;
MD_u64 string_hash;
// Source code location information.
MD_String8 filename;
MD_u8 *file_contents;
MD_u8 *at;
};
//~ Code Location Info.
typedef struct MD_CodeLoc MD_CodeLoc;
struct MD_CodeLoc
{
MD_String8 filename;
int line;
int column;
};
//~ Warning Levels
typedef enum MD_MessageKind
{
MD_MessageKind_Error,
MD_MessageKind_Warning,
}
MD_MessageKind;
//~ String-To-Node table
typedef enum MD_NodeTableCollisionRule
{
MD_NodeTableCollisionRule_Chain,
MD_NodeTableCollisionRule_Overwrite,
}
MD_NodeTableCollisionRule;
typedef struct MD_NodeTableSlot MD_NodeTableSlot;
struct MD_NodeTableSlot
{
MD_NodeTableSlot *next;
MD_u64 hash;
MD_Node *node;
};
typedef struct MD_NodeTable MD_NodeTable;
struct MD_NodeTable
{
MD_u64 table_size;
MD_NodeTableSlot **table;
};
//~ Token kinds.
typedef enum MD_TokenKind
{
MD_TokenKind_Nil,
MD_TokenKind_RegularMin,
// A group of characters that begins with an underscore or alphabetic character,
// and consists of numbers, alphabetic characters, or underscores after that.
MD_TokenKind_Identifier,
// A group of characters beginning with a numeric character or a '-', and then
// consisting of only numbers, alphabetic characters, or '.'s after that.
MD_TokenKind_NumericLiteral,
// A group of arbitrary characters, grouped together by a " character, OR by a
// """ symbol at the beginning and end of the group. String literals beginning with
// " are to only be specified on a single line, but """ strings can exist across
// many lines.
MD_TokenKind_StringLiteral,
// A group of arbitrary characters, grouped together by a ' character at the beginning,
// and a ' character at the end.
MD_TokenKind_CharLiteral,
// A group of symbolic characters, where symbolic characters means any of the following:
// ~!@#$%^&*()-+=[{]}:;<>,./?|\
//
// Groups of multiple characters are only allowed in specific circumstances. Most of these
// are only 1 character long, but some groups are allowed:
//
// "<<", ">>", "<=", ">=", "+=", "-=", "*=", "/=", "::", ":=", "==", "&=", "|=", "->"
MD_TokenKind_Symbol,
MD_TokenKind_RegularMax,
MD_TokenKind_Comment,
MD_TokenKind_WhitespaceMin,
MD_TokenKind_Whitespace,
MD_TokenKind_Newline,
MD_TokenKind_WhitespaceMax,
MD_TokenKind_MAX,
}
MD_TokenKind;
//~ Token type.
typedef struct MD_Token MD_Token;
struct MD_Token
{
MD_TokenKind kind;
MD_String8 string;
MD_String8 outer_string;
};
//~ Token groups.
typedef MD_u32 MD_TokenGroups;
enum{
MD_TokenGroup_Comment = (1 << 0),
MD_TokenGroup_Whitespace = (1 << 1),
MD_TokenGroup_Regular = (1 << 2)
};
//~ Parsing State
typedef struct MD_Error MD_Error;
struct MD_Error
{
MD_Error *next;
MD_String8 string;
MD_String8 filename;
MD_Node *node;
};
typedef struct MD_ParseCtx MD_ParseCtx;
struct MD_ParseCtx
{
MD_Node *first_root;
MD_Node *last_root;
MD_Error *first_error;
MD_Error *last_error;
MD_u8 *at;
MD_String8 filename;
MD_String8 file_contents;
};
typedef struct MD_ParseResult MD_ParseResult;
struct MD_ParseResult
{
MD_Node *node;
MD_Error *first_error;
MD_u64 bytes_parsed;
};
//~ Expression and Type-Expression parser helper types.
typedef enum MD_ExprKind
{
// VERY_IMPORTANT_NOTE(rjf): If this enum is ever changed, ensure that
// it is kept in-sync with the MD_ExprPrecFromExprKind and the following
// functions:
//
// MD_BinaryExprKindFromNode
// MD_PreUnaryExprKindFromNode
// MD_PostUnaryExprKindFromNode
MD_ExprKind_Nil,
// NOTE(rjf): Atom
MD_ExprKind_Atom,
// NOTE(rjf): Access
MD_ExprKind_Dot,
MD_ExprKind_Arrow,
MD_ExprKind_Call,
MD_ExprKind_Subscript,
MD_ExprKind_Dereference,
MD_ExprKind_Reference,
// NOTE(rjf): Arithmetic
MD_ExprKind_Add,
MD_ExprKind_Subtract,
MD_ExprKind_Multiply,
MD_ExprKind_Divide,
MD_ExprKind_Mod,
// NOTE(rjf): Comparison
MD_ExprKind_IsEqual,
MD_ExprKind_IsNotEqual,
MD_ExprKind_LessThan,
MD_ExprKind_GreaterThan,
MD_ExprKind_LessThanEqualTo,
MD_ExprKind_GreaterThanEqualTo,
// NOTE(rjf): Bools
MD_ExprKind_BoolAnd,
MD_ExprKind_BoolOr,
MD_ExprKind_BoolNot,
// NOTE(rjf): Bitwise
MD_ExprKind_BitAnd,
MD_ExprKind_BitOr,
MD_ExprKind_BitNot,
MD_ExprKind_BitXor,
MD_ExprKind_LeftShift,
MD_ExprKind_RightShift,
// NOTE(rjf): Unary numeric
MD_ExprKind_Negative,
// NOTE(rjf): Type
MD_ExprKind_Pointer,
MD_ExprKind_Array,
MD_ExprKind_MAX,
}
MD_ExprKind;
typedef MD_i32 MD_ExprPrec;
typedef struct MD_Expr MD_Expr;
struct MD_Expr
{
MD_Node *node;
MD_ExprKind kind;
MD_Expr *parent;
MD_Expr *sub[2];
};
//~ Command line parsing helper types.
typedef struct MD_CommandLine MD_CommandLine;
struct MD_CommandLine
{
// TODO(rjf): Linked-list vs. array?
MD_String8 *arguments;
int argument_count;
};
//~ File system access types.
typedef MD_u32 MD_FileFlags;
enum
{
MD_FileFlag_Directory = (1<<0),
};
typedef struct MD_FileInfo MD_FileInfo;
struct MD_FileInfo
{
MD_FileFlags flags;
MD_String8 filename;
MD_u64 file_size;
};
typedef struct MD_FileIter MD_FileIter;
struct MD_FileIter
{
// This is opaque state to store OS-specific file-system iteration data.
MD_u64 state;
};
//~ Basic Utilities
#define MD_Assert(c) if (!(c)) { *(volatile MD_u64 *)0 = 0; }
#define MD_ArrayCount(a) (sizeof(a) / sizeof((a)[0]))
MD_FUNCTION MD_b32 MD_CharIsAlpha(MD_u8 c);
MD_FUNCTION MD_b32 MD_CharIsAlphaUpper(MD_u8 c);
MD_FUNCTION MD_b32 MD_CharIsAlphaLower(MD_u8 c);
MD_FUNCTION MD_b32 MD_CharIsDigit(MD_u8 c);
MD_FUNCTION MD_b32 MD_CharIsSymbol(MD_u8 c);
MD_FUNCTION MD_b32 MD_CharIsSpace(MD_u8 c);
MD_FUNCTION MD_u8 MD_CharToUpper(MD_u8 c);
MD_FUNCTION MD_u8 MD_CharToLower(MD_u8 c);
MD_FUNCTION MD_u8 MD_CorrectSlash(MD_u8 c);
//~ Strings
MD_FUNCTION MD_String8 MD_S8(MD_u8 *str, MD_u64 size);
#define MD_S8CString(s) MD_S8((MD_u8 *)(s), MD_CalculateCStringLength(s))
#if MD_LANG_C
#define MD_S8Lit(s) (MD_String8){(MD_u8 *)(s), sizeof(s)-1}
#elif MD_LANG_CPP
#define MD_S8Lit(s) MD_S8((MD_u8*)(s), sizeof(s) - 1)
#endif
MD_FUNCTION MD_String8 MD_S8Range(MD_u8 *str, MD_u8 *opl);
MD_FUNCTION MD_String8 MD_StringSubstring(MD_String8 str, MD_u64 min, MD_u64 max);
MD_FUNCTION MD_String8 MD_StringSkip(MD_String8 str, MD_u64 min);
MD_FUNCTION MD_String8 MD_StringChop(MD_String8 str, MD_u64 nmax);
MD_FUNCTION MD_String8 MD_StringPrefix(MD_String8 str, MD_u64 size);
MD_FUNCTION MD_String8 MD_StringSuffix(MD_String8 str, MD_u64 size);
MD_FUNCTION MD_b32 MD_StringMatch(MD_String8 a, MD_String8 b, MD_StringMatchFlags flags);
MD_FUNCTION MD_u64 MD_FindSubstring(MD_String8 str, MD_String8 substring,
MD_u64 start_pos, MD_StringMatchFlags flags);
MD_FUNCTION MD_u64 MD_FindLastSubstring(MD_String8 str, MD_String8 substring, MD_StringMatchFlags flags);
MD_FUNCTION MD_String8 MD_TrimExtension(MD_String8 string);
MD_FUNCTION MD_String8 MD_TrimFolder(MD_String8 string);
MD_FUNCTION MD_String8 MD_ExtensionFromPath(MD_String8 string);
MD_FUNCTION MD_String8 MD_FolderFromPath(MD_String8 string);
MD_FUNCTION MD_String8 MD_PushStringCopy(MD_String8 string);
MD_FUNCTION MD_String8 MD_PushStringFV(char *fmt, va_list args);
MD_FUNCTION MD_String8 MD_PushStringF(char *fmt, ...);
#define MD_StringExpand(s) (int)(s).size, (s).str
MD_FUNCTION void MD_PushStringToList(MD_String8List *list, MD_String8 string);
MD_FUNCTION void MD_PushStringListToList(MD_String8List *list, MD_String8List *to_push);
// TODO(rjf): Just simplify to a single splitter
MD_FUNCTION MD_String8List MD_SplitString(MD_String8 string, int split_count, MD_String8 *splits);
MD_FUNCTION MD_String8List MD_SplitStringByString(MD_String8 string, MD_String8 split);
MD_FUNCTION MD_String8List MD_SplitStringByCharacter(MD_String8 string, MD_u8 character);
MD_FUNCTION MD_String8 MD_JoinStringList(MD_String8List list);
MD_FUNCTION MD_String8 MD_JoinStringListWithSeparator(MD_String8List list, MD_String8 separator);
// TODO(rjf): Radix
MD_FUNCTION MD_i64 MD_I64FromString(MD_String8 string);
MD_FUNCTION MD_f64 MD_F64FromString(MD_String8 string);
MD_FUNCTION MD_u64 MD_HashString(MD_String8 string);
MD_FUNCTION MD_u64 MD_CalculateCStringLength(char *cstr);
MD_FUNCTION MD_String8 MD_StyledStringFromString(MD_String8 string, MD_WordStyle word_style, MD_String8 separator);
//~ Enum/Flag Strings
MD_FUNCTION MD_String8 MD_StringFromNodeKind(MD_NodeKind kind);
MD_FUNCTION MD_String8List MD_StringListFromNodeFlags(MD_NodeFlags flags);
//~ Unicode Conversions
MD_FUNCTION MD_UnicodeConsume MD_CodepointFromUtf8(MD_u8 *str, MD_u64 max);
MD_FUNCTION MD_UnicodeConsume MD_CodepointFromUtf16(MD_u16 *str, MD_u64 max);
MD_FUNCTION MD_u32 MD_Utf8FromCodepoint(MD_u8 *out, MD_u32 codepoint);
MD_FUNCTION MD_u32 MD_Utf16FromCodepoint(MD_u16 *out, MD_u32 codepoint);
MD_FUNCTION MD_String8 MD_S8FromS16(MD_String16 str);
MD_FUNCTION MD_String16 MD_S16FromS8(MD_String8 str);
MD_FUNCTION MD_String8 MD_S8FromS32(MD_String32 str);
MD_FUNCTION MD_String32 MD_S32FromS8(MD_String8 str);
//~ String-To-Node-List Table
MD_FUNCTION MD_NodeTableSlot *MD_NodeTable_Lookup(MD_NodeTable *table, MD_String8 string);
MD_FUNCTION MD_b32 MD_NodeTable_Insert(MD_NodeTable *table, MD_NodeTableCollisionRule collision_rule, MD_String8 string, MD_Node *node);
//~ Parsing
MD_FUNCTION MD_Token MD_ZeroToken(void);
MD_FUNCTION MD_b32 MD_TokenKindIsWhitespace(MD_TokenKind kind);
MD_FUNCTION MD_b32 MD_TokenKindIsComment(MD_TokenKind kind);
MD_FUNCTION MD_b32 MD_TokenKindIsRegular(MD_TokenKind kind);
MD_FUNCTION MD_ParseCtx MD_Parse_InitializeCtx(MD_String8 filename, MD_String8 contents);
MD_FUNCTION void MD_Parse_Bump(MD_ParseCtx *ctx, MD_Token token);
MD_FUNCTION void MD_Parse_BumpNext(MD_ParseCtx *ctx);
MD_FUNCTION MD_Token MD_Parse_LexNext(MD_ParseCtx *ctx);
MD_FUNCTION MD_Token MD_Parse_PeekSkipSome(MD_ParseCtx *ctx, MD_TokenGroups skip_groups);
MD_FUNCTION MD_b32 MD_Parse_TokenMatch(MD_Token token, MD_String8 string, MD_StringMatchFlags flags);
MD_FUNCTION MD_b32 MD_Parse_Require(MD_ParseCtx *ctx, MD_String8 string);
MD_FUNCTION MD_b32 MD_Parse_RequireKind(MD_ParseCtx *ctx, MD_TokenKind kind, MD_Token *out_token);
MD_FUNCTION MD_ParseResult MD_ParseOneNode (MD_String8 filename, MD_String8 contents);
MD_FUNCTION MD_Node * MD_ParseWholeString (MD_String8 filename, MD_String8 contents);
MD_FUNCTION MD_Node * MD_ParseWholeFile (MD_String8 filename);
//~ Tree/List Building
MD_FUNCTION MD_b32 MD_NodeIsNil(MD_Node *node);
MD_FUNCTION MD_Node *MD_NilNode(void);
MD_FUNCTION MD_Node *MD_MakeNodeFromToken(MD_NodeKind kind, MD_String8 filename, MD_u8 *file, MD_u8 *at, MD_Token token);
MD_FUNCTION MD_Node *MD_MakeNodeFromString(MD_NodeKind kind, MD_String8 filename, MD_u8 *file, MD_u8 *at, MD_String8 string);
MD_FUNCTION void MD_PushSibling(MD_Node **first, MD_Node **last, MD_Node *parent, MD_Node *new_sibling);
MD_FUNCTION void MD_PushChild(MD_Node *parent, MD_Node *new_child);
MD_FUNCTION void MD_PushTag(MD_Node *node, MD_Node *tag);
//~ Introspection Helpers
MD_FUNCTION MD_Node * MD_NodeFromString(MD_Node *first, MD_Node *last, MD_String8 string);
MD_FUNCTION MD_Node * MD_NodeFromIndex(MD_Node *first, MD_Node *last, int n);
MD_FUNCTION int MD_IndexFromNode(MD_Node *node);
MD_FUNCTION MD_Node * MD_NextNodeSibling(MD_Node *last, MD_String8 string);
MD_FUNCTION MD_Node * MD_ChildFromString(MD_Node *node, MD_String8 child_string);
MD_FUNCTION MD_Node * MD_TagFromString(MD_Node *node, MD_String8 tag_string);
MD_FUNCTION MD_Node * MD_ChildFromIndex(MD_Node *node, int n);
MD_FUNCTION MD_Node * MD_TagFromIndex(MD_Node *node, int n);
MD_FUNCTION MD_Node * MD_TagArgFromIndex(MD_Node *node, MD_String8 tag_string, int n);
MD_FUNCTION MD_b32 MD_NodeHasTag(MD_Node *node, MD_String8 tag_string);
MD_FUNCTION MD_CodeLoc MD_CodeLocFromNode(MD_Node *node);
MD_FUNCTION MD_i64 MD_ChildCountFromNode(MD_Node *node);
MD_FUNCTION MD_i64 MD_TagCountFromNode(MD_Node *node);
MD_FUNCTION MD_i64 MD_ChildCountFromNodeAndString(MD_Node *node, MD_String8 string, MD_StringMatchFlags flags);
MD_FUNCTION MD_i64 MD_TagCountFromNodeAndString(MD_Node *node, MD_String8 string, MD_StringMatchFlags flags);
// NOTE(rjf): For-Loop Helper
#define MD_EachNode(it, first) MD_Node *it = (first); !MD_NodeIsNil(it); it = it->next
//~ Error/Warning Helpers
MD_FUNCTION void MD_NodeMessage(MD_Node *node, MD_MessageKind kind, MD_String8 str);
MD_FUNCTION void MD_NodeError(MD_Node *node, MD_String8 str);
MD_FUNCTION void MD_NodeWarning(MD_Node *node, MD_String8 str);
MD_FUNCTION void MD_NodeMessageF(MD_Node *node, MD_MessageKind kind, char *fmt, ...);
MD_FUNCTION void MD_NodeErrorF(MD_Node *node, char *fmt, ...);
MD_FUNCTION void MD_NodeWarningF(MD_Node *node, char *fmt, ...);
//~ Tree Comparison/Verification
MD_FUNCTION MD_b32 MD_NodeMatch(MD_Node *a, MD_Node *b, MD_StringMatchFlags str_flags, MD_NodeMatchFlags node_flags);
MD_FUNCTION MD_b32 MD_NodeDeepMatch(MD_Node *a, MD_Node *b, MD_StringMatchFlags str_flags, MD_NodeMatchFlags node_flags);
//~ Expression and Type-Expression Helper
MD_FUNCTION MD_Expr * MD_NilExpr(void);
MD_FUNCTION MD_b32 MD_ExprIsNil(MD_Expr *expr);
MD_FUNCTION MD_ExprKind MD_PreUnaryExprKindFromNode(MD_Node *node);
MD_FUNCTION MD_ExprKind MD_BinaryExprKindFromNode(MD_Node *node);
MD_FUNCTION MD_ExprPrec MD_ExprPrecFromExprKind(MD_ExprKind kind);
MD_FUNCTION MD_Expr * MD_MakeExpr(MD_Node *node, MD_ExprKind kind, MD_Expr *left, MD_Expr *right);
MD_FUNCTION MD_Expr * MD_ParseAsExpr(MD_Node *first, MD_Node *last);
MD_FUNCTION MD_Expr * MD_ParseAsType(MD_Node *first, MD_Node *last);
MD_FUNCTION MD_i64 MD_EvaluateExpr_I64(MD_Expr *expr);
MD_FUNCTION MD_f64 MD_EvaluateExpr_F64(MD_Expr *expr);
MD_FUNCTION MD_b32 MD_ExprMatch(MD_Expr *a, MD_Expr *b, MD_StringMatchFlags str_flags);
MD_FUNCTION MD_b32 MD_ExprDeepMatch(MD_Expr *a, MD_Expr *b, MD_StringMatchFlags str_flags);
//~ Generation
MD_FUNCTION void MD_OutputTree(FILE *file, MD_Node *node);
MD_FUNCTION void MD_OutputExpr(FILE *file, MD_Expr *expr);
MD_FUNCTION void MD_OutputTree_C_String(FILE *file, MD_Node *node);
MD_FUNCTION void MD_OutputTree_C_Struct(FILE *file, MD_Node *node);
MD_FUNCTION void MD_OutputTree_C_Decl(FILE *file, MD_Node *node);
MD_FUNCTION void MD_Output_C_DeclByNameAndType(FILE *file, MD_String8 name, MD_Expr *type);
MD_FUNCTION void MD_OutputType_C_LHS(FILE *file, MD_Expr *type);
MD_FUNCTION void MD_OutputType_C_RHS(FILE *file, MD_Expr *type);
//~ Command Line Argument Helper
MD_FUNCTION MD_CommandLine MD_CommandLine_Start(int argument_count, char **arguments);
MD_FUNCTION MD_b32 MD_CommandLine_Flag(MD_CommandLine *cmdln, MD_String8 string);
MD_FUNCTION MD_b32 MD_CommandLine_FlagStrings(MD_CommandLine *cmdln, MD_String8 string, int out_count, MD_String8 *out);
MD_FUNCTION MD_b32 MD_CommandLine_FlagIntegers(MD_CommandLine *cmdln, MD_String8 string, int out_count, MD_i64 *out);
MD_FUNCTION MD_b32 MD_CommandLine_FlagString(MD_CommandLine *cmdln, MD_String8 string, MD_String8 *out);
MD_FUNCTION MD_b32 MD_CommandLine_FlagInteger(MD_CommandLine *cmdln, MD_String8 string, MD_i64 *out);
MD_FUNCTION MD_b32 MD_CommandLine_Increment(MD_CommandLine *cmdln, MD_String8 **string_ptr);
//~ File System
MD_FUNCTION MD_String8 MD_LoadEntireFile(MD_String8 filename);
MD_FUNCTION MD_b32 MD_FileIterIncrement(MD_FileIter *it, MD_String8 path, MD_FileInfo *out_info);
#endif // MD_H
+2773
View File
File diff suppressed because it is too large Load Diff
+19
View File
@@ -0,0 +1,19 @@
////////////////////////////////
// TODO(allen): Write commentary for all of this.
#define MD_IMPL_Alloc(ctx,size) MD_MALLOC_Alloc(ctx,size)
static void*
MD_MALLOC_Alloc(void *ctx, MD_u64 size)
{
MD_Assert(ctx == MD_MALLOC_Alloc);
return(malloc(size));
}
#define MD_IMPL_GetCtx() MD_MALLOC_GetCtx()
static void*
MD_MALLOC_GetCtx(void)
{
return(MD_MALLOC_Alloc);
}
+5
View File
@@ -0,0 +1,5 @@
static MD_b32
_MD_OS_IMPL_FileIter_Increment(MD_FileIter *it, MD_String8 path, MD_FileInfo *out_info)
{
}
+95
View File
@@ -0,0 +1,95 @@
////////////////////////////////
// TODO(allen): Write commentary for all of this.
#define MAX_PATH 260
typedef unsigned long DWORD;
typedef unsigned short WORD;
typedef int BOOL;
typedef void *HANDLE;
typedef char CHAR;
typedef const CHAR *LPCSTR;
typedef struct _FILETIME _FILETIME;
struct _FILETIME
{
DWORD dwLowDateTime;
DWORD dwHighDateTime;
};
typedef _FILETIME FILETIME;
typedef _FILETIME *PFILETIME;
typedef _FILETIME *LPFILETIME;
typedef struct _WIN32_FIND_DATAA _WIN32_FIND_DATAA;
struct _WIN32_FIND_DATAA
{
#define FILE_ATTRIBUTE_DIRECTORY 0x10
DWORD dwFileAttributes;
FILETIME ftCreationTime;
FILETIME ftLastAccessTime;
FILETIME ftLastWriteTime;
DWORD nFileSizeHigh;
DWORD nFileSizeLow;
DWORD dwReserved0;
DWORD dwReserved1;
CHAR cFileName[MAX_PATH];
CHAR cAlternateFileName[14];
DWORD dwFileType;
DWORD dwCreatorType;
WORD wFinderFlags;
};
typedef _WIN32_FIND_DATAA WIN32_FIND_DATAA;
typedef _WIN32_FIND_DATAA *PWIN32_FIND_DATAA;
typedef _WIN32_FIND_DATAA *LPWIN32_FIND_DATAA;
MD_C_LINKAGE_BEGIN
HANDLE FindFirstFileA(LPCSTR lpFileName, LPWIN32_FIND_DATAA lpFindFileData);
BOOL FindNextFileA(HANDLE hFindFile, LPWIN32_FIND_DATAA lpFindFileData);
MD_C_LINKAGE_END
#pragma comment(lib, "User32.lib")
#define MD_IMPL_FileIterIncrement MD_WIN32_FileIterIncrement
static MD_b32
MD_WIN32_FileIterIncrement(MD_FileIter *it, MD_String8 path, MD_FileInfo *out_info)
{
MD_b32 result = 0;
WIN32_FIND_DATAA find_data = {0};
HANDLE state = *(HANDLE *)(&it->state);
if(state == 0)
{
MD_b32 need_star = 0;
if(path.str[path.size-1] == '/' ||
path.str[path.size-1] == '\\')
{
need_star = 1;
}
MD_String8 cpath = need_star ? MD_PushStringF("%.*s*", MD_StringExpand(path)) : path;
state = FindFirstFileA((char*)cpath.str, &find_data);
result = !!state;
}
else
{
result = !!FindNextFileA(state, &find_data);
}
it->state = *(MD_u64 *)(&state);
if(result)
{
out_info->flags = 0;
if(find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
out_info->flags |= MD_FileFlag_Directory;
}
out_info->filename = MD_PushStringF("%s", find_data.cFileName);
out_info->file_size = ((((MD_u64)find_data.nFileSizeHigh) << 32) |
((MD_u64)find_data.nFileSizeLow));
}
return result;
}
+9
View File
@@ -0,0 +1,9 @@
#include "md.h"
#include "md.c"
int main(void)
{
return 0;
}
+403
View File
@@ -0,0 +1,403 @@
#include "md.h"
#include "md.c"
static struct
{
int number_of_tests;
int number_passed;
}
test_ctx;
static void
BeginTest(char *name)
{
int length = MD_CalculateCStringLength(name);
int spaces = 25 - length;
if(spaces < 0)
{
spaces = 0;
}
printf("\"%s\" %.*s [", name, spaces, "------------------------------");
test_ctx.number_of_tests = 0;
test_ctx.number_passed = 0;
}
static void
TestResult(MD_b32 result)
{
test_ctx.number_of_tests += 1;
test_ctx.number_passed += !!result;
printf(result ? "." : "X");
}
static void
EndTest(void)
{
int spaces = 10 - test_ctx.number_of_tests;
if(spaces < 0) { spaces = 0; }
printf("]%.*s ", spaces, " ");
printf("[%i/%i] %i passed, %i tests, ",
test_ctx.number_passed, test_ctx.number_of_tests,
test_ctx.number_passed, test_ctx.number_of_tests);
if(test_ctx.number_of_tests == test_ctx.number_passed)
{
printf("SUCCESS ( )");
}
else
{
printf("FAILED (X)");
}
printf("\n");
}
#define Test(name) for(int _i_ = (BeginTest(name), 0); !_i_; _i_ += 1, EndTest())
static MD_Node *
MakeTestNode(MD_NodeKind kind, MD_String8 string)
{
return MD_MakeNodeFromString(kind, MD_S8Lit("`TEST_NODE`"), 0, 0, string);
}
static MD_Expr *
AtomExpr(char *str)
{
return MD_MakeExpr(MakeTestNode(MD_NodeKind_Label, MD_S8CString(str)), MD_ExprKind_Atom, MD_NilExpr(), MD_NilExpr());
}
static MD_Expr *
BinOpExpr(MD_ExprKind kind, MD_Expr *left, MD_Expr *right)
{
return MD_MakeExpr(MD_NilNode(), kind, left, right);
}
static MD_Expr *
TypeExpr(MD_ExprKind kind, MD_Expr *sub)
{
return MD_MakeExpr(MD_NilNode(), kind, sub, MD_NilExpr());
}
static MD_b32
MatchParsedWithNode(MD_String8 string, MD_Node *tree)
{
MD_ParseResult parse = MD_ParseOneNode(MD_S8Lit(""), string);
return MD_NodeDeepMatch(tree, parse.node, 0, MD_NodeMatchFlag_Tags | MD_NodeMatchFlag_TagArguments);
}
static MD_b32
MatchParsedWithExpr(MD_String8 string, MD_Expr *expr)
{
MD_ParseResult parse = MD_ParseOneNode(MD_S8Lit(""), string);
MD_Expr *parse_expr = MD_ParseAsExpr(parse.node->first_child, parse.node->last_child);
return MD_ExprDeepMatch(expr, parse_expr, 0);
}
static MD_b32
MatchParsedWithType(MD_String8 string, MD_Expr *expr)
{
MD_ParseResult parse = MD_ParseOneNode(MD_S8Lit(""), string);
MD_Expr *parse_expr = MD_ParseAsType(parse.node->first_child, parse.node->last_child);
return MD_ExprDeepMatch(expr, parse_expr, 0);
}
static MD_b32
TokenMatch(MD_Token token, MD_String8 string, MD_TokenKind kind)
{
return MD_StringMatch(string, token.string, 0) && token.kind == kind;
}
int main(void)
{
Test("Lexer")
{
MD_String8 string = MD_S8Lit("abc def 123 456 123_456 abc123 123abc");
MD_ParseCtx ctx = MD_Parse_InitializeCtx(MD_S8Lit(""), string);
TestResult(TokenMatch(MD_Parse_LexNext(&ctx), MD_S8Lit("abc"), MD_TokenKind_Identifier));
TestResult(TokenMatch(MD_Parse_LexNext(&ctx), MD_S8Lit(" "), MD_TokenKind_Whitespace));
TestResult(TokenMatch(MD_Parse_LexNext(&ctx), MD_S8Lit("def"), MD_TokenKind_Identifier));
TestResult(TokenMatch(MD_Parse_LexNext(&ctx), MD_S8Lit(" "), MD_TokenKind_Whitespace));
TestResult(TokenMatch(MD_Parse_LexNext(&ctx), MD_S8Lit("123"), MD_TokenKind_NumericLiteral));
TestResult(TokenMatch(MD_Parse_LexNext(&ctx), MD_S8Lit(" "), MD_TokenKind_Whitespace));
TestResult(TokenMatch(MD_Parse_LexNext(&ctx), MD_S8Lit("456"), MD_TokenKind_NumericLiteral));
TestResult(TokenMatch(MD_Parse_LexNext(&ctx), MD_S8Lit(" "), MD_TokenKind_Whitespace));
// TODO(rjf): Enable once numeric literal lexing is fixed
//TestResult(TokenMatch(MD_Parse_LexNext(&ctx), MD_S8Lit("123_456"), MD_TokenKind_NumericLiteral));
}
Test("Empty Sets")
{
TestResult(MatchParsedWithNode(MD_S8Lit("{}"), MakeTestNode(MD_NodeKind_UnnamedSet, MD_S8Lit(""))));
TestResult(MatchParsedWithNode(MD_S8Lit("()"), MakeTestNode(MD_NodeKind_UnnamedSet, MD_S8Lit(""))));
TestResult(MatchParsedWithNode(MD_S8Lit("[]"), MakeTestNode(MD_NodeKind_UnnamedSet, MD_S8Lit(""))));
TestResult(MatchParsedWithNode(MD_S8Lit("[)"), MakeTestNode(MD_NodeKind_UnnamedSet, MD_S8Lit(""))));
TestResult(MatchParsedWithNode(MD_S8Lit("(]"), MakeTestNode(MD_NodeKind_UnnamedSet, MD_S8Lit(""))));
}
Test("Simple Unnamed Sets")
{
{
MD_String8 string = MD_S8Lit("{a, b, c}");
MD_Node *tree = MakeTestNode(MD_NodeKind_UnnamedSet, MD_S8Lit(""));
MD_PushChild(tree, MakeTestNode(MD_NodeKind_Label, MD_S8Lit("a")));
MD_PushChild(tree, MakeTestNode(MD_NodeKind_Label, MD_S8Lit("b")));
MD_PushChild(tree, MakeTestNode(MD_NodeKind_Label, MD_S8Lit("c")));
TestResult(MatchParsedWithNode(string, tree));
}
{
MD_String8 string = MD_S8Lit("(1 2 3 4 5)");
MD_Node *tree = MakeTestNode(MD_NodeKind_UnnamedSet, MD_S8Lit(""));
MD_PushChild(tree, MakeTestNode(MD_NodeKind_Label, MD_S8Lit("1")));
MD_PushChild(tree, MakeTestNode(MD_NodeKind_Label, MD_S8Lit("2")));
MD_PushChild(tree, MakeTestNode(MD_NodeKind_Label, MD_S8Lit("3")));
MD_PushChild(tree, MakeTestNode(MD_NodeKind_Label, MD_S8Lit("4")));
MD_PushChild(tree, MakeTestNode(MD_NodeKind_Label, MD_S8Lit("5")));
TestResult(MatchParsedWithNode(string, tree));
}
{
MD_String8 string = MD_S8Lit("{a}");
MD_Node *tree = MakeTestNode(MD_NodeKind_UnnamedSet, MD_S8Lit(""));
MD_PushChild(tree, MakeTestNode(MD_NodeKind_Label, MD_S8Lit("a")));
TestResult(MatchParsedWithNode(string, tree));
}
}
Test("Simple Named Sets")
{
MD_String8 string = MD_S8Lit("simple_set: {a, b, c}");
MD_Node *tree = MakeTestNode(MD_NodeKind_Label, MD_S8Lit("simple_set"));
MD_PushChild(tree, MakeTestNode(MD_NodeKind_Label, MD_S8Lit("a")));
MD_PushChild(tree, MakeTestNode(MD_NodeKind_Label, MD_S8Lit("b")));
MD_PushChild(tree, MakeTestNode(MD_NodeKind_Label, MD_S8Lit("c")));
TestResult(MatchParsedWithNode(string, tree));
}
Test("Nested Sets")
{
{
MD_String8 string = MD_S8Lit("{a b:{1 2 3} c}");
MD_Node *tree = MakeTestNode(MD_NodeKind_UnnamedSet, MD_S8Lit(""));
MD_PushChild(tree, MakeTestNode(MD_NodeKind_Label, MD_S8Lit("a")));
{
MD_Node *sub = MakeTestNode(MD_NodeKind_Label, MD_S8Lit("b"));
MD_PushChild(sub, MakeTestNode(MD_NodeKind_Label, MD_S8Lit("1")));
MD_PushChild(sub, MakeTestNode(MD_NodeKind_Label, MD_S8Lit("2")));
MD_PushChild(sub, MakeTestNode(MD_NodeKind_Label, MD_S8Lit("3")));
MD_PushChild(tree, sub);
}
MD_PushChild(tree, MakeTestNode(MD_NodeKind_Label, MD_S8Lit("c")));
TestResult(MatchParsedWithNode(string, tree));
}
{
MD_String8 string = MD_S8Lit("foo: { (size: u64) -> *void }");
MD_Node *tree = MakeTestNode(MD_NodeKind_Label, MD_S8Lit("foo"));
MD_Node *params = MakeTestNode(MD_NodeKind_UnnamedSet, MD_S8Lit(""));
MD_Node *size = MakeTestNode(MD_NodeKind_Label, MD_S8Lit("size"));
MD_PushChild(size, MakeTestNode(MD_NodeKind_Label, MD_S8Lit("u64")));
MD_PushChild(params, size);
MD_PushChild(tree, params);
// TODO(rjf): This test will fail once we have digraphs implemented. Adjust the separate
// "-" and ">" set members, and combine them to form a single "->" set member.
// {
MD_PushChild(tree, MakeTestNode(MD_NodeKind_Label, MD_S8Lit("-")));
MD_PushChild(tree, MakeTestNode(MD_NodeKind_Label, MD_S8Lit(">")));
// }
MD_PushChild(tree, MakeTestNode(MD_NodeKind_Label, MD_S8Lit("*")));
MD_PushChild(tree, MakeTestNode(MD_NodeKind_Label, MD_S8Lit("void")));
TestResult(MatchParsedWithNode(string, tree));
}
}
Test("Non-Sets")
{
TestResult(MatchParsedWithNode(MD_S8Lit("foo"), MakeTestNode(MD_NodeKind_Label, MD_S8Lit("foo"))));
TestResult(MatchParsedWithNode(MD_S8Lit("123"), MakeTestNode(MD_NodeKind_Label, MD_S8Lit("123"))));
TestResult(MatchParsedWithNode(MD_S8Lit("+"), MakeTestNode(MD_NodeKind_Label, MD_S8Lit("+"))));
}
Test("Set Border Flags")
{
{
MD_ParseResult parse = MD_ParseOneNode(MD_S8Lit(""), MD_S8Lit("(0, 100)"));
TestResult(parse.node->flags & MD_NodeFlag_ParenLeft &&
parse.node->flags & MD_NodeFlag_ParenRight);
}
{
MD_ParseResult parse = MD_ParseOneNode(MD_S8Lit(""), MD_S8Lit("(0, 100]"));
TestResult(parse.node->flags & MD_NodeFlag_ParenLeft &&
parse.node->flags & MD_NodeFlag_BracketRight);
}
{
MD_ParseResult parse = MD_ParseOneNode(MD_S8Lit(""), MD_S8Lit("[0, 100)"));
TestResult(parse.node->flags & MD_NodeFlag_BracketLeft &&
parse.node->flags & MD_NodeFlag_ParenRight);
}
{
MD_ParseResult parse = MD_ParseOneNode(MD_S8Lit(""), MD_S8Lit("[0, 100]"));
TestResult(parse.node->flags & MD_NodeFlag_BracketLeft &&
parse.node->flags & MD_NodeFlag_BracketRight);
}
{
MD_ParseResult parse = MD_ParseOneNode(MD_S8Lit(""), MD_S8Lit("{0, 100}"));
TestResult(parse.node->flags & MD_NodeFlag_BraceLeft &&
parse.node->flags & MD_NodeFlag_BraceRight);
}
}
Test("Node Separator Flags")
{
{
MD_ParseResult parse = MD_ParseOneNode(MD_S8Lit(""), MD_S8Lit("(a, b)"));
TestResult(parse.node->first_child->flags & MD_NodeFlag_BeforeComma);
}
{
MD_ParseResult parse = MD_ParseOneNode(MD_S8Lit(""), MD_S8Lit("(a; b)"));
TestResult(parse.node->first_child->flags & MD_NodeFlag_BeforeSemicolon);
}
{
// TODO(rjf): Enable this once we have digraphs.
// MD_ParseResult parse = MD_ParseOneNode(MD_S8Lit(""), MD_S8Lit("(a -> b)"));
// TestResult(parse.node->first_child->flags & MD_NodeFlag_BeforeArrow);
}
}
Test("Node Text Flags")
{
TestResult(MD_ParseOneNode(MD_S8Lit(""), MD_S8Lit("123")).node->flags &
MD_NodeFlag_Numeric);
TestResult(MD_ParseOneNode(MD_S8Lit(""), MD_S8Lit("123_456_789")).node->flags &
MD_NodeFlag_Numeric);
TestResult(MD_ParseOneNode(MD_S8Lit(""), MD_S8Lit("abc")).node->flags &
MD_NodeFlag_Identifier);
TestResult(MD_ParseOneNode(MD_S8Lit(""), MD_S8Lit("\"foo\"")).node->flags &
MD_NodeFlag_StringLiteral);
TestResult(MD_ParseOneNode(MD_S8Lit(""), MD_S8Lit("'foo'")).node->flags &
MD_NodeFlag_CharLiteral);
}
Test("Expression Evaluation")
{
// NOTE(rjf): 5 + 3
{
MD_Expr *expr = BinOpExpr(MD_ExprKind_Add, AtomExpr("5"), AtomExpr("3"));
TestResult(MD_EvaluateExpr_I64(expr) == 8);
}
// NOTE(rjf): 5 - 3
{
MD_Expr *expr = BinOpExpr(MD_ExprKind_Subtract, AtomExpr("5"), AtomExpr("3"));
TestResult(MD_EvaluateExpr_I64(expr) == 2);
}
// NOTE(rjf): 5 * 3
{
MD_Expr *expr = BinOpExpr(MD_ExprKind_Multiply, AtomExpr("5"), AtomExpr("3"));
TestResult(MD_EvaluateExpr_I64(expr) == 15);
}
// NOTE(rjf): 10 / 2
{
MD_Expr *expr = BinOpExpr(MD_ExprKind_Divide, AtomExpr("10"), AtomExpr("2"));
TestResult(MD_EvaluateExpr_I64(expr) == 5);
}
// NOTE(rjf): (3 + 4) * (2 + 6)
{
MD_Expr *left = BinOpExpr(MD_ExprKind_Add, AtomExpr("3"), AtomExpr("4"));
MD_Expr *right = BinOpExpr(MD_ExprKind_Add, AtomExpr("2"), AtomExpr("6"));
MD_Expr *expr = BinOpExpr(MD_ExprKind_Multiply, left, right);
TestResult(MD_EvaluateExpr_I64(expr) == 56);
}
}
Test("Expression Parsing")
{
{
MD_String8 string = MD_S8Lit("(1 + 2)");
MD_Expr *expr = BinOpExpr(MD_ExprKind_Add, AtomExpr("1"), AtomExpr("2"));
TestResult(MatchParsedWithExpr(string, expr));
}
{
MD_String8 string = MD_S8Lit("((3 + 4) * (2 + 6))");
MD_Expr *left = BinOpExpr(MD_ExprKind_Add, AtomExpr("3"), AtomExpr("4"));
MD_Expr *right = BinOpExpr(MD_ExprKind_Add, AtomExpr("2"), AtomExpr("6"));
MD_Expr *expr = BinOpExpr(MD_ExprKind_Multiply, left, right);
TestResult(MatchParsedWithExpr(string, expr));
}
{
MD_String8 string = MD_S8Lit("(1*2+3)");
MD_Expr *left = BinOpExpr(MD_ExprKind_Multiply, AtomExpr("1"), AtomExpr("2"));
MD_Expr *expr = BinOpExpr(MD_ExprKind_Add, left, AtomExpr("3"));
TestResult(MatchParsedWithExpr(string, expr));
}
}
Test("Type Parsing")
{
{
MD_String8 string = MD_S8Lit("(i32)");
MD_Expr *expr = AtomExpr("i32");
TestResult(MatchParsedWithType(string, expr));
}
{
MD_String8 string = MD_S8Lit("(*i32)");
MD_Expr *expr = TypeExpr(MD_ExprKind_Pointer, AtomExpr("i32"));
TestResult(MatchParsedWithType(string, expr));
}
{
MD_String8 string = MD_S8Lit("(**i32)");
MD_Expr *expr = TypeExpr(MD_ExprKind_Pointer, TypeExpr(MD_ExprKind_Pointer, AtomExpr("i32")));
TestResult(MatchParsedWithType(string, expr));
}
{
MD_String8 string = MD_S8Lit("(*void)");
MD_Expr *expr = TypeExpr(MD_ExprKind_Pointer, AtomExpr("void"));
TestResult(MatchParsedWithType(string, expr));
}
}
Test("Style Strings")
{
{
MD_String8 str = MD_StyledStringFromString(MD_S8Lit("THIS_IS_A_TEST"), MD_WordStyle_UpperCamelCase, MD_S8Lit(" "));
TestResult(MD_StringMatch(str, MD_S8Lit("This Is A Test"), 0));
}
{
MD_String8 str = MD_StyledStringFromString(MD_S8Lit("this_is_a_test"), MD_WordStyle_UpperCamelCase, MD_S8Lit(" "));
TestResult(MD_StringMatch(str, MD_S8Lit("This Is A Test"), 0));
}
{
MD_String8 str = MD_StyledStringFromString(MD_S8Lit("ThisIsATest"), MD_WordStyle_UpperCamelCase, MD_S8Lit(" "));
TestResult(MD_StringMatch(str, MD_S8Lit("This Is A Test"), 0));
}
{
MD_String8 str = MD_StyledStringFromString(MD_S8Lit("Here is another test."), MD_WordStyle_UpperCamelCase, MD_S8Lit(""));
TestResult(MD_StringMatch(str, MD_S8Lit("HereIsAnotherTest."), 0));
}
}
Test("Enum Strings")
{
TestResult(MD_StringMatch(MD_StringFromNodeKind(MD_NodeKind_Label), MD_S8Lit("Label"), 0));
TestResult(MD_StringMatch(MD_StringFromNodeKind(MD_NodeKind_UnnamedSet), MD_S8Lit("UnnamedSet"), 0));
MD_String8List list = MD_StringListFromNodeFlags(MD_NodeFlag_CharLiteral | MD_NodeFlag_ParenLeft | MD_NodeFlag_BeforeSemicolon);
MD_b32 match = 1;
for(MD_String8Node *node = list.first; node; node = node->next)
{
if(!MD_StringMatch(node->string, MD_S8Lit("CharLiteral"), 0) &&
!MD_StringMatch(node->string, MD_S8Lit("ParenLeft"), 0) &&
!MD_StringMatch(node->string, MD_S8Lit("BeforeSemicolon"), 0))
{
match = 0;
break;
}
}
TestResult(match);
}
return 0;
}
+26
View File
@@ -0,0 +1,26 @@
#include "md.h"
#include "md.c"
void run_test_on_string(MD_String8 string)
{
MD_String16 s16 = MD_S16FromS8(string);
MD_String8 s8_ts16 = MD_S8FromS16(s16);
MD_Assert(MD_StringMatch(s8_ts16, string, 0));
MD_String32 s32 = MD_S32FromS8(string);
MD_String8 s8_ts32 = MD_S8FromS32(s32);
MD_Assert(MD_StringMatch(s8_ts32, string, 0));
}
int main(void)
{
// TODO(allen): throw more at this.
char test_string_c[] = "Foo bar; test the unicode\n\t\0Etc";
MD_String8 test_string = MD_S8(test_string_c, sizeof(test_string_c) - 1);
run_test_on_string(test_string);
return 0;
}