Compare commits

...

3 commits

Author SHA1 Message Date
Louis Burke f382f8d575 Added first real test. 2025-03-16 19:11:23 -04:00
Louis Burke 1c69cbf70e Finished initial cheatsheet 2025-02-17 02:00:37 -05:00
Louis Burke 9ebaa2982c Made m4 more readable 2024-10-27 20:31:16 -04:00
19 changed files with 1284 additions and 182 deletions

View file

@ -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

View file

@ -1,13 +1,17 @@
\documentclass{article}
\usepackage[letterpaper, landscape, margin=1cm]{geometry}
\usepackage[letterpaper, landscape, margin=0.7cm]{geometry}
\usepackage{tabularx}
\usepackage{tabulary}
\usepackage{booktabs}
\usepackage{tikz}
\usepackage{setspace}
\usepackage{listofitems}
\usetikzlibrary{shapes.multipart,positioning}
\renewcommand\tabularxcolumn[1]{m{#1}}
\usetikzlibrary{shapes.multipart,positioning,arrows.meta}
\tikzstyle{stack}=[rectangle split, rectangle split parts=#1,draw,anchor=center]
@ -16,101 +20,615 @@
\newcommand{\opsummaryshort}{#1}
\newcommand{\opsummarylong}{#2}
}{%
\draw[->, double distance=1pt] (before) -- node[above] {\texttt{\opsummaryshort}} node[below] {\texttt{\opsummarylong}} (after);
\draw[->] (before) -- node[above] {\texttt{\opsummaryshort}} node[text width=1cm,align=center,below] {\texttt{\tiny\begin{spacing}{0.6}\opsummarylong\end{spacing}}} (after);
\end{tikzpicture}
}
\newcommand{\stackdiagram}[2]{%
\readlist\leftelems{#1}
\readlist\rightelems{#2}
\node[stack=\leftelemslen] (before) {%
\ifnum\leftelemslen>0\nodepart{one}$\leftelems[1]$\fi
\ifnum\leftelemslen>1\nodepart{two}$\leftelems[2]$\fi
\ifnum\leftelemslen>2\nodepart{three}$\leftelems[3]$\fi
\ifnum\leftelemslen>3\nodepart{four}$\leftelems[4]$\fi
\ifnum\leftelemslen>4\nodepart{five}$\leftelems[5]$\fi
\ifnum\leftelemslen>5\nodepart{six}$\leftelems[6]$\fi
};
\node[stack=\rightelemslen,right=of before] (after) {%
\ifnum\rightelemslen>0\nodepart{one}$\rightelems[1]$\fi
\ifnum\rightelemslen>1\nodepart{two}$\rightelems[2]$\fi
\ifnum\rightelemslen>2\nodepart{three}$\rightelems[3]$\fi
\ifnum\rightelemslen>3\nodepart{four}$\rightelems[4]$\fi
\ifnum\rightelemslen>4\nodepart{five}$\rightelems[5]$\fi
\ifnum\rightelemslen>5\nodepart{six}$\rightelems[6]$\fi
};
}
\newif\ifnoopdef
\newif\ifopisfun
\newif\ifopisimm
\newif\ifopisgud
\newif\ifopisbad
\newcommand{\optextdescr}{}
\newcommand{\optext}[1]{\renewcommand{\optextdescr}{#1}\noopdeffalse}
\newcommand{\opsumof}[1]{%
\begin{tikzpicture}
\noopdeftrue
\opisfunfalse
\opisimmfalse
\opisgudfalse
\opisbadfalse
% TODO: document and add more metasyntactic variables/functions?
% So far uncategorized instructions
\if d#1
\optext{set x bytes of memory to immediate value at p}
\opisbadtrue
\stackdiagram{x,p,\ldots}{\ldots}
\fi
\if f#1
\optext{invoke f, saving return address on stack}
\opisfuntrue
\stackdiagram{f,\ldots}{g,\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
\optext{rotate x right by immediate bits}
\opisimmtrue
\stackdiagram{x,\ldots}{x,\ldots}
\fi
\if ]#1
\optext{arithmetic right shift x by immediate bits}
\opisimmtrue
\stackdiagram{x,\ldots}{x,\ldots}
\fi
\if u#1
\optext{logical shift x by immediate bits right}
\opisimmtrue
\stackdiagram{x,\ldots}{x,\ldots}
\fi
% Float operations
\if j#1
\optext{negate a}
\opisgudtrue
\stackdiagram{a,\ldots}{-a,\ldots}
\fi
\if m#1
\optext{floating point difference}
\opisgudtrue
\stackdiagram{b,a,\ldots}{a-b}
\fi
\if p#1
\optext{floating point multiply}
\opisgudtrue
\stackdiagram{b,a,\ldots}{a\times{}b,\ldots}
\fi
\if q#1
\optext{floating point divide}
\opisgudtrue
\stackdiagram{b,a,\ldots}{b\over{}a,\ldots}
\fi
\if s#1
\optext{floating point add}
\opisgudtrue
\stackdiagram{b,a,\ldots}{b+a,\ldots}
\fi
\if v#1
\optext{floating point absolute value}
\opisgudtrue
\stackdiagram{a,\ldots}{|a|,\ldots}
\fi
% Conversion instructions
\if .#1
\optext{convert x to a float}
\opisgudtrue
\stackdiagram{x,\ldots}{a,\ldots}
\fi
\if E#1
\optext{round a to integer and store in x}
\opisgudtrue
\stackdiagram{a,\ldots}{x,\ldots}
\fi
\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
\optext{load int at p plus immediate offset in x}
\stackdiagram{p,\ldots}{x,\ldots}
\opisimmtrue
\fi
\if H#1
\optext{load short at p plus immediate offset in x}
\stackdiagram{p,\ldots}{x,\ldots}
\opisimmtrue
\fi
\if o#1
\optext{load byte at p plus immediate offset in x}
\stackdiagram{p,\ldots}{x,\ldots}
\opisimmtrue
\fi
% 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
\ifnoopdef
\tikzset{every node/.style={fill=gray}}
\renewcommand{\optextdescr}{?}
\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}}
\else
\tikzset{->/.style={arrows={-Latex[length=5pt]}, double distance=1pt}}
\fi
\else
\ifopisimm
\tikzset{->/.style={arrows={Arc Barb[reversed,length=3pt]-Latex[length=5pt]}}}
\else
\tikzset{->/.style={arrows={-Latex[length=5pt]}}}
\fi
\fi
\draw[->]
(before) --
node[above] {\texttt{#1}}
node[text width=1cm,align=center,below]
{\texttt{\tiny\begin{spacing}{0.6}\optextdescr\end{spacing}}}
(after);
\end{tikzpicture}
}
\begin{document}
\begin{tabulary}{\textwidth}{@{}cccccccccccccccc@{}} \toprule
\begin{opsummary}{0}{BNEZ}
\node[stack=2] (before) {%
\nodepart{one}$x$
\nodepart{two}\ldots
};
\node[stack=1,right=of before] (after) {%
\nodepart{one}\ldots
};
\end{opsummary}
&
\begin{table}[h]
\renewcommand{\arraystretch}{0}
\centering
%\caption{All Code Section Instructions In ASCII Order}
\begin{tabularx}{\textwidth}{@{}XXXXXXXXXXXXXXXX@{}} %\toprule
\opsumof{0} & \opsumof{1} & \opsumof{2} & \opsumof{3} &
\opsumof{4} & \opsumof{5} & \opsumof{6} & \opsumof{7} \\
\begin{opsummary}{1}{LDAB}
\node[stack=2] (before) {%
\nodepart{one}$p$
\nodepart{two}
};
\node[stack=2,right=of before] (after) {%
\nodepart{one}$x$
\nodepart{two}
};
\end{opsummary}
&
\opsumof{8} & \opsumof{9} & \opsumof{A} & \opsumof{B} &
\opsumof{C} & \opsumof{D} & \opsumof{E} & \opsumof{F} \\
\begin{opsummary}{2}{LDAS}
\node[stack=2] (before) {%
\nodepart{one}$p$
\nodepart{two}
};
\node[stack=2,right=of before] (after) {%
\nodepart{one}$x$
\nodepart{two}
};
\end{opsummary}
&
\opsumof{G} & \opsumof{H} & \opsumof{I} & \opsumof{J} &
\opsumof{K} & \opsumof{L} & \opsumof{M} & \opsumof{N} \\
\begin{opsummary}{3}{LDAI}
\node[stack=2] (before) {%
\nodepart{one}$p$
\nodepart{two}
};
\node[stack=2,right=of before] (after) {%
\nodepart{one}$x$
\nodepart{two}
};
\end{opsummary}
&
\opsumof{O} & \opsumof{P} & \opsumof{Q} & \opsumof{R} &
\opsumof{S} & \opsumof{T} & \opsumof{U} & \opsumof{V} \\
\begin{opsummary}{4}{LDAW}
\node[stack=2] (before) {%
\nodepart{one}$p$
\nodepart{two}
};
\node[stack=2,right=of before] (after) {%
\nodepart{one}$x$
\nodepart{two}
};
\end{opsummary}
&
\opsumof{W} & \opsumof{X} & \opsumof{Y} & \opsumof{Z} &
\opsumof{a} & \opsumof{b} & \opsumof{c} & \opsumof{d} \\
5 &
6 &
7 &
8 &
9 &
A &
B &
C &
D &
E &
F \\
\opsumof{e} & \opsumof{f} & \opsumof{g} & \opsumof{h} &
\opsumof{i} & \opsumof{j} & \opsumof{k} & \opsumof{l} \\
G &
H &
I &
J &
K &
L &
M &
N &
O &
P &
Q &
R &
S &
T &
U &
V \\
\opsumof{m} & \opsumof{n} & \opsumof{o} & \opsumof{p} &
\opsumof{q} & \opsumof{r} & \opsumof{s} & \opsumof{t} \\
\end{tabulary}
\opsumof{u} & \opsumof{v} & \opsumof{w} & \opsumof{x} &
\opsumof{y} & \opsumof{z} & \opsumof{\_} & \opsumof{.} \\
\opsumof{+} & \opsumof{-} & \opsumof{*} & \opsumof{/} &
\opsumof{\%} & \opsumof{\textbackslash} & \opsumof{|} & \opsumof{\$} \\
\opsumof{@} & \opsumof{<} & \opsumof{\{} & \opsumof{=} &
\opsumof{\}} & \opsumof{>} & \opsumof{,} & \opsumof{;} \\
\opsumof{\&} & \opsumof{\textasciicircum} & \opsumof{!} & \opsumof{[} &
\opsumof{]} & \opsumof{?} & \opsumof{\textasciitilde} & \opsumof{:} \\
\opsumof{(} & \opsumof{)} & \opsumof{'} & \opsumof{"} &
\opsumof{`} & \opsumof{\textvisiblespace} & \opsumof{\#} & \opsumof{\P} \\
\end{tabularx}
\end{table}
\end{document}

View file

@ -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

View file

@ -1,50 +1,51 @@
divert(`-1')dnl
#changequote(`[', `]')dnl
changequote(`«', `»')
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
\ allows complex numerical expressions in TO and FROM
define(«forloop», «ifelse(eval(«($2) <= ($3)»), «1»,
«pushdef(«$1»)_$0(«$1», eval(«$2»),
eval(«$3»), «$4»)popdef(«$1»)»)»)
define(«_forloop»,
«define(«$1», «$2»)$4«»ifelse(«$2», «$3», «»,
«$0(«$1», incr(«$2»), «$3», «$4»)»)»)
# 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
# allows complex numerical expressions in TO and FROM
define(`forloop', `ifelse(eval(`($2) <= ($3)'), `1',
`pushdef(`$1')_$0(`$1', eval(`$2'),
eval(`$3'), `$4')popdef(`$1')')')
define(`_forloop',
`define(`$1', `$2')$4`'ifelse(`$2', `$3', `',
`$0(`$1', incr(`$2'), `$3', `$4')')')
\ foreachq(x, «item_1, item_2, ..., item_n», stmt)
\ quoted list, version based on forloop
define(«foreachq»,
«ifelse(«$2», «», «», «_$0(«$1», «$3», $2)»)»)
define(«_foreachq»,
«pushdef(«$1», forloop(«$1», «3», «$#»,
«$0_(«1», «2», indir(«$1»))»)«popdef(
«$1»)»)indir(«$1», $@)»)
define(«_foreachq_»,
««define(«$$1», «$$3»)$$2«»»»)
# foreachq(x, `item_1, item_2, ..., item_n', stmt)
# quoted list, version based on forloop
define(`foreachq',
`ifelse(`$2', `', `', `_$0(`$1', `$3', $2)')')
define(`_foreachq',
`pushdef(`$1', forloop(`$1', `3', `$#',
`$0_(`1', `2', indir(`$1'))')`popdef(
`$1')')indir(`$1', $@)')
define(`_foreachq_',
``define(`$$1', `$$3')$$2`''')
\ foreach(x, (item_1, item_2, ..., item_n), stmt)
\ parenthesized list, improved version
define(«foreach», «pushdef(«$1»)_$0(«$1»,
(dquote(dquote_elt$2)), «$3»)popdef(«$1»)»)
define(«_arg1», «$1»)
define(«_foreach», «ifelse(«$2», «(«»)», «»,
«define(«$1», _arg1$2)$3«»$0(«$1», (dquote(shift$2)), «$3»)»)»)
# foreach(x, (item_1, item_2, ..., item_n), stmt)
# parenthesized list, improved version
define(`foreach', `pushdef(`$1')_$0(`$1',
(dquote(dquote_elt$2)), `$3')popdef(`$1')')
define(`_arg1', `$1')
define(`_foreach', `ifelse(`$2', `(`')', `',
`define(`$1', _arg1$2)$3`'$0(`$1', (dquote(shift$2)), `$3')')')
\ hextox(x)
\ hexadecimal number to series of X(0xZZ)X(0xZZ)...
define(«hextox», «patsubst(«$1», «..», «X(0x\&)»)»)
# hextox(x)
# hexadecimal number to series of X(0xZZ)X(0xZZ)...
define(`hextox', `patsubst(`$1', `..', `X(0x\&)')')
\ hashtag(x)
\ expands to #x
define(«hashtag», «changecom()dnl
#$1«»dnl
changecom(«#»)»)
# hashtag(x)
# expands to #x
define(`hashtag', `changecom()dnl
#$1`'dnl
changecom(`#')')
\ rtrimn(x, n)
\ expands to all but the last n characters of x
define(«rtrimn», «substr($1, 0, eval(len($1)-$2))»)
# rtrimn(x, n)
# expands to all but the last n characters of x
define(`rtrimn', `substr($1, 0, eval(len($1)-$2))')
divert(`0')dnl
divert(«0»)dnl

9
samples/hello.arx Normal file
View file

@ -0,0 +1,9 @@
#!/usr/bin/env arctic
"data"
:message sHello\ World!\x0A
"code"
@main
:main $15 $message $1 `1 $0 ;

View file

@ -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

View file

@ -1,19 +1,19 @@
include(`misc/utils.m4')dnl vim-m4-syntax: quote=«,» comment=\\
#ifndef X86_64_H
#define X86_64_H
#include "../utils.h"
/*******************************************************************************
include(`misc/utils.m4')dnl
divert(`-1')dnl
divert(«-1»)dnl
define(`registers', `RAX, RCX, RDX, RBX, R8, R9, R10, R11, R12, R13, R14, R15')
define(«registers», «RAX, RCX, RDX, RBX, R8, R9, R10, R11, R12, R13, R14, R15»)
define(`x86_encoding_of', `translit(esyscmd(`echo 'TICK`$1'TICK` | ./tools/x86_64dump'),`
')')
define(`assemble', `hextox(x86_encoding_of($1)) Y("$1")')
define(«x86_encoding_of», «translit(esyscmd(«echo '$1' | ./tools/x86_64dump»),«
»)»)
define(«assemble», «hextox(x86_encoding_of($1)) Y("$1")»)
divert(`0')dnl
divert(«0»)dnl
Types:
@ -40,29 +40,29 @@ Implementations (auto generated):
#define XX(x) X(BYTE_0xFF(x)) X(BYTE_0xFF00(x))
#define XXX(x) X(BYTE_0xFF(x)) X(BYTE_0xFF00(x)) X(BYTE_0xFF0000(x))
#define XXXX(x) X(BYTE_0xFF(x)) X(BYTE_0xFF00(x)) X(BYTE_0xFF0000(x)) X(BYTE_0xFF000000(x))
foreachq(register, `registers', `hashtag(define) PUSH_`'register`' assemble(`push register')
')
foreachq(register, «registers», «#define PUSH_«»register«» assemble(«push register»)
»)
#define PUSH_BYTE(x) X(0x6a) X(x) Y("push " #x)
#define PUSH_WORD(x) X(0x86) XXXX(x) Y("push " #x)
foreachq(register, `registers', `hashtag(define) POP_`'register`' assemble(`pop register')
')
foreachq(registera, `registers', `
foreachq(registerb, `registers', `ifelse(registera,registerb,,hashtag(define) MOV_`'registera`'_`'registerb`' assemble(`mov registera`,'registerb')
)')')
foreachq(register, `registers', `hashtag(define) MOV_`'register`'_WORD(x) hextox(rtrimn(x86_encoding_of(`mov 'register`, 0'), 8)) XXXX(x) Y("mov register," hashtag(x))
')
foreachq(register, «registers», «#define POP_«»register«» assemble(«pop register»)
»)
foreachq(registera, «registers», «
foreachq(registerb, «registers», «ifelse(registera,registerb,,#define MOV_«»registera«»_«»registerb«» assemble(«mov registera«,»registerb»)
)»)»)
foreachq(register, «registers», «#define MOV_«»register«»_WORD(x) hextox(rtrimn(x86_encoding_of(«mov »register«, 0»), 8)) XXXX(x) Y("mov register," #x)
»)
foreachq(registera, `registers', `
foreachq(registerb, `registers', `ifelse(registera,registerb,,hashtag(define) ADD_`'registera`'_`'registerb`' assemble(`add registera`,'registerb')
)')')
foreachq(register, `registers', `hashtag(define) ADD_`'register`'_WORD(x) hextox(rtrimn(x86_encoding_of(`add 'register`, 0x12345678'), 8)) XXXX(x) Y("add register," hashtag(x))
')
foreachq(registera, «registers», «
foreachq(registerb, «registers», «ifelse(registera,registerb,,#define ADD_«»registera«»_«»registerb«» assemble(«add registera«,»registerb»)
)»)»)
foreachq(register, «registers», «#define ADD_«»register«»_WORD(x) hextox(rtrimn(x86_encoding_of(«add »register«, 0x12345678»), 8)) XXXX(x) Y("add register," #x)
»)
foreachq(registera, `registers', `
foreachq(registerb, `registers', `ifelse(registera,registerb,,hashtag(define) SUB_`'registera`'_`'registerb`' assemble(`sub registera`,'registerb')
)')')
foreachq(register, `registers', `hashtag(define) SUB_`'register`'_WORD(x) hextox(rtrimn(x86_encoding_of(`sub 'register`, 0x12345678'), 8)) XXXX(x) Y("sub register," hashtag(x))
')
foreachq(registera, «registers», «
foreachq(registerb, «registers», «ifelse(registera,registerb,,#define SUB_«»registera«»_«»registerb«» assemble(«sub registera«,»registerb»)
)»)»)
foreachq(register, «registers», «#define SUB_«»register«»_WORD(x) hextox(rtrimn(x86_encoding_of(«sub »register«, 0x12345678»), 8)) XXXX(x) Y("sub register," #x)
»)
#endif /* X86_64_H */

View file

@ -1,3 +1,37 @@
#include <string.h>
#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
}

View file

@ -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 */

53
src/arctic.h Normal file
View file

@ -0,0 +1,53 @@
#ifndef ARCTIC_H
#define ARCTIC_H
#include <stdint.h>
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 */

View file

@ -1,10 +1,29 @@
#include "args.h"
#include <argp.h>
#include <string.h>
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;
}

View file

@ -3,7 +3,7 @@
#include "architectures.h"
#include <stdio.h>
#include <linux/limits.h>
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 {

301
src/critbit.c Normal file
View file

@ -0,0 +1,301 @@
#include "critbit.h"
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
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);
}

19
src/critbit.h Normal file
View file

@ -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 */

View file

@ -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"

View file

@ -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 */

View file

@ -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
}

View file

@ -1,14 +1,120 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <argp.h>
#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;
}

6
tests/test0.arx Normal file
View file

@ -0,0 +1,6 @@
#!/usr/bin/env arctic
# Simple smoke test, ensures shebang, comments, and basic "code" work
"code"
$0 ;