diff --git a/README.md b/README.md index bc4546f..2656c97 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,6 @@ result can be more easily precompiled for native execution. - overall philosophy - easy to target for compilers - easy to implement with overhead - - possible to implement with no overhead - - can use easy but "slow" or DIY + - possible to implement with no/low overhead + - can use easy but "slow" or DIY? - e.g. MALL vs static memory and custom allocation diff --git a/doc/cheatsheet.tex b/doc/cheatsheet.tex index af01649..340eb81 100644 --- a/doc/cheatsheet.tex +++ b/doc/cheatsheet.tex @@ -1,6 +1,6 @@ \documentclass{article} -\usepackage[letterpaper, landscape, margin=1cm]{geometry} +\usepackage[letterpaper, landscape, margin=0.7cm]{geometry} \usepackage{tabularx} \usepackage{tabulary} @@ -48,6 +48,8 @@ \newif\ifnoopdef \newif\ifopisfun \newif\ifopisimm +\newif\ifopisgud +\newif\ifopisbad \newcommand{\optextdescr}{} \newcommand{\optext}[1]{\renewcommand{\optextdescr}{#1}\noopdeffalse} @@ -56,158 +58,223 @@ \noopdeftrue \opisfunfalse \opisimmfalse + \opisgudfalse + \opisbadfalse - % TODO: document and add more metasyntactic variables/functions + % TODO: document and add more metasyntactic variables/functions? % So far uncategorized instructions - \if0#1 - \optext{branch to immediate if x is not zero} - \stackdiagram{x,\ldots}{\ldots} - \fi - \if9#1 - \optext{compare and swap x and y at p, push success} - \stackdiagram{y,x,p,\ldots}{z,\ldots} - \fi - \if E#1 - \optext{round a to integer and store in x} - \stackdiagram{a,\ldots}{x,\ldots} - \fi - \if L#1 - \optext{roll stack by immediate places} - \stackdiagram{z,\cdots,y,x,\ldots}{x,z,\cdots,y,\ldots} - \opisimmtrue - \fi - \if U#1 - \optext{compare x to y unsigned and set z such that x o y is z o 0} - \stackdiagram{y,x,\ldots}{z,\ldots} - \fi - \if V#1 - \optext{bitwise OR x and y} - \stackdiagram{y,x,\ldots}{x\vee{}y,\ldots} - \fi - \if W#1 - \optext{truncate x to 32 bits, then sign extend} - \stackdiagram{x,\ldots}{x,\ldots} - \fi \if d#1 \optext{set x bytes of memory to immediate value at p} + \opisbadtrue \stackdiagram{x,p,\ldots}{\ldots} \fi - \if e#1 - \optext{rotate stack} - \stackdiagram{z,y,x,\ldots}{x,z,y,\ldots} - \fi \if f#1 \optext{invoke f, saving return address on stack} \opisfuntrue \stackdiagram{f,\ldots}{g,\ldots} \fi - \if h#1 - \optext{stack over} - \stackdiagram{y,x,\ldots}{x,y,x,\ldots} - \fi - \if j#1 - \optext{negate a} - \stackdiagram{a,\ldots}{-a,\ldots} - \fi - \if k#1 - \optext{stack pop} - \stackdiagram{x,\ldots}{\ldots} - \fi - \if l#1 - \optext{bit clear y from x to z} - \stackdiagram{y,x,\ldots}{(x\wedge\neg{}y} - \fi - \if m#1 - \optext{floating point difference} - \stackdiagram{b,a,\ldots}{a-b} - \fi - \if p#1 - \optext{floating point multiply} - \stackdiagram{b,a,\ldots}{a\times{}b,\ldots} - \fi - \if q#1 - \optext{floating point divide} - \stackdiagram{b,a,\ldots}{b\over{}a,\ldots} - \fi - \if s#1 - \optext{floating point add} - \stackdiagram{b,a,\ldots}{b+a,\ldots} - \fi - \if t#1 - \optext{stack swap} - \stackdiagram{y,x,\ldots}{x,y,\ldots} - \fi - \if u#1 - \optext{logical shift x by immediate bits right} - \opisimmtrue - \stackdiagram{x,\ldots}{x,\ldots} - \fi - \if v#1 - \optext{floating point absolute value} - \stackdiagram{a,\ldots}{|a|,\ldots} - \fi - \if w#1 - \optext{stack duplicate} - \stackdiagram{x,\ldots}{x,x,\ldots} - \fi - \ifx#1\_ - \optext{negate x} - \stackdiagram{x,\ldots}{-x,\ldots} - \fi - \if .#1 - \optext{convert x to a float} - \stackdiagram{x,\ldots}{a,\ldots} - \fi - \if +#1 - \optext{integer add} - \stackdiagram{y,x,\ldots}{x+y,\ldots} - \fi - \if -#1 - \optext{integer subtract} - \stackdiagram{y,x,\ldots}{x-y,\ldots} - \fi - \if *#1 - \optext{integer multiply} - \stackdiagram{y,x,\ldots}{x\times{}y,\ldots} - \fi - \if /#1 - \optext{integer divide} - \stackdiagram{y,x,\ldots}{x\div{}y,\ldots} - \fi - \ifx#1\% - \optext{integer modulo} - \stackdiagram{y,x,\ldots}{x\ \textrm{mod}\ y,\ldots} - \fi - \ifx#1\textbackslash - \optext{integer remainder} - \stackdiagram{y,x,\ldots}{x\%y,\ldots} - \fi - \if |#1 - \optext{integer absolute value} - \stackdiagram{x,\ldots}{|x|,\ldots} - \fi - \ifx#1\$ - \optext{load constant value} - \opisimmtrue - \stackdiagram{\ldots}{\star{},\ldots} - \fi \if @#1 \optext{call immediate name} \opisimmtrue \opisfuntrue + \opisgudtrue \stackdiagram{\ldots}{f,\ldots} \fi + \if `#1 + \optext{call builtin with given name/value} + \opisimmtrue + \opisgudtrue + \stackdiagram{?,\ldots}{?,\ldots} + \fi + \if M#1 + \optext{call math operation with given name/value} + \opisimmtrue + \opisgudtrue + \stackdiagram{?,\ldots}{?,\ldots} + \fi + + % Memory management functions + \if F#1 + \optext{free x bytes from p} + \stackdiagram{x,p,\ldots}{\ldots} + \fi + \if R#1 + \optext{reallocate p to x bytes} + \stackdiagram{x,p,\ldots}{p,\ldots} + \fi + + % Comparisons + \if9#1 + \optext{compare and swap x and y at p, push success} + \stackdiagram{y,x,p,\ldots}{z,\ldots} + \fi + \if U#1 + \optext{compare x to y unsigned and set z such that x o y is z o 0} + \opisgudtrue + \stackdiagram{y,x,\ldots}{z,\ldots} + \fi + \if ?#1 + \optext{compare x to y and set z such that x o y is z o 0} + \opisgudtrue + \stackdiagram{x,y,\ldots}{z,\ldots} + \fi + \ifx#1\textasciitilde + \optext{compare a to b within an immediate error} + \opisgudtrue + \opisimmtrue + \stackdiagram{a,b,\ldots}{c,\ldots} + \fi + + % Syntactical "instructions" + \ifx#1\textvisiblespace + \optext{do nothing} + \opisgudtrue + \stackdiagram{\ldots}{\ldots} + \fi + \ifx#1\# + \optext{comment} + \opisgudtrue + \stackdiagram{ }{ } + \fi + \ifx#1\P + \optext{mark a beat for relative branching} + \opisgudtrue + \stackdiagram{\ldots}{\ldots} + \fi + \if :#1 + \optext{label line} + \opisgudtrue + \stackdiagram{\ldots}{\ldots} + \fi + \if '#1 + \optext{breakpoint} + \opisgudtrue + \stackdiagram{ }{ } + \fi + \if "#1 + \optext{change section} + \opisgudtrue + \stackdiagram{ }{ } + \fi + \ifx#1\$ + \optext{push constant value} + \opisimmtrue + \opisgudtrue + \stackdiagram{\ldots}{\star{},\ldots} + \fi + + % Stack manipulation instructions + \if L#1 + \optext{roll stack by immediate places} + \opisgudtrue + \stackdiagram{z,\cdots,y,x,\ldots}{x,z,\cdots,y,\ldots} + \opisimmtrue + \fi + \if T#1 + \optext{push top of stack down by immediate places} + \opisgudtrue + \stackdiagram{z,y,\cdots,x,\ldots}{z,\cdots,x,y,\ldots} + \opisimmtrue + \fi + \if e#1 + \optext{rotate stack} + \opisgudtrue % named based on shape (take bottom to top) + \stackdiagram{z,y,x,\ldots}{x,z,y,\ldots} + \fi + \if h#1 + \optext{stack over} + \opisgudtrue + \stackdiagram{y,x,\ldots}{x,y,x,\ldots} + \fi + \if k#1 + \optext{stack pop} + \opisgudtrue + \stackdiagram{x,\ldots}{\ldots} + \fi + \if t#1 + \optext{stack swap} + \opisgudtrue % named after the Thrush combinator + \stackdiagram{y,x,\ldots}{x,y,\ldots} + \fi + \if w#1 + \optext{stack duplicate} + \opisgudtrue % named after the Warbler combinator + \stackdiagram{x,\ldots}{x,x,\ldots} + \fi + \if (#1 + \optext{set x to depth of stack} + \stackdiagram{\ldots}{x,\ldots} + \fi + \if )#1 + \optext{pack x elements of stack into array p} + \stackdiagram{x,p,\ldots}{p,\ldots} + \fi + + % Integer operations + \if V#1 + \optext{bitwise OR x and y} + \opisgudtrue + \stackdiagram{y,x,\ldots}{x\vee{}y,\ldots} + \fi + \if l#1 + \optext{bit clear y from x to z} + \opisgudtrue + \stackdiagram{y,x,\ldots}{(x\wedge\neg{}y)} + \fi + \ifx#1\_ + \optext{negate x} + \opisgudtrue + \stackdiagram{x,\ldots}{-x,\ldots} + \fi + \if +#1 + \optext{integer add} + \opisgudtrue + \stackdiagram{y,x,\ldots}{x+y,\ldots} + \fi + \if -#1 + \optext{integer subtract} + \opisgudtrue + \stackdiagram{y,x,\ldots}{x-y,\ldots} + \fi + \if *#1 + \optext{integer multiply} + \opisgudtrue + \stackdiagram{y,x,\ldots}{x\times{}y,\ldots} + \fi + \if /#1 + \optext{integer divide} + \opisgudtrue + \stackdiagram{y,x,\ldots}{x\div{}y,\ldots} + \fi + \ifx#1\% + \optext{integer modulo} + \opisgudtrue + \stackdiagram{y,x,\ldots}{x\ \textrm{mod}\ y,\ldots} + \fi + \ifx#1\textbackslash + \optext{integer remainder} + \opisgudtrue + \stackdiagram{y,x,\ldots}{x\%y,\ldots} + \fi + \if |#1 + \optext{integer absolute value} + \opisgudtrue + \stackdiagram{x,\ldots}{|x|,\ldots} + \fi \ifx#1\& \optext{set z to the bitwise and of x and y} + \opisgudtrue \stackdiagram{x,y,\ldots}{x\wedge{}y} \fi \ifx#1\textasciicircum \optext{set z to the bitwise xor of x and y} + \opisgudtrue \stackdiagram{x,y,\ldots}{x\oplus{}y} \fi \if !#1 \optext{set y to the bitwise not of x} + \opisgudtrue \stackdiagram{x,\ldots}{y,\ldots} \fi \if [#1 @@ -220,68 +287,90 @@ \opisimmtrue \stackdiagram{x,\ldots}{x,\ldots} \fi - \if ?#1 - \optext{compare x to y and set z such that x o y is z o 0} - \stackdiagram{x,y,\ldots}{z,\ldots} - \fi - \ifx#1\textasciitilde - \optext{compare a to b within an immediate error} + \if u#1 + \optext{logical shift x by immediate bits right} \opisimmtrue - \stackdiagram{a,b,\ldots}{c,\ldots} + \stackdiagram{x,\ldots}{x,\ldots} \fi - \if :#1 - \optext{label code location} - \stackdiagram{\ldots}{\ldots} + + % Float operations + \if j#1 + \optext{negate a} + \opisgudtrue + \stackdiagram{a,\ldots}{-a,\ldots} \fi - \if (#1 - \optext{set x to depth of stack} - \stackdiagram{\ldots}{x,\ldots} + \if m#1 + \optext{floating point difference} + \opisgudtrue + \stackdiagram{b,a,\ldots}{a-b} \fi - \if )#1 - \optext{pack x elements of stack into array p} - \stackdiagram{x,p,\ldots}{p,\ldots} + \if p#1 + \optext{floating point multiply} + \opisgudtrue + \stackdiagram{b,a,\ldots}{a\times{}b,\ldots} \fi - \if '#1 - \optext{embed data} - \stackdiagram{ }{ } + \if q#1 + \optext{floating point divide} + \opisgudtrue + \stackdiagram{b,a,\ldots}{b\over{}a,\ldots} \fi - \if "#1 - \optext{change section} - \stackdiagram{ }{ } + \if s#1 + \optext{floating point add} + \opisgudtrue + \stackdiagram{b,a,\ldots}{b+a,\ldots} \fi - \if `#1 - \optext{call builtin with given name/value} - \opisimmtrue - \stackdiagram{?,\ldots}{?,\ldots} + \if v#1 + \optext{floating point absolute value} + \opisgudtrue + \stackdiagram{a,\ldots}{|a|,\ldots} \fi - \ifx#1\textvisiblespace - \optext{do nothing} - \stackdiagram{\ldots}{\ldots} + + % Conversion instructions + \if .#1 + \optext{convert x to a float} + \opisgudtrue + \stackdiagram{x,\ldots}{a,\ldots} \fi - \ifx#1\# - \optext{comment} - \stackdiagram{ }{ } + \if E#1 + \optext{round a to integer and store in x} + \opisgudtrue + \stackdiagram{a,\ldots}{x,\ldots} \fi - \ifx#1\P - \optext{mark a beat for relative branching} - \stackdiagram{\ldots}{\ldots} + \if W#1 + \optext{truncate x to 32 bits, then sign extend} + \opisgudtrue + \stackdiagram{x,\ldots}{x,\ldots} + \fi + \if S#1 + \optext{truncate x to 16 bits, then sign extend} + \opisgudtrue + \stackdiagram{x,\ldots}{x,\ldots} + \fi + \if O#1 + \optext{truncate x to 8 bits, then sign extend} + \opisgudtrue + \stackdiagram{x,\ldots}{x,\ldots} \fi % Load instructions \if1#1 \optext{load byte x from p} + \opisgudtrue \stackdiagram{p,\ldots}{x,\ldots} \fi \if2#1 \optext{load short x from p} + \opisgudtrue \stackdiagram{p,\ldots}{x,\ldots} \fi \if3#1 \optext{load int x from p} + \opisgudtrue \stackdiagram{p,\ldots}{x,\ldots} \fi \if4#1 \optext{load word x from p} + \opisgudtrue \stackdiagram{p,\ldots}{x,\ldots} \fi \if G#1 @@ -303,122 +392,158 @@ % Store instructions \if5#1 \optext{store byte x to p} + \opisgudtrue \stackdiagram{x,p,\ldots}{\ldots} \fi \if6#1 \optext{store short x to p} + \opisgudtrue \stackdiagram{x,p,\ldots}{\ldots} \fi \if7#1 \optext{store int x to p} + \opisgudtrue \stackdiagram{x,p,\ldots}{\ldots} \fi \if8#1 \optext{store word x to p} + \opisgudtrue \stackdiagram{x,p,\ldots}{\ldots} \fi % Register instructions \if A#1 \optext{store x in rA} + \opisgudtrue \stackdiagram{x,\ldots}{\ldots} \fi \if B#1 \optext{store x in rB} + \opisgudtrue \stackdiagram{x,\ldots}{\ldots} \fi \if C#1 \optext{store x in rC} + \opisgudtrue \stackdiagram{x,\ldots}{\ldots} \fi \if I#1 \optext{store x in rI} + \opisgudtrue + \stackdiagram{x,\ldots}{\ldots} + \fi + \if N#1 + \optext{store x in rN} + \opisgudtrue \stackdiagram{x,\ldots}{\ldots} \fi \if X#1 \optext{store x in rX} + \opisgudtrue \stackdiagram{x,\ldots}{\ldots} \fi \if Y#1 \optext{store x in rY} + \opisgudtrue \stackdiagram{x,\ldots}{\ldots} \fi \if Z#1 \optext{store x in rZ} + \opisgudtrue \stackdiagram{x,\ldots}{\ldots} \fi \if a#1 \optext{load x from rA} + \opisgudtrue \stackdiagram{\ldots}{x,\ldots} \fi \if b#1 \optext{load x from rB} + \opisgudtrue \stackdiagram{\ldots}{x,\ldots} \fi \if c#1 \optext{load x from rC} + \opisgudtrue \stackdiagram{\ldots}{x,\ldots} \fi \if i#1 \optext{load x from rI} + \opisgudtrue \stackdiagram{\ldots}{x,\ldots} \fi \if n#1 \optext{load x from rN} + \opisgudtrue \stackdiagram{\ldots}{x,\ldots} \fi \if x#1 \optext{load x from rX} + \opisgudtrue \stackdiagram{\ldots}{x,\ldots} \fi \if y#1 \optext{load x from rY} + \opisgudtrue \stackdiagram{\ldots}{x,\ldots} \fi \if z#1 \optext{load x from rZ} + \opisgudtrue \stackdiagram{\ldots}{x,\ldots} \fi % Branch instructions + \if0#1 + \optext{branch to immediate if x is not zero} + \opisgudtrue + \stackdiagram{x,\ldots}{\ldots} + \fi \if <#1 \optext{branch to immediate if x is less than zero} + \opisgudtrue \opisimmtrue \opisfuntrue \stackdiagram{x,\ldots}{\ldots} \fi \ifx#1\{ \optext{branch to immediate if x is less than or equal to zero} + \opisgudtrue \opisimmtrue \opisfuntrue \stackdiagram{x,\ldots}{\ldots} \fi \if =#1 \optext{branch to immediate if x is zero} + \opisgudtrue \opisimmtrue \opisfuntrue \stackdiagram{x,\ldots}{\ldots} \fi \ifx#1\} \optext{branch to immediate if x is greater or equal to zero} + \opisgudtrue \opisimmtrue \opisfuntrue \stackdiagram{x,\ldots}{\ldots} \fi \if >#1 \optext{branch to immediate if x is greater than zero} + \opisgudtrue \opisimmtrue \opisfuntrue \stackdiagram{x,\ldots}{\ldots} \fi \if ,#1 \optext{jump to immediate without pushing return address} + \opisgudtrue \opisimmtrue \opisfuntrue \stackdiagram{\ldots}{\ldots} \fi \if ;#1 \optext{return from subroutine} + \opisgudtrue \opisfuntrue \stackdiagram{f,\ldots}{\ldots} \fi @@ -429,6 +554,14 @@ \stackdiagram{?}{?} \fi + \ifopisgud + \tikzset{every node/.style={color=black!25!green}} + \fi + + \ifopisbad + \tikzset{every node/.style={color=black!25!red}} + \fi + \ifopisfun \ifopisimm \tikzset{->/.style={arrows={Arc Barb[reversed,length=3pt]-Latex[length=5pt]}, double distance=1pt}} diff --git a/doc/documentation.md b/doc/documentation.md index ce95bb0..99e5c1b 100644 --- a/doc/documentation.md +++ b/doc/documentation.md @@ -89,6 +89,11 @@ Need better description of data '' format. See QBE's data for inspiration? Consider having each op-code mean something different in different sections? Only consistency would be labels... those could be more universally parsed? Alternatively remove sections altogether and use only dynamic memory? +I prefer sections, start with just implementations/documentation/specification +of code/data. For JIT code is obvious, data is variables loaded with appropriate +data and "passed through" to JIT. +Ideally keep immediate instructions consistent in each section (easier +parsing/highlighting/etc)? ``` ## File Format diff --git a/misc/utils.m4 b/misc/utils.m4 index d5d28ff..e0c2157 100644 --- a/misc/utils.m4 +++ b/misc/utils.m4 @@ -1,11 +1,9 @@ divert(`-1')dnl changequote(`«', `»') -changecom(«/*», «*/») +changecom(«\») \ note: use https://github.com/vim-scripts/syntaxm4.vim.git vim-m4-syntax: quote=«,» comment=\\ -define(«TICK», changequote([,])[changequote([,])»changequote(«,»)]changequote(«,»)) - \ forloop(var, from, to, stmt) - improved version: \ works even if VAR is not a strict macro name \ performs sanity check that FROM is larger than TO diff --git a/samples/hello.arx b/samples/hello.arx new file mode 100644 index 0000000..71d5dc7 --- /dev/null +++ b/samples/hello.arx @@ -0,0 +1,9 @@ +#!/usr/bin/env arctic + +"data" +:message sHello\ World!\x0A + +"code" +@main +:main $15 $message $1 `1 $0 ; + diff --git a/share/vim/syntax/arctic.vim b/share/vim/syntax/arctic.vim index 8c15143..451b658 100644 --- a/share/vim/syntax/arctic.vim +++ b/share/vim/syntax/arctic.vim @@ -11,12 +11,21 @@ let b:current_syntax = "arctic" syntax match arcticComment "\v#.*$" highlight link arcticComment Comment -syntax region arcticData start=/'/ end=/'/ -highlight link arcticData Character +syntax region arcticEmbed start=/'/ end=/'/ +highlight link arcticEmbed Character syntax region arcticSection start=/"/ end=/"/ highlight link arcticSection PreProc +syntax region arcticData keepend start=/\c"data"/ end=/"/ contains=arcticData.*,arcticLabel,arcticSection +syntax region arcticCode keepend start=/\c"code"/ end=/"/ contains=arcticCode.*,arcticLabel,arcticCall,arcticConstant,arcticSection + +syntax match arcticDataImmediate "\v[][a-zA-Z0-9_]([^ ]|\\ )*" +highlight link arcticDataImmediate Statement + +syntax match arcticDataOperator "\v[-.+*/%\|;&^!?()]" +highlight link arcticDataOperator Operator + syntax match arcticLabel "\v:[^ ]*" highlight link arcticLabel Label @@ -26,8 +35,8 @@ highlight link arcticCall Function syntax match arcticConstant "\v\$[^ ]*" highlight link arcticConstant Constant -syntax match arcticImmediate "\v[][0EFGHLMPQTdou$@<{=}>,`][^ ]*" -highlight link arcticImmediate Statement +syntax match arcticCodeImmediate "\v[][0EFGHLMPQTdou@<{=}>,`]([^ ]|\\ )*" +highlight link arcticCodeImmediate Statement -syntax match arcticOperator "\v[-123456789ABCDIJKNORSUVWXYZabcefghijklmnpqrstvwxyz_.+*/%\|;&^!?()]" -highlight link arcticOperator Operator +syntax match arcticCodeOperator "\v[-123456789ABCDIJKNORSUVWXYZabcefghijklmnpqrstvwxyz_.+*/%\|;&^!?()]" +highlight link arcticCodeOperator Operator diff --git a/src/architectures.c b/src/architectures.c index 457315b..cccde26 100644 --- a/src/architectures.c +++ b/src/architectures.c @@ -1,3 +1,37 @@ +#include + #include "architectures.h" -#include "arch/x86_64.h" +enum Architecture parse_arch(const char *arch) { + if (!arch) + return ARCH_NATIVE; + + if (!strncmp(arch, "x86_64", 6)) { + return ARCH_X86_64; + } + + return ARCH_NATIVE; +} + +const char *archstr(enum Architecture arch) { + switch (arch) { + case ARCH_NATIVE: + return "native"; + + case ARCH_INTERPRET: + return "interpret"; + + case ARCH_X86_64: + return "x86_64"; + } + + return 0; +} + +enum Architecture getnative() { +#ifdef __x86_64__ + return ARCH_X86_64; +#else + return ARCH_INTERPRET; +#endif +} diff --git a/src/architectures.h b/src/architectures.h index 468ddc1..9e94e87 100644 --- a/src/architectures.h +++ b/src/architectures.h @@ -2,7 +2,17 @@ #define ARCHITECTURES_H enum Architecture { - ARCH_NATIVE + ARCH_NATIVE, + ARCH_INTERPRET, + + ARCH_X86_64 }; +/* when given a null pointer, defaults to ARCH_NATIVE */ +enum Architecture parse_arch(const char *arch); + +const char *archstr(enum Architecture arch); + +enum Architecture getnative(); + #endif /* ARCHITECTURES_H */ diff --git a/src/arctic.h b/src/arctic.h new file mode 100644 index 0000000..81de6c7 --- /dev/null +++ b/src/arctic.h @@ -0,0 +1,53 @@ +#ifndef ARCTIC_H +#define ARCTIC_H + +#include + +enum ArcticVariableIndex { + VARIABLE_A = 0, VARIABLE_B, VARIABLE_C, + VARIABLE_I, VARIABLE_N, + VARIABLE_X, VARIABLE_Y, VARIABLE_Z, + VARIABLE_MAX +}; + +enum ArcticImmediateKind { + PLAIN_IMMEDIATE, VARIABLE_IMMEDIATE, INTEGER_IMMEDIATE, NUMERIC_IMMEDIATE, SYMBOLIC_IMMEDIATE +}; + +struct ArcticImmediate { + enum ArcticImmediateKind kind; + union { + enum ArcticVariableIndex v; + int64_t i; + double n; + const char *s; + }; +}; + +struct ArcticOperation { + char code; + uint8_t bigendian; + struct ArcticImmediate immediate; +}; + +union ArcticType { + int8_t b; + uint8_t ub; + int16_t s; + uint16_t us; + int32_t w; + uint32_t uw; + int64_t i; + uint64_t u; + double d; + uint8_t *p; + struct ArcticOperation *f; +}; + +struct ArcticState { + +}; + +int parse_arctic(struct ArcticState *state, const char *arctic); + +#endif /* ARCTIC_H */ diff --git a/src/args.c b/src/args.c index e67fbbb..c78cbda 100644 --- a/src/args.c +++ b/src/args.c @@ -1,10 +1,29 @@ #include "args.h" #include +#include static int parse_opt(int key, char *arg, struct argp_state *state) { - switch (key) { + struct Arguments *args = state->input; + switch (key) { + case 'i': + args->behaviour = BEHAVIOUR_INTERPRET; + return 0; + + case 'j': + args->behaviour = BEHAVIOUR_JIT; + args->jit.arch = parse_arch(arg); + return 0; + + case 'a': + args->behaviour = BEHAVIOUR_ASM; + args->assemble.arch = parse_arch(arg); + return 0; + + case ARGP_KEY_ARG: + strncpy(args->program, arg, PATH_MAX); + return 0; } return 0; @@ -21,5 +40,7 @@ int parse_arguments(struct Arguments *args, int argc, char *argv[]) { const char *args_doc = "PROGRAM"; struct argp argp = { options, parse_opt, args_doc, 0, 0, 0 }; - return argp_parse(&argp, argc, argv, 0, 0, args); + int result; + argp_parse(&argp, argc, argv, 0, &result, args); + return result; } diff --git a/src/args.h b/src/args.h index 0bae1fb..7c6d277 100644 --- a/src/args.h +++ b/src/args.h @@ -3,7 +3,7 @@ #include "architectures.h" -#include +#include enum Behaviour { BEHAVIOUR_INTERPRET, @@ -13,12 +13,13 @@ enum Behaviour { struct Arguments { enum Behaviour behaviour; - FILE *program; + char program[PATH_MAX + 1]; union { struct { } interpret; struct { + enum Architecture arch; } jit; struct { diff --git a/src/critbit.c b/src/critbit.c new file mode 100644 index 0000000..e938bb8 --- /dev/null +++ b/src/critbit.c @@ -0,0 +1,301 @@ +#include "critbit.h" + +#include +#include +#include + +struct CritBitNode { + unsigned int isleaf:1; + + union { + struct { + unsigned int byte:31; + uint8_t others; + struct CritBitNode *left; /* lefts are 0s */ + struct CritBitNode *right; /* rights are 1s */ + }; + struct { + unsigned int valuelen:31; + void *value; + char *key; + }; + }; +}; + +void crit_bit_init(struct CritBitTree *tree, void (*free_data)(void *data, int datalen)) { + if (!tree) return; + + tree->count = 0; + tree->root = 0; + tree->free_data = free_data; +} + +static void free_node(void (*free_data)(void *data, int datalen), struct CritBitNode *node) { + if (!node) return; + if (node->isleaf) { + if (free_data) + free_data(node->value, node->valuelen); + free(node->key); + free(node); + } else { + free_node(free_data, node->left); + free_node(free_data, node->right); + free(node); + } +} + +void crit_bit_free(struct CritBitTree *tree) { + if (tree->root) + free_node(tree->free_data, tree->root); + + tree->root = 0; + tree->count = 0; +} + +static struct CritBitNode *makeleaf(const char *key, size_t keylen, void *data, int datalen) { + struct CritBitNode *leaf = malloc(sizeof *leaf); + if (!leaf) + return 0; + + leaf->key = malloc(keylen + 1); + if (!leaf->key) { + free(leaf); + return 0; + } + + memcpy(leaf->key, key, keylen + 1); + leaf->value = data; + leaf->valuelen = datalen; + leaf->isleaf = 1; + + return leaf; +} + +int crit_bit_add(struct CritBitTree *tree, const char *key, void *data, int datalen) { + if (!tree) return 0; + + const uint8_t *keybytes = (void*)key; + size_t keylen = strlen(key); + struct CritBitNode *node = tree->root; + + if (!node) { + struct CritBitNode *root = malloc(sizeof *root); + root->key = malloc(keylen + 1); + strcpy(root->key, key); + root->valuelen = datalen; + root->value = data; + root->isleaf = 1; + tree->root = root; + tree->count++; + return 0; + } + + while (node && !node->isleaf) { + uint8_t check = 0; + if (node->byte < keylen) + check = keybytes[node->byte]; + if ((node->others | check) == 0xFF) + node = node->right; + else + node = node->left; + } + + if (!node) + return -1; + + uint32_t newbyte; + uint32_t newothers = ~0; /* sigil for finding existing keys */ + for (newbyte = 0; newbyte < keylen; ++newbyte) { + if (node->key[newbyte] != key[newbyte]) { + newothers = node->key[newbyte] ^ key[newbyte]; + break; + } + } + + if (newothers == ~0) { + if (node->key[newbyte] != 0) { + newothers = node->key[newbyte]; + } + } + + if (newothers == ~0) { + if (tree->free_data) + tree->free_data(node->value, node->valuelen); + node->value = data; + node->valuelen = datalen; + return 0; + } + + /* bit hacks */ + while (newothers & (newothers - 1)) + newothers &= newothers - 1; + newothers ^= 0xFF; + uint8_t c = node->key[newbyte]; + int isright = (1 + (newothers | c)) >> 8; + + struct CritBitNode *newnode = malloc(sizeof *newnode); + if (!newnode) + return -2; + + struct CritBitNode *leafnode = makeleaf(key, keylen, data, datalen); + if (!leafnode) + return -2; + + newnode->isleaf = 0; + newnode->byte = newbyte; + newnode->others = newothers; + + if (isright) { + newnode->left = leafnode; + } else { + newnode->right = leafnode; + } + + struct CritBitNode **parent = (struct CritBitNode **)&tree->root; + for (;;) { + struct CritBitNode *here = *parent; + if (here->isleaf) break; + if (here->byte > newbyte) break; + if (here->byte == newbyte && here->others > newothers) break; + + uint8_t c2 = 0; + if (here->byte < keylen) c2 = keybytes[here->byte]; + if ((1 + (here->others | c2)) > 0xFF) { + parent = &here->right; + } else { + parent = &here->left; + } + } + + if (isright) { + newnode->right = *parent; + } else { + newnode->left = *parent; + } + *parent = newnode; + tree->count++; + return 0; +} + +int crit_bit_has(const struct CritBitTree *tree, const char *key) { + if (!tree) return 0; + + const uint8_t *keybytes = (void*)key; + size_t keylen = strlen(key); + const struct CritBitNode *node = tree->root; + + while (node && !node->isleaf) { + uint8_t check = 0; + if (node->byte < keylen) + check = keybytes[node->byte]; + if ((node->others | check) == 0xFF) + node = node->right; + else + node = node->left; + } + + return node && !strcmp(key, node->key); +} + +void *crit_bit_get(const struct CritBitTree *tree, const char *key, int *len) { + if (!tree) return 0; + + const uint8_t *keybytes = (void*)key; + size_t keylen = strlen(key); + const struct CritBitNode *node = tree->root; + + while (node && !node->isleaf) { + uint8_t check = 0; + if (node->byte < keylen) + check = keybytes[node->byte]; + if ((node->others | check) == 0xFF) + node = node->right; + else + node = node->left; + } + + if (!node) return 0; + + if (len) + *len = node->valuelen; + return node->value; +} + +int crit_bit_del(struct CritBitTree *tree, const char *key) { + if (!tree) return 1; + + const uint8_t *keybytes = (void*)key; + size_t keylen = strlen(key); + struct CritBitNode *node = tree->root; + struct CritBitNode *prev = 0; + void *tmp = &(tree->root); + struct CritBitNode **parent = (struct CritBitNode **)tmp; + struct CritBitNode **grandparent = 0; + int isright; + + if (!node) return 1; + + while (node && !node->isleaf) { + grandparent = parent; + uint8_t c = 0; + if (node->byte < keylen) c = keybytes[node->byte]; + isright = ((1 + (node->others | c)) > 0xFF); + if (isright) { + parent = &node->right; + } else { + parent = &node->left; + } + prev = node; + node = *parent; + } + + if (!node) + return 1; + + if (strcmp(key, node->key)) + return 1; + + if (tree->free_data) tree->free_data(node->value, node->valuelen); + free(node->key); + free(node); + tree->count--; + + if (!grandparent || !prev) { + tree->root = 0; + return 0; + } + + if (isright) { + *grandparent = prev->left; + } else { + *grandparent = prev->right; + } + + free(prev); + return 0; +} + +int crit_bit_size(const struct CritBitTree *tree) { + return tree->count; +} + +static int iterate_node(int (*callback)(const char*,void*,int,void*), void *data, struct CritBitNode *node) { + if (!node) return 0; + if (node->isleaf) { + return callback(node->key, node->value, node->valuelen, data); + } else { + int r; + r = iterate_node(callback, data, node->left); + if (r) return r; + r = iterate_node(callback, data, node->right); + return r; + } +} + +int crit_bit_foreach(const struct CritBitTree *tree, int (*callback)(const char*,void*,int,void*), void *data) { + if (!tree) return 0; + if (!tree->root) return 0; + if (!callback) return 0; + + return iterate_node(callback, data, tree->root); +} diff --git a/src/critbit.h b/src/critbit.h new file mode 100644 index 0000000..df7905a --- /dev/null +++ b/src/critbit.h @@ -0,0 +1,19 @@ +#ifndef CRITBIT_H +#define CRITBIT_H + +struct CritBitTree { + void *root; + int count; + void (*free_data)(void *data, int datalen); +}; + +void crit_bit_init(struct CritBitTree *tree, void (*free_data)(void *data, int datalen)); /* or just { 0 } if you don't need a free_data */ +void crit_bit_free(struct CritBitTree *tree); /* a free tree is still initted and can be used */ +int crit_bit_add(struct CritBitTree *tree, const char *key, void *data, int datalen); +int crit_bit_has(const struct CritBitTree *tree, const char *key); +void *crit_bit_get(const struct CritBitTree *tree, const char *key, int *len); +int crit_bit_del(struct CritBitTree *tree, const char *key); +int crit_bit_size(const struct CritBitTree *tree); +int crit_bit_foreach(const struct CritBitTree *tree, int (*callback)(const char*,void*,int,void*), void *data); /* returns short-circuit OR of callback value */ + +#endif /* CRITBIT_H */ diff --git a/src/jit.c b/src/jit.c index e4f5ea0..f576a44 100644 --- a/src/jit.c +++ b/src/jit.c @@ -1,7 +1,4 @@ #include "jit.h" -#ifdef __x86_64__ -#include "jit/x86_64.c" -#else -#include "jit/interpret.c" -#endif /* architecture */ +// #include "arch/x86_64.c" +// #include "arch/interpret.c" diff --git a/src/jit.h b/src/jit.h index 496575c..2544925 100644 --- a/src/jit.h +++ b/src/jit.h @@ -1,7 +1,19 @@ #ifndef JIT_H #define JIT_H -/* the implementations must implement this function */ -int (*jit(const char *src))(int, char**); +#include "architectures.h" + +/* these are what jit functions return */ +typedef int (*main_func)(int, char**); + +/* the implementations must implement these functions */ +typedef int (*((*jit)(const char *,int)))(int, char**); + +jit get_implementation(enum Architecture arch); + +/* these are what the asm functions receive */ +typedef void (*str_func)(const char *, void *); + +void asm_implementation(enum Architecture arch, const char *source, int len, str_func cb, void *data); #endif /* JIT_H */ diff --git a/src/jit/x86_64.c b/src/jit/x86_64.c index c8da3f8..dbae659 100644 --- a/src/jit/x86_64.c +++ b/src/jit/x86_64.c @@ -21,7 +21,7 @@ const uint8_t prelude[] = { // MOV(RBP,RSP) }; -int (*jit(const char *src))(int, char**) { +int (*jit_x86_64(const char *src))(int, char**) { // TODO: see https://github.com/spencertipping/jit-tutorial } diff --git a/src/main.c b/src/main.c index 2a1ad29..982f81f 100644 --- a/src/main.c +++ b/src/main.c @@ -1,14 +1,120 @@ -#include +#include +#include + +#include +#include +#include #include #include "jit.h" #include "args.h" #include "architectures.h" -int main(int argc, char *argv[]) { - struct Arguments args; - int rc = parse_arguments(&args, argc, argv); +char *read_dynamic(int fd, int *len) { + char buf[0x1000]; + ssize_t n; + char *result = 0; + *len = 0; + while ((n = read(fd, buf, sizeof buf))) { + if (n < 0) { + if (errno == EAGAIN) + continue; + perror("read"); + break; + } - printf("%d\n", rc); - return 0; + char *nresult = realloc(result, *len + n + 1); + if (!nresult) { + free(result); + return 0; + } + result = nresult; + memcpy(result + *len, buf, n); + *len += n; + result[*len] = 0; + } + + return result; +} + +char *read_filename(const char *fname, int *len, int *mapped) { + if (fname[0] == '-' && fname[1] == 0) { + *mapped = 0; + return read_dynamic(0, len); + } else { + char *result = 0; + int fd = open(fname, O_RDONLY); + *len = lseek(fd, 0, SEEK_END); + if (*len < 0) { + /* fallback to dynamic memory */ + *len = 0; + *mapped = 0; + result = read_dynamic(fd, len); + } else { + *mapped = 1; + result = mmap(0, *len, PROT_READ, MAP_PRIVATE, fd, 0); + } + close(fd); + return result; + } +} + +void free_read_file(char *contents, int len, int mapped) { + if (!mapped) { + free(contents); + } else { + munmap(contents, len); + } +} + +void dump_str(const char *str, void *unused) { + puts(str); +} + +int main(int argc, char *argv[]) { + struct Arguments args = { 0 }; + int offset = parse_arguments(&args, argc, argv); + + int len; + int mapped; + char *source = read_filename(args.program, &len, &mapped); + int result; + + switch (args.behaviour) { + case BEHAVIOUR_INTERPRET: { + jit interp = get_implementation(ARCH_INTERPRET); + if (!interp) { + free_read_file(source, len, mapped); + return -1; + } + + main_func interp_main = interp(source, len); + free_read_file(source, len, mapped); + if (!interp_main) return -1; + + result = interp_main(argc - offset, argv + offset); + break; + } + + case BEHAVIOUR_JIT: { + jit jitter = get_implementation(args.jit.arch); + if (!jitter) { + free_read_file(source, len, mapped); + return -1; + } + + main_func jitter_main = jitter(source, len); + free_read_file(source, len, mapped); + result = jitter_main(argc - offset, argv + offset); + break; + } + + case BEHAVIOUR_ASM: + asm_implementation(args.assemble.arch, source, len, dump_str, 0); + free_read_file(source, len, mapped); + result = 0; + break; + } + + return result; } diff --git a/tests/test0.arx b/tests/test0.arx new file mode 100644 index 0000000..9a44f0d --- /dev/null +++ b/tests/test0.arx @@ -0,0 +1,6 @@ +#!/usr/bin/env arctic + +# Simple smoke test, ensures shebang, comments, and basic "code" work +"code" +$0 ; +