spl-1.0pre6/0000755000000000000000000000000011300476114011440 5ustar rootrootspl-1.0pre6/GNUmakefile.deps0000644000000000000000000000637111064462007014456 0ustar rootrootasm.o: asm.c spl.h builtins.o: builtins.c spl.h compat.h clib.o: clib.c spl.h compat.h compiler.o: compiler.c spl.h compat.h compiler.o: compiler.y spl.h compat.h debug.o: debug.c spl.h compat.h dump.o: dump.c spl.h examples/c-api-test1.o: examples/c-api-test1.c spl.h examples/c-api-test2.o: examples/c-api-test2.c spl.h examples/c-api-test3.o: examples/c-api-test3.c spl.h examples/c-api-test4.o: examples/c-api-test4.c spl.h exec.o: exec.c spl.h compat.h gc.o: gc.c spl.h hnode.o: hnode.c spl.h compat.h httpsrv.o: httpsrv.c spl.h webspl_common.h compat.h optimizer.o: optimizer.c spl.h compat.h rccheck.o: rccheck.c spl.h report.o: report.c spl.h compat.h restore.o: restore.c spl.h sha1.o: sha1.c spl.h spl_modules/mod_array.o: spl_modules/mod_array.c spl.h compat.h spl_modules/mod_array.splh spl_modules/mod_bits.o: spl_modules/mod_bits.c spl.h compat.h spl_modules/mod_cgi.o: spl_modules/mod_cgi.c spl.h compat.h webspl_common.h spl_modules/mod_crypt.o: spl_modules/mod_crypt.c spl.h compat.h spl_modules/mod_curl.o: spl_modules/mod_curl.c spl.h compat.h spl_modules/mod_encode_xml.o: spl_modules/mod_encode_xml.c spl.h compat.h spl_modules/mod_environ.o: spl_modules/mod_environ.c spl.h compat.h spl_modules/mod_epfb.o: spl_modules/mod_epfb.c spl.h compat.h spl_modules/mod_fann.o: spl_modules/mod_fann.c spl.h compat.h spl_modules/mod_file.o: spl_modules/mod_file.c spl.h compat.h spl_modules/mod_format_xml.o: spl_modules/mod_format_xml.c spl.h compat.h spl_modules/mod_gl.o: spl_modules/mod_gl.c spl.h compat.h spl_modules/mod_gyro.o: spl_modules/mod_gyro.c spl.h compat.h spl_modules/mod_kde.o: spl_modules/mod_kde.c spl.h compat.h spl_modules/mod_multimouse.o: spl_modules/mod_multimouse.c spl.h compat.h spl_modules/mod_prime.o: spl_modules/mod_prime.c spl.h compat.h spl_modules/mod_qt.o: spl_modules/mod_qt.c spl.h spl_modules/moc_mod_qt.c spl_modules/mod_sdl.o: spl_modules/mod_sdl.c spl.h compat.h spl_modules/mod_socket.o: spl_modules/mod_socket.c spl.h compat.h spl_modules/mod_sql.o: spl_modules/mod_sql.c spl.h compat.h spl_modules/mod_sql.h spl_modules/mod_sql_mysql.o: spl_modules/mod_sql_mysql.c spl.h spl_modules/mod_sql.h compat.h spl_modules/mod_sql_postgres.o: spl_modules/mod_sql_postgres.c spl.h spl_modules/mod_sql.h compat.h spl_modules/mod_sql_sqlite.o: spl_modules/mod_sql_sqlite.c spl.h compat.h spl_modules/mod_sql.h spl_modules/mod_system.o: spl_modules/mod_system.c spl.h compat.h spl_modules/mod_task.o: spl_modules/mod_task.c spl.h compat.h spl_modules/mod_task.splh spl_modules/mod_termio.o: spl_modules/mod_termio.c spl.h compat.h spl_modules/mod_time.o: spl_modules/mod_time.c spl.h compat.h spl_modules/mod_uuidgen.o: spl_modules/mod_uuidgen.c spl.h compat.h spl_modules/mod_webdebug.o: spl_modules/mod_webdebug.c spl.h compat.h spl_modules/mod_webdebug.splh spl_modules/mod_wscons.o: spl_modules/mod_wscons.c spl.h compat.h spl_modules/mod_xml.o: spl_modules/mod_xml.c spl.h compat.h spl_op.o: spl_op.c spl.h splrun.o: splrun.c spl.h compat.h state.o: state.c spl.h compat.h string.o: string.c spl.h compat.h treedump.o: treedump.c spl.h compat.h utf8.o: utf8.c spl.h utf8tab.h util.o: util.c spl.h compat.h webspl.o: webspl.c spl.h webspl_common.h compat.h webspl_common.o: webspl_common.c spl.h webspl_common.h compat.h webspld.o: webspld.c spl.h webspl_common.h compat.h spl-1.0pre6/make-deps.sh0000644000000000000000000000170410562034342013646 0ustar rootroot#!/bin/sh echo "** $* -MM -MG -xc" >&2 ( eval "$* -DMAKEDEPS -MM -MG -xc compiler.y *.c examples/*.c spl_modules/*.c" | sed "s|^mod_|spl_modules/mod_|; s|^c-api-test|examples/&|; s| $(pwd -P)/| |g;" | tr '\n' '|' | sed 's,\\| *,,g' | tr '|' '\n' | grep -v '^#' | egrep -v '^(moddir|moc_mod_qt)\.o:' | sed 's| \./| |g' | sed 's| /[^ ]*||g'; ) | while read trg srclist; do echo -n "$trg" for src in $srclist; do case "$src" in spl_op.h|*.splh|spl_modules/*) echo -n " $src" ;; *) [ -f "$src" ] && echo -n " $src" ;; esac done echo done > GNUmakefile.deps.new echo 'spl_modules/mod_fann.o: spl_modules/mod_fann.c spl.h compat.h' >> GNUmakefile.deps.new echo 'spl_modules/mod_gl.o: spl_modules/mod_gl.c spl.h compat.h' >> GNUmakefile.deps.new echo 'spl_op.o: spl_op.c spl.h' >> GNUmakefile.deps.new if test -s GNUmakefile.deps.new; then LC_ALL="C" sort -u GNUmakefile.deps.new > GNUmakefile.deps fi rm -f GNUmakefile.deps.new spl-1.0pre6/txt2tex.spl0000644000000000000000000000457110437047443013622 0ustar rootroot load "file"; function encode_tex(t) { t =~ s/\\/\\textbackslash{}/g; t =~ s/~/\\textasciitilde{}/g; t =~ s/\^/\\textasciicircum{}/g; t =~ s/(?]|--+)/\$$1\$/g; return t; } if (not declared parameters or not declared parameters.infile) { write("Need parameter 'infile' for input file!\n"); exit; } if (not declared parameters or not declared parameters.outfile) { write("Need parameter 'outfile' for output file!\n"); exit; } if (not declared parameters or not declared parameters.title) { write("Need parameter 'title' for document title!\n"); exit; } if (not declared parameters or not declared parameters.author) { write("Need parameter 'author' for document author!\n"); exit; } var input_file = parameters.infile; var output_file = parameters.outfile; var title = parameters.title; var author = parameters.author; var text; while (input_file =~ s/([^,]+)//) text ~= file_read($1) ~ "\n\n"; var latex = <> \documentclass[a4paper]{report} \begin{document} \title{${tex::title}} \author{${tex::author}} \maketitle \tableofcontents ; var blank_lines = 0; while (text =~ s/^(.*)\n//) { var line = $1; if (text =~ /^=+\s*\n/) { latex ~= "\\chapter{${tex::line}}\n"; text =~ s/^.*\n//; blank_lines = 0; } else if (text =~ /^-+\s*\n/) { latex ~= "\\section{${tex::line}}\n"; text =~ s/^.*\n//; blank_lines = 0; } else if (text =~ /^~+\s*\n/) { latex ~= "\\subsection{${tex::line}}\n"; text =~ s/^.*\n//; blank_lines = 0; } else if (text =~ /^\.+\s*\n/) { latex ~= "\\subsubsection{${tex::line}}\n"; text =~ s/^.*\n//; blank_lines = 0; } else if (line =~ /^\s/) { latex ~= "\\begin{verbatim}\n"; latex ~= "$line\n"; if (line =~ ,^\s*/\*\s+--\s+(\S+)\s+--\s+\*/\s*$,) { var prog = file_read($1); prog =~ s,^/\*.*?\*/\s*,\n,s; prog =~ s,\n,\n\t,g; prog =~ s,\s+$,\n,; latex ~= prog; } while (text =~ /^\n*[\t ]/) { text =~ s/^(.*\n)//; latex ~= $1; } latex ~= "\\end{verbatim}\n"; blank_lines = 0; } else { if ( line =~ /\S/ ) { if (blank_lines >= 2) latex ~= "\\vspace*{.3cm}\n"; blank_lines = 0; } else blank_lines++; latex ~= "${tex::line}\n"; } } latex ~= <> \end{document} ; latex =~ s/\t/ /g; file_write(output_file, latex); spl-1.0pre6/compiler.y0000644000000000000000000026444611206166551013472 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * compiler.y: Compiler for the high-level language * * +----------------+ * | \|/ ____ \|/ | WARNING: In the epilog of this bison parser you will * | "@'/ ,. \`@" | find a hand-written lexer which does a lot of nasty * | /_| \__/ |_\ | things. Reading it may cause collywobbles and changing * | \__U_/ | it might break the whole compiler. * | | | | You have been warned!! * +----------------+ */ %{ #include #include #include #include #include #include #ifdef ENABLE_PTHREAD_SUPPORT #include #endif #include "spl.h" #define COMPAT_H_NO_WIN_INCL #include "compat.h" static int spl_yylex (void); static void spl_yyerror (char const *); static struct spl_asm *as; static int gen_debug_info; static int no_checkp_insn; static void create_debug_op(int force); static int rvtolv_counter; static int import_asm_label_counter; static int label_stack[1024]; static int label_stack_index; static int label_stack_counter; static void lbstack_push() { label_stack[++label_stack_index] = ++label_stack_counter; } static void lbstack_pop() { label_stack_index--; } static int lbstack_value() { return label_stack[label_stack_index]; } static int breakcont_stack[1024]; static int breakcont_stack_index; static int breakcont_stack_counter; static void breakcont_begin() { breakcont_stack[++breakcont_stack_index] = ++breakcont_stack_counter; } static void breakcont_end() { breakcont_stack_index--; } static int breakcont_value() { return breakcont_stack[breakcont_stack_index]; } static int breakcont_label(char type) { char label[100]; snprintf(label, sizeof(label), "%c%d:%d", type, lbstack_value(), breakcont_value()); if ( spl_asm_setlabel(as, label, spl_asm_add(as, SPL_OP_NOP, 0)) < 0 ) { spl_yyerror("Assembler error"); return 1; } return 0; } static void breakcont_goto(char type) { char label[100]; snprintf(label, sizeof(label), "%c%d:%d", type, lbstack_value(), breakcont_value()); spl_asm_reflabel(as, label, spl_asm_add(as, SPL_OP_GOTO, 0)); } static int packpatch_stack[1024]; static int packpatch_stack_index; static int pbstack_push(int v) { return packpatch_stack[packpatch_stack_index++] = v; } static int pbstack_pop() { return packpatch_stack[--packpatch_stack_index]; } static int php_like_tags_active; static int php_like_tags_indenting_delim; static char *php_like_tags_term; #ifdef ENABLE_PTHREAD_SUPPORT static pthread_mutex_t compiler_lck = PTHREAD_MUTEX_INITIALIZER; #endif #define CHECKPOINT() \ do { if (!no_checkp_insn) spl_asm_add(as, SPL_OP_CHECKP, 0); } while (0) #define REGEX_EVAL_SUBST 1 #define REGEX_EVAL_RETURN 2 #define REGEX_EVAL_NEG 4 %} /* there are 3 shift/reduce conflicts in this grammar. search * * for "shift/reduce conflict" in the sources below for details. */ %expect 3 %name-prefix="spl_yy" %token-table %debug %union { char *text; char ch; } %token ID FUNC_ID VALUE SPECIALREF %token NOTOKEN %token TRANSLATE_PREFIX %token TRANSLATE_SEPERATOR %token TRANSLATE_END %token STRING_EOL %token STRING_EOL_S %token STRING_LABEL %token STRING_LABEL_S %token DBLCOMMA %token DEBUG %token WARNING %token ERROR %token DELETE %token FUNCTION %token METHOD %token IMPORT %token LOAD %token OBJECT %token VAR %token STATIC %token NEW %token THIS %token IF %token ELSE %token DO %token WHILE %token FOR %token FOREACH %token ASM %token RETURN %token EXIT %token DECLARED %token UNDEF %token GOTO %token BREAK %token CONTINUE %token ARRAYREF %token TRY %token CATCH %token THROW %token SWITCH %token DEFAULT %token CASE %left '.' %token EF_BEGIN EF_END BLK_BEGIN_NOCTX %token INSERT_PROG_BEGIN INSERT_PROG_END %token RVALUE_CONTEXT_BEGIN RVALUE_CONTEXT_END %right '=' SETCOPY ADDEQ SUBEQ MULEQ DIVEQ MODEQ POWEQ IADDEQ ISUBEQ IMULEQ IDIVEQ IMODEQ IPOWEQ FADDEQ FSUBEQ FMULEQ FDIVEQ FMODEQ FPOWEQ OADDEQ OSUBEQ OMULEQ ODIVEQ OMODEQ OPOWEQ SAPPEND XCHG %left LOR %left LAND %nonassoc REGEX NREGEX REGEX_SUBST NREGEX_SUBST REGEX_SUBST_R NREGEX_SUBST_R REGEX_EVAL NREGEX_EVAL REGEX_EVAL_R NREGEX_EVAL_R REGEX_SEP %nonassoc PEQ PNE EQ NE LT GT LE GE IEQ INE ILT IGT ILE IGE FEQ FNE FLT FGT FLE FGE OEQ ONE OLT OGT OLE OGE SEQ SNE SLT SGT SLE SGE %right POP SHIFT PUSH UNSHIFT NEXT PREV EVAL LENGTHOF ELEMENTSOF %left SUB ADD ISUB IADD FSUB FADD OSUB OADD %left MUL DIV MOD IMUL IDIV IMOD FMUL FDIV FMOD OMUL ODIV OMOD %left POW IPOW FPOW OPOW %left LNOT %left INC DEC %left NEG %right DEFINED %right ENC %left CAT %type function_method %type foreach_type %type regex_eval_mode %% prog: | prog { create_debug_op(0); } cmd ; cmd: '{' { spl_asm_add(as, SPL_OP_BEGIN, 0); } prog '}' { spl_asm_add(as, SPL_OP_END, 0); } | complex_expr ';' { spl_asm_add(as, SPL_OP_DROP, 0); CHECKPOINT(); } | lvalue XCHG lvalue ';' { spl_asm_add(as, SPL_OP_LXCHG, 0); } | ';' { CHECKPOINT(); } | BLK_BEGIN_NOCTX prog ']' '}' | ID ':' { char *label; my_asprintf(&label, "L%d:%s", lbstack_value(), $1); if ( spl_asm_setlabel(as, label, spl_asm_add(as, SPL_OP_NOP, 0)) < 0 ) { spl_yyerror("Assembler error"); free(label); YYABORT; } free(label); free($1); } cmd | GOTO ID ';' { char *label; my_asprintf(&label, "L%d:%s", lbstack_value(), $2); spl_asm_reflabel(as, label, spl_asm_add(as, SPL_OP_GOTO, 0)); free(label); free($2); } | BREAK { breakcont_goto('B'); } | CONTINUE { breakcont_goto('C'); } | VAR var_decl ';' { CHECKPOINT(); } | STATIC static_decl ';' { CHECKPOINT(); } | IF '(' complex_expr ')' { spl_asm_add(as, SPL_OP_UNLESS, 0); pbstack_push(spl_asm_add(as, SPL_OP_JUMP, 0)); } cmd else_branch { CHECKPOINT(); } | WHILE { pbstack_push(spl_asm_add(as, SPL_OP_NOP, 0)); breakcont_begin(); if (breakcont_label('C')) YYABORT; } '(' complex_expr ')' { spl_asm_add(as, SPL_OP_UNLESS, 0); pbstack_push(spl_asm_add(as, SPL_OP_JUMP, 0)); } cmd { int label_end = pbstack_pop(); int label_begin = pbstack_pop(); spl_asm_setaddr(as, spl_asm_add(as, SPL_OP_JUMP, 0), label_begin); spl_asm_setaddr(as, label_end, spl_asm_add(as, SPL_OP_NOP, 0)); if (breakcont_label('B')) YYABORT; breakcont_end(); } { CHECKPOINT(); } | DO { pbstack_push(spl_asm_add(as, SPL_OP_NOP, 0)); breakcont_begin(); } cmd { if (breakcont_label('C')) YYABORT; } WHILE '(' complex_expr ')' ';' { int label_begin = pbstack_pop(); spl_asm_add(as, SPL_OP_IF, 0); spl_asm_setaddr(as, spl_asm_add(as, SPL_OP_JUMP, 0), label_begin); if (breakcont_label('B')) YYABORT; breakcont_end(); } { CHECKPOINT(); } | FOR '(' { breakcont_begin(); spl_asm_add(as, SPL_OP_BEGIN, 0); } for_cmds ';' { pbstack_push(spl_asm_add(as, SPL_OP_NOP, 0)); } complex_expr ';' { spl_asm_add(as, SPL_OP_UNLESS, 0); pbstack_push(spl_asm_add(as, SPL_OP_JUMP, 0)); } { pbstack_push(spl_asm_add(as, SPL_OP_NOP, 0)); } for_cmds ')' { pbstack_push(spl_asm_add(as, SPL_OP_NOP, 0)); } cmd { if (breakcont_label('C')) YYABORT; int label_body_start = pbstack_pop(); int label_extra_start = pbstack_pop(); int label_finish_jump = pbstack_pop(); int label_cond_start = pbstack_pop(); int label_body_end = spl_asm_add(as, SPL_OP_JUMP, 0); spl_asm_setaddr(as, label_body_end, label_cond_start); spl_asm_setaddr(as, label_finish_jump, spl_asm_add(as, SPL_OP_NOP, 0)); spl_asm_shuffle(as, label_body_end - label_body_start, label_body_start, label_extra_start, label_body_start - label_extra_start, label_extra_start, label_extra_start + (label_body_end - label_body_start), -1); spl_asm_add(as, SPL_OP_END, 0); if (breakcont_label('B')) YYABORT; breakcont_end(); } { CHECKPOINT(); } | FOREACH foreach_type ID { breakcont_begin(); spl_asm_add(as, SPL_OP_BEGIN, 0); spl_asm_add(as, SPL_OP_PUSHC, "#index"); spl_asm_add(as, SPL_OP_UNDEF, 0); spl_asm_add(as, SPL_OP_POPL, 0); spl_asm_add(as, SPL_OP_PUSHC, "#array"); } '(' complex_expr ')' { spl_asm_add(as, SPL_OP_POPL, 0); if (breakcont_label('C')) YYABORT; pbstack_push(spl_asm_add(as, SPL_OP_NOP, 0)); spl_asm_add(as, SPL_OP_PUSHC, "#index"); spl_asm_add(as, SPL_OP_PUSH, "#array"); spl_asm_add(as, SPL_OP_PUSH, "#index"); spl_asm_add(as, SPL_OP_NEXT, 0); spl_asm_add(as, SPL_OP_COPY, 0); spl_asm_add(as, SPL_OP_DEFINED, 0); spl_asm_add(as, SPL_OP_UNLESS, 0); pbstack_push(spl_asm_add(as, SPL_OP_JUMP, 0)); spl_asm_add(as, SPL_OP_POPL, 0); if ($2 == 'V') { spl_asm_add(as, SPL_OP_PUSHC, $3); spl_asm_add(as, SPL_OP_PUSHC, "#array"); spl_asm_add(as, SPL_OP_PUSH, "#index"); spl_asm_add(as, SPL_OP_HENC, 0); spl_asm_add(as, SPL_OP_DOTCAT, 0); spl_asm_add(as, SPL_OP_GETVAL, 0); spl_asm_add(as, SPL_OP_POPL, 0); } else { spl_asm_add(as, SPL_OP_PUSHC, $3); spl_asm_add(as, SPL_OP_PUSH, "#index"); spl_asm_add(as, SPL_OP_POPL, 0); } } cmd { int finish_jump = pbstack_pop(); spl_asm_setaddr(as, spl_asm_add(as, SPL_OP_JUMP, 0), pbstack_pop()); spl_asm_setaddr(as, finish_jump, spl_asm_add(as, SPL_OP_DROP, 0)); spl_asm_add(as, SPL_OP_DROP, 0); spl_asm_add(as, SPL_OP_END, 0); if (breakcont_label('B')) YYABORT; breakcont_end(); CHECKPOINT(); free($3); } | function_method ID '(' { lbstack_push(); spl_asm_add(as, SPL_OP_PUSHC, $2); pbstack_push(spl_asm_add(as, $1 == 'F' ? SPL_OP_REGF : SPL_OP_REGM, 0)); } arglist_def optional_list_tail ')' { spl_asm_add(as, SPL_OP_CLEARA, 0); if (gen_debug_info) { spl_asm_add(as, SPL_OP_PUSHC, "#func"); spl_asm_add(as, SPL_OP_PUSHC, $2); spl_asm_add(as, SPL_OP_POPL, 0); } } '{' prog '}' { spl_asm_add(as, SPL_OP_UNDEF, 0); spl_asm_add(as, SPL_OP_RETURN, 0); spl_asm_setaddr(as, pbstack_pop(), spl_asm_add(as, SPL_OP_POPL, 0)); CHECKPOINT(); lbstack_pop(); free($2); } | OBJECT lvalue lvalue '{' { lbstack_push(); spl_asm_add(as, SPL_OP_OBJECT, 0); } prog '}' { spl_asm_add(as, SPL_OP_ENDOBJ, 0); CHECKPOINT(); lbstack_pop(); } | OBJECT lvalue '{' { lbstack_push(); spl_asm_add(as, SPL_OP_UNDEF, 0); spl_asm_add(as, SPL_OP_OBJECT, 0); spl_asm_add(as, SPL_OP_PUSHC, "init"); pbstack_push(spl_asm_add(as, SPL_OP_REGM, 0)); spl_asm_add(as, SPL_OP_CLEARA, 0); spl_asm_add(as, SPL_OP_PUSH, "!THIS"); spl_asm_add(as, SPL_OP_RETURN, 0); spl_asm_setaddr(as, pbstack_pop(), spl_asm_add(as, SPL_OP_POPL, 0)); } prog '}' { spl_asm_add(as, SPL_OP_ENDOBJ, 0); CHECKPOINT(); lbstack_pop(); } | TRY '(' ID ')' '{' { lbstack_push(); spl_asm_add(as, SPL_OP_BEGIN, 0); spl_asm_add(as, SPL_OP_PUSHC, $3); spl_asm_add(as, SPL_OP_TRY, 0); free($3); int j = spl_asm_add(as, SPL_OP_JUMP, 0); int n = spl_asm_add(as, SPL_OP_NOP, 0); pbstack_push(n); pbstack_push(j); } prog { pbstack_push(spl_asm_add(as, SPL_OP_JUMP, 0)); } catch_list '}' { spl_asm_setaddr(as, pbstack_pop(), spl_asm_add(as, SPL_OP_NOP, 0)); int j = pbstack_pop(); int n = pbstack_pop(); spl_asm_setaddr(as, j, n); spl_asm_add(as, SPL_OP_END, 0); lbstack_pop(); } | THROW complex_expr ';' { spl_asm_add(as, SPL_OP_THROW, 0); } | SWITCH '{' { spl_asm_add(as, SPL_OP_BEGIN, 0); } prog { int case_begin_jump = spl_asm_add(as, SPL_OP_JUMP, 0); int fin_jump = spl_asm_add(as, SPL_OP_JUMP, 0); spl_asm_setaddr(as, case_begin_jump, spl_asm_add(as, SPL_OP_NOP, 0)); pbstack_push(fin_jump); } case_list '}' { spl_asm_setaddr(as, pbstack_pop(), spl_asm_add(as, SPL_OP_NOP, 0)); spl_asm_add(as, SPL_OP_END, 0); } | ASM asm_list ';' { CHECKPOINT(); } | RETURN complex_expr ';' { spl_asm_add(as, SPL_OP_RETURN, 0); } | RETURN ';' { spl_asm_add(as, SPL_OP_UNDEF, 0); } { spl_asm_add(as, SPL_OP_RETURN, 0); } | EXIT ';' { spl_asm_add(as, SPL_OP_HALT, 0); } | DELETE lvalue ';' { spl_asm_add(as, SPL_OP_DELETE, 0); } { CHECKPOINT(); } | DEBUG complex_expr ';' { spl_asm_add(as, SPL_OP_DEBUG, 0); } | WARNING complex_expr ';' { spl_asm_add(as, SPL_OP_WARNING, 0); } | ERROR complex_expr ';' { spl_asm_add(as, SPL_OP_ERROR, 0); } | IMPORT complex_expr ';' { spl_asm_add(as, SPL_OP_IMPORT, 0); } | LOAD complex_expr ';' { spl_asm_add(as, SPL_OP_LOAD, 0); } ; asm_list: | asm_list VALUE { spl_asm_parse_line(as, $2); free($2); } /** shift/reduce conflict ** * * In a statement such as: * * for (var x=1, y=2; ..... * ^ * there is a shift/reduce conflict wheter the comma seperates two commands * or two var_decl_entries. It is resolved by shifting, so the DBLCOMMA (,,) * operator is needed for adding additional commands. */ for_cmds: | VAR var_decl | complex_expr { spl_asm_add(as, SPL_OP_DROP, 0); CHECKPOINT(); } | for_cmds ',' complex_expr { spl_asm_add(as, SPL_OP_DROP, 0); CHECKPOINT(); } | for_cmds DBLCOMMA complex_expr { spl_asm_add(as, SPL_OP_DROP, 0); CHECKPOINT(); } ; var_decl: var_decl_entry | var_decl ',' var_decl_entry ; var_decl_entry: lvalue { spl_asm_add(as, SPL_OP_UNDEF, 0); spl_asm_add(as, SPL_OP_POPL, 0); } | lvalue '=' complex_expr { spl_asm_add(as, SPL_OP_POPL, 0); } | lvalue SETCOPY { spl_asm_add(as, SPL_OP_COPY, 0); spl_asm_add(as, SPL_OP_UNDEF, 0); spl_asm_add(as, SPL_OP_POPL, 0); } complex_expr { spl_asm_add(as, SPL_OP_POPU, 0); } ; static_decl: static_decl_entry | static_decl ',' var_decl_entry ; static_decl_entry: lvalue { spl_asm_add(as, SPL_OP_UNDEF, 0); spl_asm_add(as, SPL_OP_POPS, 0); } | lvalue '=' complex_expr { spl_asm_add(as, SPL_OP_POPS, 0); } | lvalue SETCOPY { spl_asm_add(as, SPL_OP_COPY, 0); spl_asm_add(as, SPL_OP_UNDEF, 0); spl_asm_add(as, SPL_OP_POPS, 0); } complex_expr { spl_asm_add(as, SPL_OP_POPU, 0); } ; /** shift/reduce conflict ** * * This is the typical else-branch shift/reduce conflict which can be found in * almost every programming language grammar. */ else_branch: { spl_asm_setaddr(as, pbstack_pop(), spl_asm_add(as, SPL_OP_NOP, 0)); } | ELSE { int else_label = pbstack_pop(); pbstack_push(spl_asm_add(as, SPL_OP_JUMP, 0)); spl_asm_setaddr(as, else_label, spl_asm_add(as, SPL_OP_NOP, 0)); } cmd { spl_asm_setaddr(as, pbstack_pop(), spl_asm_add(as, SPL_OP_NOP, 0)); } ; catch_list: catch_list_entry | catch_list catch_list_entry ; catch_list_entry: CATCH ID ':' { int fin = pbstack_pop(); int frm = pbstack_pop(); spl_asm_setaddr(as, frm, spl_asm_add(as, SPL_OP_NOP, 0)); spl_asm_add(as, SPL_OP_PUSHC, $2); int c = spl_asm_add(as, SPL_OP_CATCH, 0); free($2); frm = spl_asm_add(as, SPL_OP_JUMP, 0); pbstack_push(frm); pbstack_push(fin); spl_asm_setaddr(as, c, spl_asm_add(as, SPL_OP_NOP, 0)); } prog { int fin = pbstack_pop(); int frm = pbstack_pop(); spl_asm_setaddr(as, spl_asm_add(as, SPL_OP_JUMP, 0), fin); pbstack_push(frm); pbstack_push(fin); } ; case_list: case_list_entry | case_list case_list_entry ; case_list_entry: case_list_entry_cond { spl_asm_add(as, SPL_OP_UNLESS, 0); pbstack_push(spl_asm_add(as, SPL_OP_JUMP, 0)); } prog { int next_jump = pbstack_pop(); int fin_jump = pbstack_pop(); pbstack_push(fin_jump); spl_asm_setaddr(as, spl_asm_add(as, SPL_OP_JUMP, 0), fin_jump); spl_asm_setaddr(as, next_jump, spl_asm_add(as, SPL_OP_NOP, 0)); } ; case_list_entry_cond: CASE complex_expr ':' | DEFAULT ':' { spl_asm_add(as, SPL_OP_ONE, 0); } ; foreach_type: '[' ']' { $$ = 'V'; } | { $$ = 'K'; } ; function_method: FUNCTION { $$ = 'F'; } | METHOD { $$ = 'M'; } ; regex_eval_mode: basic_expr REGEX_EVAL_R { $$ = REGEX_EVAL_RETURN; } | basic_expr NREGEX_EVAL_R { $$ = REGEX_EVAL_RETURN|REGEX_EVAL_NEG; } | lvalue REGEX_EVAL { $$ = REGEX_EVAL_SUBST; } | lvalue NREGEX_EVAL { $$ = REGEX_EVAL_SUBST|REGEX_EVAL_NEG; } ; lvalue: lvalue_head lvalue_tail_list | rvalue_to_lvalue_head '.' { char varname[32]; snprintf(varname, 32, "#rvtolv%d", rvtolv_counter++); spl_asm_add(as, SPL_OP_PUSHC, varname); spl_asm_add(as, SPL_OP_XCHG, 0); spl_asm_add(as, SPL_OP_POPL, 0); spl_asm_add(as, SPL_OP_PUSHC, varname); } lvalue_tail lvalue_tail_list { spl_asm_add(as, SPL_OP_DOTCAT, 0); } ; rvalue_to_lvalue_head: simple_call | '(' complex_expr ')' ; lvalue_head: ID { spl_asm_add(as, SPL_OP_PUSHC, $1); free($1); } | THIS { spl_asm_add(as, SPL_OP_PUSHC, "!THIS"); } | SPECIALREF { spl_asm_add(as, SPL_OP_PUSHC, $1); free($1); } ; lvalue_tail_list: | '.' lvalue_tail lvalue_tail_list { spl_asm_add(as, SPL_OP_DOTCAT, 0); } ; lvalue_tail: ID { spl_asm_add(as, SPL_OP_PUSHC, $1); free($1); } | '[' complex_expr ']' { spl_asm_add(as, SPL_OP_HENC, 0); } | SPECIALREF { spl_asm_add(as, SPL_OP_PUSHC, $1); free($1); } ; val_list_entry: VALUE { spl_asm_add(as, SPL_OP_PUSHC, $1); free($1); } ; val_list: val_list_entry | val_list val_list_entry { spl_asm_add(as, SPL_OP_CAT, 0); } ; /** shift/reduce conflict ** * * The 'rvalue CAT rvalue' creates another simple shift/reduce conflict. * Shifting is ok - so we have no problem here.. */ rvalue: rvalue_primitives | lvalue INC { spl_asm_add(as, SPL_OP_POSTINC, 0); } | lvalue DEC { spl_asm_add(as, SPL_OP_POSTDEC, 0); } | INC lvalue { spl_asm_add(as, SPL_OP_PREINC, 0); } | DEC lvalue { spl_asm_add(as, SPL_OP_PREDEC, 0); } | LENGTHOF rvalue { spl_asm_add(as, SPL_OP_LENGTH, 0); } | ELEMENTSOF rvalue { spl_asm_add(as, SPL_OP_ELEMENTS, 0); } | rvalue CAT rvalue { spl_asm_add(as, SPL_OP_CAT, 0); } | ID ENC { spl_asm_add(as, SPL_OP_ZERO, 0); } basic_expr { char *fname; my_asprintf(&fname, "encode_%s", $1); spl_asm_add(as, SPL_OP_PUSHAV, 0); spl_asm_add(as, SPL_OP_DCALL, fname); free(fname); free($1); } | ADD basic_expr %prec NEG | SUB basic_expr %prec NEG { spl_asm_add(as, SPL_OP_NEG, 0); } | LNOT basic_expr { spl_asm_add(as, SPL_OP_LNOT, 0); } | UNDEF { spl_asm_add(as, SPL_OP_UNDEF, 0); } | DEFINED rvalue { spl_asm_add(as, SPL_OP_DEFINED, 0); } | DECLARED lvalue { spl_asm_add(as, SPL_OP_DECLARED, 0); } | '[' { spl_asm_add(as, SPL_OP_UNDEF, 0); } array_list optional_list_tail ']' | function_method '(' { lbstack_push(); pbstack_push(spl_asm_add(as, $1 == 'F' ? SPL_OP_REGF : SPL_OP_REGM, 0)); } arglist_def optional_list_tail ')' { spl_asm_add(as, SPL_OP_CLEARA, 0); } '{' prog '}' { spl_asm_add(as, SPL_OP_UNDEF, 0); spl_asm_add(as, SPL_OP_RETURN, 0); spl_asm_setaddr(as, pbstack_pop(), spl_asm_add(as, SPL_OP_NOP, 0)); lbstack_pop(); } | EF_BEGIN { lbstack_push(); pbstack_push(spl_asm_add(as, SPL_OP_ENTER, 0)); } prog EF_END { spl_asm_add(as, SPL_OP_UNDEF, 0); spl_asm_add(as, SPL_OP_RETURN, 0); spl_asm_setaddr(as, pbstack_pop(), spl_asm_add(as, SPL_OP_NOP, 0)); lbstack_pop(); } | INSERT_PROG_BEGIN asm_list ';' { no_checkp_insn++; } prog INSERT_PROG_END asm_list ';' { no_checkp_insn--; } | RVALUE_CONTEXT_BEGIN { no_checkp_insn++; } { spl_asm_add(as, SPL_OP_BEGIN, 0); } basic_expr RVALUE_CONTEXT_END { spl_asm_add(as, SPL_OP_END, 0); } { no_checkp_insn--; } | func_expr '(' { pbstack_push(spl_asm_add(as, SPL_OP_ZERO, 0)); } arglist_call optional_list_tail ')' { pbstack_pop(); spl_asm_add(as, SPL_OP_LIFTCALL, 0); spl_asm_add(as, SPL_OP_CALL, 0); } | TRANSLATE_PREFIX { pbstack_push(spl_asm_add(as, SPL_OP_ZERO, 0)); } translate_args TRANSLATE_END { pbstack_pop(); spl_asm_add(as, SPL_OP_DCALL, "_"); } | simple_call ; simple_call: NEW { pbstack_push(spl_asm_add(as, SPL_OP_NOP, 0)); } rvalue_primitives '(' { pbstack_push(spl_asm_add(as, SPL_OP_ZERO, 0)); } arglist_call optional_list_tail ')' { int call_pos = spl_asm_add(as, SPL_OP_NEW, 0); int arg_pos = pbstack_pop(); int fp_pos = pbstack_pop(); int fp_size = arg_pos - fp_pos; int arg_size = call_pos - arg_pos; spl_asm_shuffle(as, fp_size, fp_pos, fp_pos + arg_size, arg_size, arg_pos, fp_pos, -1); } | MUL { pbstack_push(spl_asm_add(as, SPL_OP_NOP, 0)); } func_expr '(' { spl_asm_add(as, SPL_OP_SETCTX, 0); } { pbstack_push(spl_asm_add(as, SPL_OP_ZERO, 0)); } arglist_call optional_list_tail ')' { int call_pos = spl_asm_add(as, SPL_OP_CALL, 0); int arg_pos = pbstack_pop(); int fp_pos = pbstack_pop(); int fp_size = arg_pos - fp_pos; int arg_size = call_pos - arg_pos; spl_asm_shuffle(as, fp_size, fp_pos, fp_pos + arg_size, arg_size, arg_pos, fp_pos, -1); } | '$' ID '(' { pbstack_push(spl_asm_add(as, SPL_OP_ZERO, 0)); } arglist_call optional_list_tail ')' { pbstack_pop(); spl_asm_add(as, SPL_OP_CLIB, $2); free($2); } | FUNC_ID '(' { pbstack_push(spl_asm_add(as, SPL_OP_ZERO, 0)); } arglist_call optional_list_tail ')' { pbstack_pop(); spl_asm_add(as, SPL_OP_DCALL, $1); free($1); } ; rvalue_primitives: val_list | '(' complex_expr ')' | '#' '(' complex_expr ')' { spl_asm_add(as, SPL_OP_TOINT, 0); } | '.' '(' complex_expr ')' { spl_asm_add(as, SPL_OP_TOFLOAT, 0); } | lvalue { spl_asm_add(as, SPL_OP_GETVAL, 0); } ; complex_expr: lvalue '=' complex_expr { spl_asm_add(as, SPL_OP_POPIC, 0); } | lvalue SETCOPY complex_expr { spl_asm_add(as, SPL_OP_POPUC, 0); } | lvalue ADDEQ { spl_asm_add(as, SPL_OP_COPY, 0); spl_asm_add(as, SPL_OP_GETVAL, 0); } complex_expr { spl_asm_add(as, SPL_OP_ADD, 0); spl_asm_add(as, SPL_OP_POPIC, 0); } | lvalue SUBEQ { spl_asm_add(as, SPL_OP_COPY, 0); spl_asm_add(as, SPL_OP_GETVAL, 0); } complex_expr { spl_asm_add(as, SPL_OP_SUB, 0); spl_asm_add(as, SPL_OP_POPIC, 0); } | lvalue MULEQ { spl_asm_add(as, SPL_OP_COPY, 0); spl_asm_add(as, SPL_OP_GETVAL, 0); } complex_expr { spl_asm_add(as, SPL_OP_MUL, 0); spl_asm_add(as, SPL_OP_POPIC, 0); } | lvalue DIVEQ { spl_asm_add(as, SPL_OP_COPY, 0); spl_asm_add(as, SPL_OP_GETVAL, 0); } complex_expr { spl_asm_add(as, SPL_OP_DIV, 0); spl_asm_add(as, SPL_OP_POPIC, 0); } | lvalue MODEQ { spl_asm_add(as, SPL_OP_COPY, 0); spl_asm_add(as, SPL_OP_GETVAL, 0); } complex_expr { spl_asm_add(as, SPL_OP_MOD, 0); spl_asm_add(as, SPL_OP_POPIC, 0); } | lvalue POWEQ { spl_asm_add(as, SPL_OP_COPY, 0); spl_asm_add(as, SPL_OP_GETVAL, 0); } complex_expr { spl_asm_add(as, SPL_OP_POW, 0); spl_asm_add(as, SPL_OP_POPIC, 0); } | lvalue IADDEQ { spl_asm_add(as, SPL_OP_COPY, 0); spl_asm_add(as, SPL_OP_GETVAL, 0); } complex_expr { spl_asm_add(as, SPL_OP_IADD, 0); spl_asm_add(as, SPL_OP_POPIC, 0); } | lvalue ISUBEQ { spl_asm_add(as, SPL_OP_COPY, 0); spl_asm_add(as, SPL_OP_GETVAL, 0); } complex_expr { spl_asm_add(as, SPL_OP_ISUB, 0); spl_asm_add(as, SPL_OP_POPIC, 0); } | lvalue IMULEQ { spl_asm_add(as, SPL_OP_COPY, 0); spl_asm_add(as, SPL_OP_GETVAL, 0); } complex_expr { spl_asm_add(as, SPL_OP_IMUL, 0); spl_asm_add(as, SPL_OP_POPIC, 0); } | lvalue IDIVEQ { spl_asm_add(as, SPL_OP_COPY, 0); spl_asm_add(as, SPL_OP_GETVAL, 0); } complex_expr { spl_asm_add(as, SPL_OP_IDIV, 0); spl_asm_add(as, SPL_OP_POPIC, 0); } | lvalue IMODEQ { spl_asm_add(as, SPL_OP_COPY, 0); spl_asm_add(as, SPL_OP_GETVAL, 0); } complex_expr { spl_asm_add(as, SPL_OP_IMOD, 0); spl_asm_add(as, SPL_OP_POPIC, 0); } | lvalue IPOWEQ { spl_asm_add(as, SPL_OP_COPY, 0); spl_asm_add(as, SPL_OP_GETVAL, 0); } complex_expr { spl_asm_add(as, SPL_OP_IPOW, 0); spl_asm_add(as, SPL_OP_POPIC, 0); } | lvalue FADDEQ { spl_asm_add(as, SPL_OP_COPY, 0); spl_asm_add(as, SPL_OP_GETVAL, 0); } complex_expr { spl_asm_add(as, SPL_OP_FADD, 0); spl_asm_add(as, SPL_OP_POPIC, 0); } | lvalue FSUBEQ { spl_asm_add(as, SPL_OP_COPY, 0); spl_asm_add(as, SPL_OP_GETVAL, 0); } complex_expr { spl_asm_add(as, SPL_OP_FSUB, 0); spl_asm_add(as, SPL_OP_POPIC, 0); } | lvalue FMULEQ { spl_asm_add(as, SPL_OP_COPY, 0); spl_asm_add(as, SPL_OP_GETVAL, 0); } complex_expr { spl_asm_add(as, SPL_OP_FMUL, 0); spl_asm_add(as, SPL_OP_POPIC, 0); } | lvalue FDIVEQ { spl_asm_add(as, SPL_OP_COPY, 0); spl_asm_add(as, SPL_OP_GETVAL, 0); } complex_expr { spl_asm_add(as, SPL_OP_FDIV, 0); spl_asm_add(as, SPL_OP_POPIC, 0); } | lvalue FMODEQ { spl_asm_add(as, SPL_OP_COPY, 0); spl_asm_add(as, SPL_OP_GETVAL, 0); } complex_expr { spl_asm_add(as, SPL_OP_FMOD, 0); spl_asm_add(as, SPL_OP_POPIC, 0); } | lvalue FPOWEQ { spl_asm_add(as, SPL_OP_COPY, 0); spl_asm_add(as, SPL_OP_GETVAL, 0); } complex_expr { spl_asm_add(as, SPL_OP_FPOW, 0); spl_asm_add(as, SPL_OP_POPIC, 0); } | lvalue OADDEQ { spl_asm_add(as, SPL_OP_COPY, 0); spl_asm_add(as, SPL_OP_GETVAL, 0); } complex_expr { spl_asm_add(as, SPL_OP_OBJOP, "add"); spl_asm_add(as, SPL_OP_POPIC, 0); } | lvalue OSUBEQ { spl_asm_add(as, SPL_OP_COPY, 0); spl_asm_add(as, SPL_OP_GETVAL, 0); } complex_expr { spl_asm_add(as, SPL_OP_OBJOP, "sub"); spl_asm_add(as, SPL_OP_POPIC, 0); } | lvalue OMULEQ { spl_asm_add(as, SPL_OP_COPY, 0); spl_asm_add(as, SPL_OP_GETVAL, 0); } complex_expr { spl_asm_add(as, SPL_OP_OBJOP, "mul"); spl_asm_add(as, SPL_OP_POPIC, 0); } | lvalue ODIVEQ { spl_asm_add(as, SPL_OP_COPY, 0); spl_asm_add(as, SPL_OP_GETVAL, 0); } complex_expr { spl_asm_add(as, SPL_OP_OBJOP, "div"); spl_asm_add(as, SPL_OP_POPIC, 0); } | lvalue OMODEQ { spl_asm_add(as, SPL_OP_COPY, 0); spl_asm_add(as, SPL_OP_GETVAL, 0); } complex_expr { spl_asm_add(as, SPL_OP_OBJOP, "mod"); spl_asm_add(as, SPL_OP_POPIC, 0); } | lvalue OPOWEQ { spl_asm_add(as, SPL_OP_COPY, 0); spl_asm_add(as, SPL_OP_GETVAL, 0); } complex_expr { spl_asm_add(as, SPL_OP_OBJOP, "pow"); spl_asm_add(as, SPL_OP_POPIC, 0); } | lvalue SAPPEND { spl_asm_add(as, SPL_OP_COPY, 0); spl_asm_add(as, SPL_OP_GETVAL, 0); } complex_expr { spl_asm_add(as, SPL_OP_CAT, 0); spl_asm_add(as, SPL_OP_POPIC, 0); } | basic_expr '?' { spl_asm_add(as, SPL_OP_UNLESS, 0); pbstack_push(spl_asm_add(as, SPL_OP_JUMP, 0)); } complex_expr ':' { int bp_pos = pbstack_pop(); pbstack_push(spl_asm_add(as, SPL_OP_JUMP, 0)); spl_asm_setaddr(as, bp_pos, spl_asm_add(as, SPL_OP_NOP, 0)); } complex_expr { spl_asm_setaddr(as, pbstack_pop(), spl_asm_add(as, SPL_OP_NOP, 0)); } | POP rvalue { spl_asm_add(as, SPL_OP_APOP, 0); } | SHIFT rvalue { spl_asm_add(as, SPL_OP_ASHIFT, 0); } | PUSH lvalue ',' complex_expr { spl_asm_add(as, SPL_OP_APUSH, 0); } | UNSHIFT lvalue ',' complex_expr { spl_asm_add(as, SPL_OP_AUNSHIFT, 0); } | NEXT basic_expr ',' rvalue { spl_asm_add(as, SPL_OP_NEXT, 0); } | PREV basic_expr ',' rvalue { spl_asm_add(as, SPL_OP_PREV, 0); } | EVAL rvalue { spl_asm_add(as, SPL_OP_EVAL, 0); } | basic_expr ; basic_expr: rvalue | basic_expr PEQ basic_expr { spl_asm_add(as, SPL_OP_PEQ, 0); } | basic_expr PNE basic_expr { spl_asm_add(as, SPL_OP_PEQ, 0); spl_asm_add(as, SPL_OP_LNOT, 0); } | basic_expr ADD basic_expr { spl_asm_add(as, SPL_OP_ADD, 0); } | basic_expr SUB basic_expr { spl_asm_add(as, SPL_OP_SUB, 0); } | basic_expr MUL basic_expr { spl_asm_add(as, SPL_OP_MUL, 0); } | basic_expr DIV basic_expr { spl_asm_add(as, SPL_OP_DIV, 0); } | basic_expr MOD basic_expr { spl_asm_add(as, SPL_OP_MOD, 0); } | basic_expr POW basic_expr { spl_asm_add(as, SPL_OP_POW, 0); } | basic_expr EQ basic_expr { spl_asm_add(as, SPL_OP_EQ, 0); } | basic_expr NE basic_expr { spl_asm_add(as, SPL_OP_NE, 0); } | basic_expr LT basic_expr { spl_asm_add(as, SPL_OP_LT, 0); } | basic_expr GE basic_expr { spl_asm_add(as, SPL_OP_GE, 0); } | basic_expr LE basic_expr { spl_asm_add(as, SPL_OP_LE, 0); } | basic_expr GT basic_expr { spl_asm_add(as, SPL_OP_GT, 0); } | basic_expr IADD basic_expr { spl_asm_add(as, SPL_OP_IADD, 0); } | basic_expr ISUB basic_expr { spl_asm_add(as, SPL_OP_ISUB, 0); } | basic_expr IMUL basic_expr { spl_asm_add(as, SPL_OP_IMUL, 0); } | basic_expr IDIV basic_expr { spl_asm_add(as, SPL_OP_IDIV, 0); } | basic_expr IMOD basic_expr { spl_asm_add(as, SPL_OP_IMOD, 0); } | basic_expr IPOW basic_expr { spl_asm_add(as, SPL_OP_IPOW, 0); } | basic_expr IEQ basic_expr { spl_asm_add(as, SPL_OP_IEQ, 0); } | basic_expr INE basic_expr { spl_asm_add(as, SPL_OP_INE, 0); } | basic_expr ILT basic_expr { spl_asm_add(as, SPL_OP_ILT, 0); } | basic_expr IGE basic_expr { spl_asm_add(as, SPL_OP_IGE, 0); } | basic_expr ILE basic_expr { spl_asm_add(as, SPL_OP_ILE, 0); } | basic_expr IGT basic_expr { spl_asm_add(as, SPL_OP_IGT, 0); } | basic_expr FADD basic_expr { spl_asm_add(as, SPL_OP_FADD, 0); } | basic_expr FSUB basic_expr { spl_asm_add(as, SPL_OP_FSUB, 0); } | basic_expr FMUL basic_expr { spl_asm_add(as, SPL_OP_FMUL, 0); } | basic_expr FDIV basic_expr { spl_asm_add(as, SPL_OP_FDIV, 0); } | basic_expr FMOD basic_expr { spl_asm_add(as, SPL_OP_FMOD, 0); } | basic_expr FPOW basic_expr { spl_asm_add(as, SPL_OP_FPOW, 0); } | basic_expr FEQ basic_expr { spl_asm_add(as, SPL_OP_FEQ, 0); } | basic_expr FNE basic_expr { spl_asm_add(as, SPL_OP_FNE, 0); } | basic_expr FLT basic_expr { spl_asm_add(as, SPL_OP_FLT, 0); } | basic_expr FGE basic_expr { spl_asm_add(as, SPL_OP_FGE, 0); } | basic_expr FLE basic_expr { spl_asm_add(as, SPL_OP_FLE, 0); } | basic_expr FGT basic_expr { spl_asm_add(as, SPL_OP_FGT, 0); } | basic_expr OADD basic_expr { spl_asm_add(as, SPL_OP_OBJOP, "add"); } | basic_expr OSUB basic_expr { spl_asm_add(as, SPL_OP_OBJOP, "sub"); } | basic_expr OMUL basic_expr { spl_asm_add(as, SPL_OP_OBJOP, "mul"); } | basic_expr ODIV basic_expr { spl_asm_add(as, SPL_OP_OBJOP, "div"); } | basic_expr OMOD basic_expr { spl_asm_add(as, SPL_OP_OBJOP, "mod"); } | basic_expr OPOW basic_expr { spl_asm_add(as, SPL_OP_OBJOP, "pow"); } | basic_expr OEQ basic_expr { spl_asm_add(as, SPL_OP_OBJOP, "eq"); } | basic_expr ONE basic_expr { spl_asm_add(as, SPL_OP_OBJOP, "ne"); } | basic_expr OLT basic_expr { spl_asm_add(as, SPL_OP_OBJOP, "lt"); } | basic_expr OGE basic_expr { spl_asm_add(as, SPL_OP_OBJOP, "ge"); } | basic_expr OLE basic_expr { spl_asm_add(as, SPL_OP_OBJOP, "le"); } | basic_expr OGT basic_expr { spl_asm_add(as, SPL_OP_OBJOP, "gt"); } | basic_expr SEQ basic_expr { spl_asm_add(as, SPL_OP_SEQ, 0); } | basic_expr SNE basic_expr { spl_asm_add(as, SPL_OP_SNE, 0); } | basic_expr SLT basic_expr { spl_asm_add(as, SPL_OP_SLT, 0); } | basic_expr SGE basic_expr { spl_asm_add(as, SPL_OP_SGE, 0); } | basic_expr SLE basic_expr { spl_asm_add(as, SPL_OP_SLE, 0); } | basic_expr SGT basic_expr { spl_asm_add(as, SPL_OP_SGT, 0); } /*** OR and AND with logical ops *** | basic_expr LOR basic_expr { spl_asm_add(as, SPL_OP_LOR, 0); } | basic_expr LAND basic_expr { spl_asm_add(as, SPL_OP_LAND, 0); } ***********************************/ | basic_expr LOR { spl_asm_add(as, SPL_OP_COPY, 0); spl_asm_add(as, SPL_OP_IF, 0); pbstack_push(spl_asm_add(as, SPL_OP_JUMP, 0)); spl_asm_add(as, SPL_OP_DROP, 0); } basic_expr { spl_asm_setaddr(as, pbstack_pop(), spl_asm_add(as, SPL_OP_NOP, 0)); } | basic_expr LAND { spl_asm_add(as, SPL_OP_COPY, 0); spl_asm_add(as, SPL_OP_UNLESS, 0); pbstack_push(spl_asm_add(as, SPL_OP_JUMP, 0)); spl_asm_add(as, SPL_OP_DROP, 0); } basic_expr { spl_asm_setaddr(as, pbstack_pop(), spl_asm_add(as, SPL_OP_NOP, 0)); } | basic_expr REGEX basic_expr REGEX_SEP basic_expr REGEX_SEP { spl_asm_add(as, SPL_OP_REMATCH, 0); } | basic_expr REGEX_SUBST_R basic_expr REGEX_SEP basic_expr REGEX_SEP basic_expr REGEX_SEP { spl_asm_add(as, SPL_OP_RESUBST, 0); } | lvalue REGEX_SUBST basic_expr REGEX_SEP basic_expr REGEX_SEP basic_expr REGEX_SEP { spl_asm_add(as, SPL_OP_RESUBST, 0); } | basic_expr NREGEX basic_expr REGEX_SEP basic_expr REGEX_SEP { spl_asm_add(as, SPL_OP_REMATCH, 0); } { spl_asm_add(as, SPL_OP_LNOT, 0); } | basic_expr NREGEX_SUBST_R basic_expr REGEX_SEP basic_expr REGEX_SEP basic_expr REGEX_SEP { spl_asm_add(as, SPL_OP_RESUBST, 0); } { spl_asm_add(as, SPL_OP_LNOT, 0); } | lvalue NREGEX_SUBST basic_expr REGEX_SEP basic_expr REGEX_SEP basic_expr REGEX_SEP { spl_asm_add(as, SPL_OP_RESUBST, 0); } { spl_asm_add(as, SPL_OP_LNOT, 0); } | regex_eval_mode { spl_asm_add(as, SPL_OP_BEGIN, 0); if ($1 & REGEX_EVAL_SUBST) { spl_asm_add(as, SPL_OP_COPY, 0); spl_asm_add(as, SPL_OP_PUSHC, "#name"); spl_asm_add(as, SPL_OP_XCHG, 0); spl_asm_add(as, SPL_OP_POPL, 0); spl_asm_add(as, SPL_OP_GETVAL, 0); } spl_asm_add(as, SPL_OP_PUSHC, "#text"); spl_asm_add(as, SPL_OP_XCHG, 0); spl_asm_add(as, SPL_OP_POPL, 0); spl_asm_add(as, SPL_OP_PUSHC, "#array"); spl_asm_add(as, SPL_OP_PUSH, "#text"); } basic_expr REGEX_SEP basic_expr REGEX_SEP { spl_asm_add(as, SPL_OP_REMATCH, 0); spl_asm_add(as, SPL_OP_POPL, 0); spl_asm_add(as, SPL_OP_PUSHC, "#index"); spl_asm_add(as, SPL_OP_UNDEF, 0); spl_asm_add(as, SPL_OP_POPL, 0); spl_asm_add(as, SPL_OP_PUSHC, "#newtext"); spl_asm_add(as, SPL_OP_UNDEF, 0); spl_asm_add(as, SPL_OP_POPL, 0); pbstack_push(spl_asm_add(as, SPL_OP_NOP, 0)); spl_asm_add(as, SPL_OP_PUSHC, "#index"); spl_asm_add(as, SPL_OP_PUSH, "#array"); spl_asm_add(as, SPL_OP_PUSH, "#index"); spl_asm_add(as, SPL_OP_NEXT, 0); spl_asm_add(as, SPL_OP_COPY, 0); spl_asm_add(as, SPL_OP_DEFINED, 0); spl_asm_add(as, SPL_OP_UNLESS, 0); pbstack_push(spl_asm_add(as, SPL_OP_JUMP, 0)); spl_asm_add(as, SPL_OP_POPL, 0); spl_asm_add(as, SPL_OP_PUSHC, "#reres"); spl_asm_add(as, SPL_OP_PUSHC, "#array"); spl_asm_add(as, SPL_OP_PUSH, "#index"); spl_asm_add(as, SPL_OP_HENC, 0); spl_asm_add(as, SPL_OP_DOTCAT, 0); spl_asm_add(as, SPL_OP_GETVAL, 0); spl_asm_add(as, SPL_OP_POPL, 0); spl_asm_add(as, SPL_OP_PUSHC, "#newtext"); spl_asm_add(as, SPL_OP_PUSH, "#newtext"); spl_asm_add(as, SPL_OP_PUSH, "#reres.=NC"); spl_asm_add(as, SPL_OP_CAT, 0); spl_asm_add(as, SPL_OP_BEGIN, 0); } basic_expr { spl_asm_add(as, SPL_OP_END, 0); int finish_jump = pbstack_pop(); spl_asm_add(as, SPL_OP_CAT, 0); spl_asm_add(as, SPL_OP_POPL, 0); spl_asm_setaddr(as, spl_asm_add(as, SPL_OP_JUMP, 0), pbstack_pop()); spl_asm_setaddr(as, finish_jump, spl_asm_add(as, SPL_OP_DROP, 0)); spl_asm_add(as, SPL_OP_DROP, 0); spl_asm_add(as, SPL_OP_PUSH, "#newtext"); spl_asm_add(as, SPL_OP_DEFINED, 0); spl_asm_add(as, SPL_OP_UNLESS, 0); int p1 = spl_asm_add(as, SPL_OP_JUMP, 0); if ($1 & REGEX_EVAL_SUBST) spl_asm_add(as, SPL_OP_PUSH, "#name"); spl_asm_add(as, SPL_OP_PUSH, "#newtext"); spl_asm_add(as, SPL_OP_PUSHC, "#array"); spl_asm_add(as, SPL_OP_PUSH, "#index"); spl_asm_add(as, SPL_OP_HENC, 0); spl_asm_add(as, SPL_OP_DOTCAT, 0); spl_asm_add(as, SPL_OP_PUSHC, "=LC"); spl_asm_add(as, SPL_OP_DOTCAT, 0); spl_asm_add(as, SPL_OP_GETVAL, 0); spl_asm_add(as, SPL_OP_CAT, 0); if ($1 & REGEX_EVAL_SUBST) { spl_asm_add(as, SPL_OP_POPI, 0); spl_asm_setaddr(as, p1, spl_asm_add(as, SPL_OP_NOP, 0)); } if ($1 & REGEX_EVAL_RETURN) { int p2 = spl_asm_add(as, SPL_OP_JUMP, 0); spl_asm_setaddr(as, p1, spl_asm_add(as, SPL_OP_PUSH, "#text")); spl_asm_setaddr(as, p2, spl_asm_add(as, SPL_OP_NOP, 0)); } if ($1 & REGEX_EVAL_SUBST) { spl_asm_add(as, SPL_OP_PUSH, "#array"); spl_asm_add(as, SPL_OP_ZERO, 0); spl_asm_add(as, SPL_OP_IADD, 0); } spl_asm_add(as, SPL_OP_END, 0); if ($1 & REGEX_EVAL_NEG) spl_asm_add(as, SPL_OP_LNOT, 0); CHECKPOINT(); } ; optional_list_tail: | ',' optional_list_tail ; array_list: | array_element | array_list ',' array_element ; array_element: complex_expr ARRAYREF { spl_asm_add(as, SPL_OP_HENC, 0); } complex_expr { spl_asm_add(as, SPL_OP_APUSHREFID, 0); } | hash_index_lvalue ':' complex_expr { spl_asm_add(as, SPL_OP_APUSHREFID, 0); } | complex_expr { spl_asm_add(as, SPL_OP_APUSHREF, 0); } ; func_expr: '(' complex_expr ')' | lvalue { spl_asm_add(as, SPL_OP_GETVAL, 0); } ; arglist_def: | arglist_def_entry | arglist_def ',' arglist_def_entry ; arglist_def_entry: ID { spl_asm_add(as, SPL_OP_POPA, $1); free($1); } | '@' ID { spl_asm_add(as, SPL_OP_PUSHC, $2); spl_asm_add(as, SPL_OP_APOPA, 0); free($2); } | MOD ID { spl_asm_add(as, SPL_OP_PUSHC, $2); spl_asm_add(as, SPL_OP_UNDEF, 0); spl_asm_add(as, SPL_OP_POPLC, 0); spl_asm_add(as, SPL_OP_HGETA, 0); free($2); } ; arglist_call: | arglist_call_entry | arglist_call ',' arglist_call_entry ; translate_args: | arglist_call_entry | translate_args TRANSLATE_SEPERATOR arglist_call_entry ; arglist_call_entry: { pbstack_push(spl_asm_add(as, SPL_OP_NOP, 0)); } arglist_call_entry_tail ; arglist_call_entry_tail: complex_expr { int begin_this_param = pbstack_pop(); int begin_param_list = pbstack_pop() + 1; int size_list = begin_this_param - begin_param_list; int size_this = spl_asm_add(as, SPL_OP_PUSHAV, 0) + 1 - begin_this_param; spl_asm_shuffle(as, size_list, begin_param_list, begin_param_list+size_this, size_this, begin_this_param, begin_param_list, -1); pbstack_push(begin_param_list - 1); } | '@' complex_expr { int begin_this_param = pbstack_pop(); int begin_param_list = pbstack_pop() + 1; int size_list = begin_this_param - begin_param_list; int size_this = spl_asm_add(as, SPL_OP_APUSHA, 0) + 1 - begin_this_param; spl_asm_shuffle(as, size_list, begin_param_list, begin_param_list+size_this, size_this, begin_this_param, begin_param_list, -1); pbstack_push(begin_param_list - 1); } | MOD complex_expr { int begin_this_param = pbstack_pop(); int begin_param_list = pbstack_pop() + 1; int size_list = begin_this_param - begin_param_list; int size_this = spl_asm_add(as, SPL_OP_HSETA, 0) + 1 - begin_this_param; spl_asm_shuffle(as, size_list, begin_param_list, begin_param_list+size_this, size_this, begin_this_param, begin_param_list, -1); pbstack_push(begin_param_list - 1); } | hash_index_lvalue ':' complex_expr { int begin_this_param = pbstack_pop(); int begin_param_list = pbstack_pop() + 1; int size_list = begin_this_param - begin_param_list; int size_this = spl_asm_add(as, SPL_OP_APUSHREFID, 0) + 1 - begin_this_param; spl_asm_shuffle(as, size_list, begin_param_list, begin_param_list+size_this, size_this, begin_this_param, begin_param_list, -1); pbstack_push(begin_param_list - 1); } ; hash_index_lvalue: VALUE { spl_asm_add(as, SPL_OP_PUSHC, $1); free($1); } | lvalue ; %% static struct { char *text; int lex; } lex_ops[] = { { "(**)=", OPOWEQ }, { "#**=", IPOWEQ }, { ".**=", FPOWEQ }, { "(**)", OPOW }, { "(+)=", OADDEQ }, { "(-)=", OSUBEQ }, { "(*)=", OMULEQ }, { "(/)=", ODIVEQ }, { "(%)=", OMODEQ }, { "(==)", OEQ }, { "(!=)", ONE }, { "(<=)", OLE }, { "(>=)", OGE }, { "(<)", OLT }, { "(>)", OGT }, { "(+)", OADD }, { "(-)", OSUB }, { "(*)", OMUL }, { "(/)", ODIV }, { "(%)", OMOD }, { "[*]", SPECIALREF }, { "[+]", SPECIALREF }, { "[/]", SPECIALREF }, { "[.]", SPECIALREF }, { ">>>", STRING_EOL }, { "<<<", STRING_EOL_S }, { "<=>", XCHG }, { "^==", PEQ }, { "^!=", PNE }, { "#==", IEQ }, { "#!=", INE }, { "#<=", ILE }, { "#>=", IGE }, { ".==", FEQ }, { ".!=", FNE }, { ".<=", FLE }, { ".>=", FGE }, { "~==", SEQ }, { "~!=", SNE }, { "~<=", SLE }, { "~>=", SGE }, { "#+=", IADDEQ }, { "#-=", ISUBEQ }, { "#*=", IMULEQ }, { "#/=", IDIVEQ }, { "#%=", IMODEQ }, { ".+=", FADDEQ }, { ".-=", FSUBEQ }, { ".*=", FMULEQ }, { "./=", FDIVEQ }, { ".%=", FMODEQ }, { "**=", POWEQ }, { "#**", IPOW }, { ".**", FPOW }, { ">>", STRING_LABEL }, { "<<", STRING_LABEL_S }, { "#<", ILT }, { "#>", IGT }, { ".<", FLT }, { ".>", FGT }, { "~<", SLT }, { "~>", SGT }, { ",,", DBLCOMMA }, { ":=", SETCOPY }, { "||", LOR }, { "&&", LAND }, { "++", INC }, { "--", DEC }, { "==", EQ }, { "!=", NE }, { "<=", LE }, { ">=", GE }, { "+=", ADDEQ }, { "-=", SUBEQ }, { "*=", MULEQ }, { "/=", DIVEQ }, { "%=", MODEQ }, { "#+", IADD }, { "#-", ISUB }, { "#*", IMUL }, { "#/", IDIV }, { "#%", IMOD }, { ".+", FADD }, { ".-", FSUB }, { ".*", FMUL }, { "./", FDIV }, { ".%", FMOD }, { "**", POW }, { "::", ENC }, { "~=", SAPPEND }, { "=>", ARRAYREF }, { "<", LT }, { ">", GT }, { "!", LNOT }, { "+", ADD }, { "-", SUB }, { "*", MUL }, { "/", DIV }, { "%", MOD }, { "~", CAT }, { 0, 0 } }; static struct { char *text; int lex; } lex_keywords[] = { { "debug", DEBUG }, { "warning", WARNING }, { "panic", ERROR }, { "delete", DELETE }, { "function", FUNCTION }, { "method", METHOD }, { "import", IMPORT }, { "load", LOAD }, { "new", NEW }, { "this", THIS }, { "object", OBJECT }, { "var", VAR }, { "static", STATIC }, { "if", IF }, { "else", ELSE }, { "do", DO }, { "while", WHILE }, { "for", FOR }, { "foreach", FOREACH }, { "asm", ASM }, { "return", RETURN }, { "exit", EXIT }, { "defined", DEFINED }, { "declared", DECLARED }, { "undef", UNDEF }, { "goto", GOTO }, { "break", BREAK }, { "continue", CONTINUE }, { "pop", POP }, { "shift", SHIFT }, { "push", PUSH }, { "unshift", UNSHIFT }, { "next", NEXT }, { "prev", PREV }, { "eval", EVAL }, { "not", LNOT }, { "and", LAND }, { "or", LOR }, { "try", TRY }, { "catch", CATCH }, { "throw", THROW }, { "switch", SWITCH }, { "default", DEFAULT }, { "case", CASE }, { "lengthof", LENGTHOF }, { "elementsof", ELEMENTSOF }, { 0, 0 } }; struct lex_srcfile; struct lex_srcfile { struct lex_srcfile *next; const char *text; const char *name; }; struct lex_item; struct lex_item { char *text; int lex; struct lex_item *next, *prev; struct lex_srcfile *src; const char *pos; }; static const char *input; static char *encoding; static spl_malloc_file_function *malloc_file_func; static struct lex_item *lex_item_list_first; static struct lex_item *lex_item_list_last; static struct lex_item *lex_last_item; static struct lex_item *lex_translate_ptr; int lex_translate_count; static struct lex_srcfile *lex_srcfile_list; static struct lex_srcfile *lex_srcfile_current; static jmp_buf lex_goterr; static char *lex_errstr; struct define { int active, isarg; char *name, *text, *args; struct define *next; }; static int in_pragma_arg; static struct define *define_list; static char *current_define; static int define_recursion; static struct lex_item *lex_insert(struct lex_item *insertafter, int lex, char *text) { if (in_pragma_arg) { if (text) free(text); return 0; } struct lex_item *item = calloc(1, sizeof(struct lex_item)); if (spl_yydebug) fprintf(stderr, "[Lexer: new symbol %s (%d) with (%s) value]\n", yytname[YYTRANSLATE(lex)], lex, text ? text : ""); item->text = text; item->lex = lex; item->src = lex_srcfile_current; item->pos = input; if (insertafter == lex_item_list_last) insertafter = 0; if (insertafter) { struct lex_item *next = insertafter->next; next->prev = insertafter->next = item; item->prev = insertafter; item->next = next; } else { if (lex_item_list_first) { lex_item_list_last->next = item; item->prev = lex_item_list_last; } else lex_item_list_first = item; lex_item_list_last = item; } return item; } static struct lex_item *lex_new(int lex, char *text) { return lex_insert(0, lex, text); } static struct lex_item *lex_new_translate(int lex, char *text) { if (!lex_translate_ptr) return lex_insert(0, lex, text); lex_translate_ptr = lex_insert(lex_translate_ptr, lex, text); return lex_translate_ptr; } static int spl_yylex() { if ( !lex_item_list_first ) return 0; int lex = lex_item_list_first->lex; spl_yylval.text = lex_item_list_first->text; if (spl_yydebug) fprintf(stderr, "[Lexer: shifting symbol %s (%d) with (%s) value] ", yytname[YYTRANSLATE(lex)], lex, spl_yylval.text ? spl_yylval.text : ""); if (lex_last_item) free(lex_last_item); lex_last_item = lex_item_list_first; lex_item_list_first = lex_item_list_first->next; return lex; } static void spl_lex_pragma(); static void spl_lex_string(char *term, int flags, int indenting_delim); static void spl_lex_prog_string(char *text); static void spl_lex_prog(char *term); #define LEX_STRING_BACKSLASHES 0x0001 #define LEX_STRING_SPLTAGS 0x0002 #define LEX_STRING_REGEX 0x0004 static char *malloc_file_wrapper(const char *filename, int is_embedded) { if ( is_embedded ) { char *token; my_asprintf(&token, "#embedded-file %s ", filename); const char *begin = strstr(lex_srcfile_current->text, token); int token_size = strlen(token); free(token); if ( !begin ) return 0; begin += token_size; token_size = strcspn(begin, " \r\n\t"); if ( token_size <= 0 ) return 0; token = my_strndup(begin, token_size); begin = begin + token_size; if ( *begin == ' ' || *begin == '\t' || *begin == '\r' || *begin == '\n' ) begin++; const char *end = strstr(begin, token); free(token); if ( !end ) return 0; return my_strndup(begin, end-begin); } if ( malloc_file_func ) { char *retval; if (*filename != '/' && as->vm && as->vm->current_dir_name) { char fullname[strlen(filename) + strlen(as->vm->current_dir_name) + 2]; sprintf(fullname, "%s/%s", as->vm->current_dir_name, filename); retval = malloc_file_func(fullname, 0); } else retval = malloc_file_func(filename, 0); if (retval) { if (encoding) { char *orig_retval = retval; retval = spl_utf8_import(orig_retval, encoding); free(orig_retval); if (!retval) { spl_report(SPL_REPORT_COMPILER, as, "Character set (#encoding) '%s' is unknown!\n", encoding); return 0; } } if ( spl_utf8_check(retval) ) { spl_report(SPL_REPORT_COMPILER, as, "Compiler tried to load non-utf8 encoded text!\n" "A decoder bug or just a missing '#encoding' pragma?\n"); free(retval); return 0; } } return retval; } return 0; } static char *get_pragma_body() { int data_len = strcspn(input, "\n"); while (input[data_len] == '\n' && input[data_len+1] == '\\') data_len += strcspn(input+data_len+1, "\n")+1; char *ret = my_strndup(input, data_len); input += data_len; for (char *in=ret, *out=ret; (*out = *in) != 0; in++, out++) if (in[0] == '\n' && in[1] == '\\') in++; return ret; } static void import_asm(int mode, unsigned char *bytecode, unsigned char *map, int offset, int bytecode_len) { int next_condit = 0; next_op: if (offset < 16 || offset >= bytecode_len) { my_asprintf(&lex_errstr, "Error while importing bytecode file."); longjmp(lex_goterr, __LINE__); } unsigned char op = bytecode[offset]; int arg_size = 0, arg = 0; char *txtarg = 0; if (op == SPL_OP_HALT || (mode == 0 && map[offset])) return; int this_offset = offset++; if (op < 0x60) { arg_size = 4 - (op & 3); op = op & ~3; arg = spl_bytes_to_int(arg_size, bytecode + offset); offset += arg_size; if (op >= 0x20) { if (offset + arg >= bytecode_len || offset + arg < 16) { my_asprintf(&lex_errstr, "Error while importing bytecode file."); longjmp(lex_goterr, __LINE__); } txtarg = (char*)(bytecode + offset + arg); } } if (mode == 0) { int optype = spl_optype(op); map[this_offset] = 1; int this_condit = next_condit; next_condit = optype == 'C'; if (optype == 'J' && !this_condit) { offset += arg; goto next_op; } if (optype == 'J' || optype == 'B') import_asm(mode, bytecode, map, offset + arg, bytecode_len); if (optype == 'E') return; } if (mode == 1) { char buffer[100 + (txtarg ? strlen(txtarg)*2 : 0)]; int pos = sprintf(buffer, ":BCI_%d_%d %s", import_asm_label_counter, this_offset, spl_asm_op2txt(op)); if (txtarg) { buffer[pos++] = ' '; buffer[pos++] = '\"'; for (int i=0; txtarg[i]; i++) { if (txtarg[i] == '\n') { buffer[pos++] = '\\'; buffer[pos++] = 'n'; continue; } if (txtarg[i] == '\"') { buffer[pos++] = '\\'; buffer[pos++] = '\"'; continue; } if (txtarg[i] == '\\') { buffer[pos++] = '\\'; buffer[pos++] = '\\'; continue; } buffer[pos++] = txtarg[i]; } buffer[pos++] = '\"'; } if (op < 0x20) pos += sprintf(buffer+pos, " :BCI_%d_%d", import_asm_label_counter, offset + arg); buffer[pos++] = 0; lex_new(VALUE, strdup(buffer)); while (offset < bytecode_len && !map[offset]) offset++; if (offset >= bytecode_len) return; } goto next_op; } static void spl_lex_pragma() { static const char *stop_string = " \t\r\n;,(){}[]*-+~!\'\"@#$%^&=?"; static const char *hard_pragmas[] = { "define", "undef", "encoding", "file-as-const", "file-as-code", "file-as-template", "file-as-bytecode", "embedded-file", 0 }; // ignore '#!...\n' strings if (*input == '!') { while (*input && *input != '\n') input++; return; } int stmt_len = strcspn(input, stop_string); for (int i=0; hard_pragmas[i]; i++) { int len = strlen(hard_pragmas[i]); if (!strncmp(input, hard_pragmas[i], len) && strcspn(input+len, stop_string) == 0) { stmt_len = len; break; } } char *stmt = my_strndupa(input, stmt_len); input += stmt_len; input += strspn(input, " \t\r\n"); int arg_has_asterisk_prefix = 0; char *arg = 0; if (!strncmp(stmt, "file-as-", 8)) if ( *input == '*' ) { arg_has_asterisk_prefix=1; input++; } if (!strncmp(stmt, "file-as-", 8) || !strcmp(stmt, "encoding") || !strcmp(stmt, "define") || !strcmp(stmt, "undef") || !strcmp(stmt, "embedded-file")) { int arg_len = strcspn(input, stop_string); arg = my_strndupa(input, arg_len); input += arg_len; } if ( !strcmp(stmt, "encoding") ) { return; } if ( !strcmp(stmt, "define") ) { if (in_pragma_arg) { free(get_pragma_body()); return; } struct define *def = calloc(1, sizeof(struct define)); if (*input == '(') { int len = strcspn(input+1, ")"); def->args = my_strndup(input+1, len); for (char *in=def->args, *out=def->args; (*out = *in) != 0; in++, out++) { while (in[1] == '\n' || in[1] == '\r' || in[1] == '\t' || in[1] == ' ') in++; } input += len+2; } def->name = strdup(arg); def->text = get_pragma_body(); def->next = define_list; define_list = def; return; } if ( !strcmp(stmt, "embedded-file") && *input == ' ' ) { int token_size = strcspn(++input, " \r\n\t"); if ( token_size <= 0 ) goto unknown_pragma; char *token = my_strndup(input, token_size); input = input + token_size; const char *end = strstr(input, token); free(token); if ( !end ) { my_asprintf(&lex_errstr, "Can't find end label for #%s.", stmt); longjmp(lex_goterr, __LINE__); } input = end + token_size; return; } if (in_pragma_arg) return; if ( !strcmp(stmt, "undef") ) { struct define **lastp = &define_list; struct define *def = define_list; while (def) { if (!def->isarg && !strcmp(def->name, arg)) { *lastp = def->next; free(def->name); free(def->text); if (def->args) free(def->args); free(def); def = *lastp; } else { lastp = &def->next; def = def->next; } } return; } if ( !strcmp(stmt, "file-as-const") ) { char *text = malloc_file_wrapper(arg, arg_has_asterisk_prefix); if ( !text ) { my_asprintf(&lex_errstr, "Can't open #file-as-const file (%s%s).", arg_has_asterisk_prefix ? "*" : "", arg); longjmp(lex_goterr, __LINE__); } lex_new(VALUE, text); return; } if ( !strcmp(stmt, "file-as-code") ) { char *text = malloc_file_wrapper(arg, arg_has_asterisk_prefix); if ( !text ) { my_asprintf(&lex_errstr, "Can't open #file-as-code file (%s%s).", arg_has_asterisk_prefix ? "*" : "", arg); longjmp(lex_goterr, __LINE__); } struct lex_srcfile *oldsrc = lex_srcfile_current; const char *oldinput = input; lex_srcfile_current = malloc(sizeof(struct lex_srcfile)); lex_srcfile_current->text = input = text; lex_srcfile_current->name = strdup(arg); lex_srcfile_current->next = lex_srcfile_list; lex_srcfile_list = lex_srcfile_current; spl_lex_prog(0); lex_srcfile_current = oldsrc; input = oldinput; return; } if ( !strcmp(stmt, "file-as-template") ) { char *text = malloc_file_wrapper(arg, arg_has_asterisk_prefix); if ( !text ) { my_asprintf(&lex_errstr, "Can't open #file-as-template file (%s%s).", arg_has_asterisk_prefix ? "*" : "", arg); longjmp(lex_goterr, __LINE__); } struct lex_srcfile *oldsrc = lex_srcfile_current; const char *oldinput = input; lex_srcfile_current = malloc(sizeof(struct lex_srcfile)); lex_srcfile_current->text = input = text; lex_srcfile_current->name = strdup(arg); lex_srcfile_current->next = lex_srcfile_list; lex_srcfile_list = lex_srcfile_current; spl_lex_string(0, LEX_STRING_SPLTAGS, 0); lex_srcfile_current = oldsrc; input = oldinput; return; } if ( !strcmp(stmt, "file-as-bytecode") ) { unsigned char *bytecode = 0; int bytecode_len = 0; if (malloc_file_func) { if (*arg != '/' && as->vm && as->vm->current_dir_name) { char fullname[strlen(arg) + strlen(as->vm->current_dir_name) + 2]; sprintf(fullname, "%s/%s", as->vm->current_dir_name, arg); bytecode = malloc_file_func(fullname, &bytecode_len); } else bytecode = malloc_file_func(arg, &bytecode_len); } if (!bytecode) { my_asprintf(&lex_errstr, "Can't open #file-as-bytecode file (%s).", arg); longjmp(lex_goterr, __LINE__); } if (bytecode_len < 16 || memcmp(bytecode, SPL_SIGNATURE, 16)) { my_asprintf(&lex_errstr, "Bytecode from #file-as-bytecode file (%s) has invalid signature.", arg); longjmp(lex_goterr, __LINE__); } lex_new(ASM, 0); import_asm_label_counter++; unsigned char *map = calloc(1, bytecode_len); import_asm(0, bytecode, map, 16, bytecode_len); import_asm(1, bytecode, map, 16, bytecode_len); lex_new(';', 0); free(bytecode); free(map); return; } for (struct define *def = define_list; def; def = def->next) if (!strcmp(def->name, stmt)) { lex_new(VALUE, strdup(def->text)); return; } unknown_pragma: my_asprintf(&lex_errstr, "Unknown compiler-pragma #%s.", stmt); longjmp(lex_goterr, __LINE__); } static void spl_lex_new_string(char *text, int flags) { if ( flags & LEX_STRING_BACKSLASHES ) { int i, j; for (i=0, j=0; text[i]; i++, j++) if ( text[i] == '\\' && text[i+1] ) { switch ( text[++i] ) { case 'a': text[j] = '\a'; break; case 'b': text[j] = '\b'; break; case 't': text[j] = '\t'; break; case 'n': text[j] = '\n'; break; case 'v': text[j] = '\v'; break; case 'f': text[j] = '\f'; break; case 'r': text[j] = '\r'; break; default: text[j] = input[i]; } } else text[j] = text[i]; text[j] = text[i]; } else { int i, j; for (i=0, j=0; text[i]; i++, j++) { text[j] = text[i]; } text[j] = text[i]; } if (lex_translate_ptr) { int newtext_len = 0; char *newtext; for (int i=0; text[i]; i++, newtext_len++) if (text[i] == '{') newtext_len++; newtext = malloc(newtext_len+1); for (int i=0, j=0; text[i]; i++) { newtext[j++] = text[i]; if (text[i] == '{') newtext[j++] = '}'; } newtext[newtext_len] = 0; free(text); text = newtext; } lex_new_translate(VALUE, text); } static int spl_lex_findelsetag() { int offset = strspn(input, " \t\r\n"); if (!strncmp(input + offset, "", 10)) { input += offset + 10; return 1; } return 0; } static void spl_lex_splifcalltag(int flags, int indenting_delim, int isiftag) { int len = strcspn(input, " \t\r\n>"); char *funcname, *endtag; int hasbody = 1; my_asprintf(&funcname, "%s_%.*s", isiftag ? "splif" : "splcall", len, input); my_asprintf(&endtag, "", isiftag ? "splif" : "splcall", len, input); input += len; lex_new('(', 0); lex_new(ID, funcname); lex_new('(', 0); while (1) { input += strspn(input, " \t\r\n"); if (!isiftag && input[0] == '/' && input[1] == '>') { hasbody = 0; input++; } if (*input == '>' || *input == 0) { if (*input) input++; break; } len = strcspn(input, " \t\r\n=\"\'(>"); lex_new(ID, my_strndup(input, len)); lex_new(':', 0); input += len; input += strcspn(input, "\'\"(>"); if (*input == '"') { input++; spl_lex_string("\"", LEX_STRING_BACKSLASHES, 0); if (*input == '"') input++; } if (*input == '\'') { input++; spl_lex_string("'", LEX_STRING_BACKSLASHES, 0); if (*input == '"') input++; } if (*input == '(') { input++; spl_lex_prog(")"); if (*input == ')') input++; } lex_new(',', 0); } if (!isiftag && hasbody) { lex_new(FUNCTION, 0); lex_new('(', 0); lex_new(')', 0); lex_new('{', 0); lex_new(RETURN, 0); spl_lex_string(endtag, flags, indenting_delim); lex_new(';', 0); lex_new('}', 0); } lex_new(')', 0); if (isiftag) { lex_new('?', 0); spl_lex_string(endtag, flags, indenting_delim); lex_new(':', 0); if (spl_lex_findelsetag()) spl_lex_string("", flags, indenting_delim); else lex_new(VALUE, strdup("")); } lex_new(')', 0); free(endtag); } static void spl_lex_spltag(int flags, int indenting_delim) { int len = strcspn(input, " \t\r\n>"); char *tag = my_strndupa(input, len); char *attr_var = 0; char *attr_list = 0; char *attr_code = 0; char *attr_char = 0; input += len; input += strspn(input, " \t\r\n"); while ( *input && *input != '>' ) { int inner_len = strcspn(input, " \t\r\n\"=>"); char *attr = my_strndupa(input, inner_len); input += inner_len; if ( *input == '=' ) { input++; if ( *input == '"' ) { input++; inner_len = strcspn(input, "\""); } else inner_len = strcspn(input, " \t\r\n\">"); char *value = my_strndupa(input, inner_len); input += inner_len; if (*input == '"') input++; input += strspn(input, " \t\r\n"); if ( !strcmp(attr, "var") ) attr_var = value; if ( !strcmp(attr, "list") ) attr_list = value; if ( !strcmp(attr, "code") ) attr_code = value; if ( !strcmp(attr, "char") ) attr_char = value; } } if (*input == '>') input++; if ( tag[0] == '_' && tag[strlen(tag)-1] == '_' ) { int endtag_len = strlen(tag) + 10; char endtag[endtag_len]; snprintf(endtag, endtag_len, "", tag); lex_new('(', 0); lex_new(ID, strdup(tag)); spl_lex_string(endtag, flags, indenting_delim); lex_new(')', 0); return; } if ( !strcmp(tag, "comment") ) { char *end = strstr(input, ""); if (!end) { my_asprintf(&lex_errstr, "missing ."); longjmp(lex_goterr, __LINE__); } input = end + strlen(""); lex_new(VALUE, strdup("")); return; } if ( !strcmp(tag, "inline") ) { lbstack_push(); lex_new(INSERT_PROG_BEGIN, 0); lex_new(';', 0); spl_lex_prog(""); input += strlen(""); lex_new(INSERT_PROG_END, 0); lex_new(VALUE, strdup("undef")); lex_new(';', 0); lbstack_pop(); return; } if ( !strcmp(tag, "code") ) { if ( !attr_code ) { lex_new(EF_BEGIN, 0); spl_lex_prog(""); input += strlen(""); lex_new(EF_END, 0); } else { lex_new(EF_BEGIN, 0); lex_new(VAR, 0); lex_new(ID, strdup("_data_")); lex_new('=', 0); spl_lex_string("", flags, indenting_delim); lex_new(';', 0); lex_new(RETURN, 0); spl_lex_prog_string(strdup(attr_code)); lex_new(';', 0); lex_new(EF_END, 0); } return; } if ( !strcmp(tag, "var") ) { if ( !attr_var ) { my_asprintf(&lex_errstr, " requires attribute 'var'."); longjmp(lex_goterr, __LINE__); } lex_new(EF_BEGIN, 0); char *var_name_dup; my_asprintf(&var_name_dup, "[*].%s", attr_var); if ( !attr_code ) { lex_new(VAR, 0); spl_lex_prog_string(var_name_dup); lex_new('=', 0); spl_lex_string("", flags, indenting_delim); lex_new(';', 0); } else { lex_new(VAR, 0); lex_new(ID, strdup("_data_")); lex_new('=', 0); spl_lex_string("", flags, indenting_delim); lex_new(';', 0); lex_new(VAR, 0); spl_lex_prog_string(var_name_dup); lex_new('=', 0); spl_lex_prog_string(strdup(attr_code)); lex_new(';', 0); } lex_new(EF_END, 0); return; } if ( !strcmp(tag, "if") ) { if ( !attr_code ) { my_asprintf(&lex_errstr, " requires attribute 'code'."); longjmp(lex_goterr, __LINE__); } lex_new('(', 0); spl_lex_prog_string(strdup(attr_code)); lex_new('?', 0); spl_lex_string("", flags, indenting_delim); lex_new(':', 0); if (spl_lex_findelsetag()) spl_lex_string("", flags, indenting_delim); else lex_new(VALUE, strdup("")); lex_new(')', 0); return; } if ( !strcmp(tag, "foreach") ) { if ( !attr_var || !attr_list ) { my_asprintf(&lex_errstr, " requires attributes 'var' and 'list'."); longjmp(lex_goterr, __LINE__); } lex_new(EF_BEGIN, 0); lex_new(VAR, 0); lex_new(ID, strdup("#iret")); lex_new(';', 0); lex_new(FOREACH, 0); spl_lex_prog_string(strdup(attr_var)); lex_new('(', 0); spl_lex_prog_string(strdup(attr_list)); lex_new(')', 0); lex_new(ID, strdup("#iret")); lex_new('=', 0); lex_new(ID, strdup("#iret")); lex_new(CAT, 0); spl_lex_string("", flags, indenting_delim); lex_new(';', 0); lex_new(RETURN, 0); lex_new(ID, strdup("#iret")); lex_new(';', 0); lex_new(EF_END, 0); return; } if ( !strcmp(tag, "indent") ) { lex_new('(', 0); spl_lex_string("", flags, attr_char ? *attr_char : 0); lex_new(')', 0); return; } if ( !strcmp(tag, "else") ) { my_asprintf(&lex_errstr, "Found tag without prior or tag."); longjmp(lex_goterr, __LINE__); } my_asprintf(&lex_errstr, "Unknown tag: %s", tag); longjmp(lex_goterr, __LINE__); } static char *strndup_with_indenting_delim(const char *str, int len, int indenting_delim, int *id_status) { if (!indenting_delim) return my_strndup(str, len); char *text = malloc(len + 1); int i=0, j=0; while (str[i] && i < len) { if (!*id_status) { if (!strchr(" \t\n\r", str[i])) { if (str[i] == indenting_delim) { if (str[++i] == ' ') i++; } *id_status = 1; continue; } } else { if (str[i] == '\n') *id_status = 0; text[j++] = str[i]; } i++; } text[j++] = 0; return realloc(text, j); } static void spl_lex_string(char *term, int flags, int indenting_delim) { int len, tlen = term ? strlen(term) : 0; int old_php_like_tags_active; int old_php_like_tags_indenting_delim; int old_lex_translate_count; struct lex_item *old_lex_translate_ptr; int called_in_php_like_tags_context = 0; int term_is_newline = 0; int id_status = 0; old_lex_translate_count = lex_translate_count; old_lex_translate_ptr = lex_translate_ptr; if (lex_item_list_last && lex_item_list_last->lex == ID) { char *translate_id = lex_item_list_last->text; int translate_id_len = strlen(translate_id); if (!strcmp(translate_id, "_")) { translate_id = 0; } else { if (translate_id_len < 3 || translate_id[0] != '_' || translate_id[translate_id_len-1] != '_') goto not_a_translate_call; translate_id = my_strndup(translate_id+1, translate_id_len-2); } lex_translate_ptr = lex_item_list_last; lex_item_list_last->lex = TRANSLATE_PREFIX; free(lex_item_list_last->text); lex_item_list_last->text = 0; flags &= ~LEX_STRING_SPLTAGS; lex_new(TRANSLATE_SEPERATOR, 0); if (translate_id) lex_new(VALUE, translate_id); else lex_new(UNDEF, 0); not_a_translate_call:; } if ( term && !strcmp(term, "\n") ) { term_is_newline = 1; tlen = 0; } old_php_like_tags_active = php_like_tags_active; old_php_like_tags_indenting_delim = php_like_tags_indenting_delim; php_like_tags_active = 0; if ( term && !strcmp(term, "") ) { php_like_tags_active = 1; called_in_php_like_tags_context = 1; indenting_delim = php_like_tags_indenting_delim; term = php_like_tags_term; tlen = term ? strlen(term) : 0; lex_new(ID, strdup("#tpldata")); lex_new(SAPPEND, 0); } if (!called_in_php_like_tags_context && (flags & LEX_STRING_SPLTAGS)) { lbstack_push(); lex_new(RVALUE_CONTEXT_BEGIN, 0); } for (len = 0; input[len]; len++) { if ( flags & (LEX_STRING_BACKSLASHES|LEX_STRING_REGEX) ) { if ( input[len] == '\\' && input[len+1] ) { len++; continue; } } if ( term ) { if ( term_is_newline ) { if (input[len] == '\r' || input[len] == '\n') { if (input[len] == '\r') len++; if (input[len] == '\n') len++; break; } } else if ( !strncmp(input+len, term, tlen) ) break; } if ( flags & LEX_STRING_SPLTAGS ) { if ( !strncmp(input+len, ""); input += 2; lex_new(ID, strdup("#tpldata")); lex_new(SAPPEND, 0); len = -1; continue; } } if ( input[len] == '$' && (!(flags & LEX_STRING_REGEX) || isalpha((unsigned char)input[len+1]) || strchr("_({$:[", input[len+1])) ) { spl_lex_new_string(strndup_with_indenting_delim(input, len, indenting_delim, &id_status), flags); input += len + 1; if (lex_translate_ptr) { char *translate_token; my_asprintf(&translate_token, "{%d}", lex_translate_count++); lex_new_translate(CAT, 0); lex_new_translate(VALUE, translate_token); lex_new(TRANSLATE_SEPERATOR, 0); } else lex_new(CAT, 0); switch ( *input ) { case '(': lex_new(EF_BEGIN, 0); input++; spl_lex_prog(")"); lex_new(EF_END, 0); input++; break; case '{': lex_new('(', 0); input++; spl_lex_prog("}"); lex_new(')', 0); input++; break; case '@': case '#': case ']': { char prog[3] = { '$', *input, 0 }; spl_lex_prog_string(strdup(prog)); input++; break; } case '$': case ':': case '?': len = 0; goto skip_2nd_cat_op; case ' ': case '\t': case '\r': case '\n': len = -1; while (*input == ' ' || *input == '\t') input++; if (*input == '\r') input++; if (*input == '\n') input++; goto skip_2nd_cat_op; case '[': len = -1; while (*input && *input != ']') input++; if (*input == ']') input++; goto skip_2nd_cat_op; case '<': { char *regex_result_id; int inner_len = strcspn(++input, ">"); my_asprintf(®ex_result_id, "#reres.%.*s", inner_len, input); lex_new(ID, regex_result_id); input += inner_len; if (*input == '>') input++; break; } case '-': case '+': case '0' ... '9': { char *regex_result_id; int inner_len = 1; /* strspn(input, "0123456789"); */ if (*input == '-' || *input == '+') my_asprintf(®ex_result_id, "#reres.%s", *input == '-' ? "=NC" : "=LC"); else my_asprintf(®ex_result_id, "#reres.%.*s", inner_len, input); lex_new(ID, regex_result_id); input += inner_len; break; } case 'a' ... 'z': case 'A' ... 'Z': case '_': { int inner_len = 0; do inner_len++; while ( (input[inner_len] >= 'a' && input[inner_len] <= 'z') || (input[inner_len] >= 'A' && input[inner_len] <= 'Z') || (input[inner_len] >= '0' && input[inner_len] <= '9') || input[inner_len] == '_' || input[inner_len] == '.'); while (input[inner_len-1] == '.') inner_len--; lex_new('(', 0); spl_lex_prog_string(my_strndup(input, inner_len)); lex_new(')', 0); input += inner_len; break; } default: my_asprintf(&lex_errstr, "Unrecognised $ substitution in string."); longjmp(lex_goterr, __LINE__); break; } lex_new_translate(CAT, 0); len = -1; skip_2nd_cat_op:; } } spl_lex_new_string(strndup_with_indenting_delim(input, len, indenting_delim, &id_status), flags); input += len + tlen; if (php_like_tags_active) { lex_new(';', 0); lex_new(INSERT_PROG_END, 0); lex_new(VALUE, strdup("push \"#tpldata\"")); lex_new(';', 0); } if (!called_in_php_like_tags_context && (flags & LEX_STRING_SPLTAGS)) { lex_new(RVALUE_CONTEXT_END, 0); lbstack_pop(); } if (lex_translate_ptr && !old_lex_translate_ptr) { lex_new(TRANSLATE_END, 0); lex_translate_count = old_lex_translate_count; lex_translate_ptr = old_lex_translate_ptr; } php_like_tags_active = old_php_like_tags_active; php_like_tags_indenting_delim = old_php_like_tags_indenting_delim; } static void spl_lex_prog_string(char *text) { const char *oldinput = input; struct lex_srcfile *oldsrc = lex_srcfile_current; lex_srcfile_current = malloc(sizeof(struct lex_srcfile)); lex_srcfile_current->text = input = text; my_asprintf((char**)&lex_srcfile_current->name, "%s:byte(%d)", oldsrc->name, (int)(oldinput - oldsrc->text)); lex_srcfile_current->next = lex_srcfile_list; lex_srcfile_list = lex_srcfile_current; spl_lex_prog(0); lex_srcfile_current = oldsrc; input = oldinput; } static void spl_lex_prog(char *term) { int tlen = term ? strlen(term) : 0; int last_was_id = 0; struct lex_item *old_lex_translate_ptr = lex_translate_ptr; int old_lex_translate_count = lex_translate_count; lex_translate_ptr = 0; lex_translate_count = 0; next_symbol: if ( last_was_id > 0 ) last_was_id--; input += strspn(input, " \t\r\n"); switch (*input) { case 0: if (!term) goto lex_prog_return; my_asprintf(&lex_errstr, "Unexpected end-of-file (expected '%s').", term); longjmp(lex_goterr, __LINE__); case '"': case '\'': { char *new_term = my_strndupa(input++, 1); spl_lex_string(new_term, LEX_STRING_BACKSLASHES, 0); goto next_symbol; } case '#': if ((input[1] < 'a' || input[1] > 'z') && input[1] != '!') goto just_a_normal_operator; input++; spl_lex_pragma(); goto next_symbol; case '.': if (input[1] < '0' || input[1] > '9') goto just_a_normal_operator; case '0' ... '9': if (input[0] == '0' && input[1] == 'x') { char *buffer; my_asprintf(&buffer, "%ld", strtol(input+2, (char**)&input, 16)); lex_new(VALUE, buffer); } else if (input[0] == '0' && input[1] == 'o') { char *buffer; my_asprintf(&buffer, "%ld", strtol(input+2, (char**)&input, 8)); lex_new(VALUE, buffer); } else if (input[0] == '0' && input[1] == 'b') { char *buffer; my_asprintf(&buffer, "%ld", strtol(input+2, (char**)&input, 2)); lex_new(VALUE, buffer); } else { int len = strspn(input, "0123456789."); while (input[len-1] == '.') len--; lex_new(VALUE, my_strndup(input, len)); input += len; } goto next_symbol; case '$': { char *regex_result_id; int len = 0; switch (input[1]) { case '<': input += 2; len = strcspn(input, ">"); my_asprintf(®ex_result_id, "#reres.%.*s", len++, input); break; case '$': len = 2; regex_result_id = strdup("#reres"); break; case '-': len = 2; regex_result_id = strdup("#reres.=NC"); break; case '+': len = 2; regex_result_id = strdup("#reres.=LC"); break; case '0' ... '9': input += 1; len = 1; /* strspn(input, "0123456789"); */ my_asprintf(®ex_result_id, "#reres.%.*s", len, input); break; case '@': len = 2; regex_result_id = strdup("#array"); break; case '#': len = 2; regex_result_id = strdup("#index"); break; case '[': case ']': len = 2; lex_new('(', 0); lex_new(LNOT, 0); lex_new(DEFINED, 0); lex_new('(', 0); if (input[1] == '[') lex_new(PREV, 0); else lex_new(NEXT, 0); lex_new(ID, strdup("#array")); lex_new(',', 0); lex_new(ID, strdup("#index")); lex_new(')', 0); lex_new(')', 0); goto ignore_regex_result_id; default: goto just_a_normal_operator; } lex_new(ID, regex_result_id); ignore_regex_result_id: input += len; last_was_id = 2; goto next_symbol; } case 'a' ... 'z': case 'A' ... 'Z': case '_': { int len = 0; while (1) { if (input[len] >= 'a' && input[len] <= 'z') len++; else if (input[len] >= 'A' && input[len] <= 'Z') len++; else if (input[len] >= '0' && input[len] <= '9') len++; else if (input[len] == '_') len++; else break; } char *text = my_strndup(input, len); input += len; if (!lex_item_list_last || lex_item_list_last->lex != '.' ) for (int i=0; lex_keywords[i].text; i++) if ( !strcmp(lex_keywords[i].text, text) ) { lex_new(lex_keywords[i].lex, 0); free(text); goto next_symbol; } if (!in_pragma_arg) for (struct define *def = define_list; def; def=def->next) { if ((!def->active || strcmp(current_define, def->name)) && !strcmp(def->name, text)) { char *expanded_text = strdup(def->text); free(text); if (define_recursion > 1000) { my_asprintf(&lex_errstr, "Endless recursion in macro expansion."); longjmp(lex_goterr, __LINE__); } struct define *args_start = 0; struct define *args_stop = 0; if (def->args) { char *this_args = strdup(def->args); char *this_arg, *this_args_p = this_args; input += strcspn(input, "("); if (*input) input++; this_arg = my_strsep(&this_args_p, ","); while (this_arg) { struct define *adef = calloc(1, sizeof(struct define)); adef->isarg = 1; adef->name = strdup(this_arg); in_pragma_arg=1; const char *begin = input; spl_lex_prog(this_args_p ? "," : ")"); adef->text=my_strndup(begin, input-begin); if (*input) input++; in_pragma_arg=0; adef->next = define_list; define_list = args_start = adef; if (!args_stop) args_stop = adef; this_arg = my_strsep(&this_args_p, ","); } free(this_args); int new_expanded_len = 1; for (int i=0; expanded_text[i]; i++) { if (expanded_text[i] == '<') { struct define *idef = args_start; while (idef) { if (!strncmp(expanded_text+i+1, idef->name, strlen(idef->name)) && expanded_text[i+1+strlen(idef->name)] == '>') { new_expanded_len += strlen(idef->text); i += strlen(idef->name) + 1; break; } if (idef == args_stop) break; idef = idef->next; } } new_expanded_len++; } char *new_expanded_text = malloc(new_expanded_len); int new_expanded_text_pos = 0; for (int i=0; expanded_text[i]; i++) { if (expanded_text[i] == '<') { struct define *idef = args_start; while (idef) { if (!strncmp(expanded_text+i+1, idef->name, strlen(idef->name)) && expanded_text[i+1+strlen(idef->name)] == '>') { strcpy(new_expanded_text+new_expanded_text_pos, idef->text); new_expanded_text_pos += strlen(idef->text); i += strlen(idef->name) + 1; goto do_not_copy_char; } if (idef == args_stop) break; idef = idef->next; } } new_expanded_text[new_expanded_text_pos++] = expanded_text[i]; do_not_copy_char:; } free(expanded_text); new_expanded_text[new_expanded_text_pos] = 0; expanded_text = new_expanded_text; } def->active = 1; define_recursion++; char *old_current_define = current_define; current_define = def->name; spl_lex_prog_string(expanded_text); current_define = old_current_define; define_recursion--; def->active = 0; struct define *idef = define_list; struct define **lastp = &define_list; while (idef) { if (idef == args_start) { *lastp = idef->next; free(idef->name); free(idef->text); if (idef->args) free(idef->args); free(idef); if (idef == args_stop) break; idef = args_start = *lastp; } else { lastp = &idef->next; idef = idef->next; } } goto next_symbol; } } lex_new(ID, text); last_was_id = 2; goto next_symbol; } case ']': lex_new(']', 0); input++; last_was_id = 2; goto next_symbol; case '}': case ')': if ( term && !strncmp(input, term, tlen) ) goto lex_prog_return; my_asprintf(&lex_errstr, "Unexpected '%c'.", *input); longjmp(lex_goterr, __LINE__); case '{': if (input[1] == '[') { lex_new(BLK_BEGIN_NOCTX, 0); input += 2; } else { lex_new('{', 0); input++; } spl_lex_prog("}"); lex_new('}', 0); input++; goto next_symbol; case '(': for (int i=0; lex_ops[i].text; i++) if ( !strncmp(input, lex_ops[i].text, strlen(lex_ops[i].text)) ) goto just_a_normal_operator; if ( input[1] == '{' ) { lex_new(EF_BEGIN, 0); input+=2; spl_lex_prog("})"); lex_new(EF_END, 0); input+=2; } else { if ( lex_item_list_last && lex_item_list_last->lex == ID ) { if ( lex_item_list_last->prev && lex_item_list_last->prev->lex != '.' && lex_item_list_last->prev->lex != ']' && lex_item_list_last->prev->lex != '&' && lex_item_list_last->prev->lex != '$' && lex_item_list_last->prev->lex != MUL && lex_item_list_last->prev->lex != FUNCTION && lex_item_list_last->prev->lex != METHOD && lex_item_list_last->prev->lex != FOREACH && lex_item_list_last->prev->lex != NEW ) lex_item_list_last->lex = FUNC_ID; if ( !lex_item_list_last->prev ) lex_item_list_last->lex = FUNC_ID; } lex_new('(', 0); input++; spl_lex_prog(")"); lex_new(')', 0); input++; } goto next_symbol; case '=': case '!': { if (input[1] != '~') goto just_a_normal_operator; int not_mode = *input == '!'; int eval_mode = 0; int len = 0; input += 2; input += strspn(input, " \t\r\n"); static char seplist[] = "/:,!%@"; char sepstr[] = "X"; struct lex_item *subst_lex = 0; if ( input[0] == 's' && strchr(seplist, input[1]) ) { sepstr[0] = input[1]; subst_lex = lex_new(not_mode ? NREGEX_SUBST : REGEX_SUBST, 0); input += 2; spl_lex_string(sepstr, LEX_STRING_REGEX, 0); lex_new(REGEX_SEP, 0); spl_lex_string(sepstr, LEX_STRING_REGEX, 0); lex_new(REGEX_SEP, 0); } else if ( input[0] == 'e' && strchr(seplist, input[1]) ) { eval_mode = 1; sepstr[0] = input[1]; subst_lex = lex_new(not_mode ? NREGEX_EVAL : REGEX_EVAL, 0); input += 2; spl_lex_string(sepstr, LEX_STRING_REGEX, 0); lex_new(REGEX_SEP, 0); } else if ( strchr(seplist, input[0]) ) { sepstr[0] = input[0]; lex_new(not_mode ? NREGEX : REGEX, 0); input++; spl_lex_string(sepstr, LEX_STRING_REGEX, 0); lex_new(REGEX_SEP, 0); } else { my_asprintf(&lex_errstr, "Error in regex syntax."); longjmp(lex_goterr, __LINE__); } while ( isalpha((unsigned char)input[len]) ) len++; char *mod = my_strndup(input, len); if (subst_lex && strchr(mod, 'R')) { if (subst_lex->lex == REGEX_SUBST) subst_lex->lex = REGEX_SUBST_R; if (subst_lex->lex == NREGEX_SUBST) subst_lex->lex = NREGEX_SUBST_R; if (subst_lex->lex == REGEX_EVAL) subst_lex->lex = REGEX_EVAL_R; if (subst_lex->lex == NREGEX_EVAL) subst_lex->lex = NREGEX_EVAL_R; } if (eval_mode) { char *new_mod; if (strchr(mod, 'N') || strchr(mod, 'P') || strchr(mod, 'A') || strchr(mod, 'I') || strchr(mod, 'E') || strchr(mod, 'L')) { my_asprintf(&lex_errstr, "Error in regex syntax."); longjmp(lex_goterr, __LINE__); } my_asprintf(&new_mod, "%sEANP", mod); free(mod); mod = new_mod; } lex_new(VALUE, mod); input += len; lex_new(REGEX_SEP, 0); goto next_symbol; } case '/': if (input[1] == '/') { while (*input && *input != '\r' && *input != '\n') input++; goto next_symbol; } if (input[1] == '*') { while (input[0] && input[1] && (input[0] != '*' || input[1] != '/')) input++; if (*input) input++; if (*input) input++; goto next_symbol; } goto just_a_normal_operator; case '<': { int i = 1, with_indent = 0; if (input[i] == ':') { with_indent = 1; i++; } while (isalpha((unsigned char)input[i])) i++; if (input[i] == '>') { char endtag[i+32]; snprintf(endtag, i+32, "", i-(1+with_indent), input+1+with_indent); input += i+1; if (with_indent) input += strspn(input, " \t\r\n"); spl_lex_string(endtag, LEX_STRING_SPLTAGS, with_indent ? ':' : 0); goto next_symbol; } goto just_a_normal_operator; } case '?': if (php_like_tags_active && input[1] == '>') { if (!strcmp(term, "?>")) goto lex_prog_return; input += 2; spl_lex_string("", LEX_STRING_SPLTAGS, 0); goto next_symbol; } goto just_a_normal_operator; just_a_normal_operator: default: { if ( term && !strncmp(input, term, tlen) ) goto lex_prog_return; for (int i=0; lex_ops[i].text; i++) if ( !strncmp(input, lex_ops[i].text, strlen(lex_ops[i].text)) ) { input += strlen(lex_ops[i].text); switch ( lex_ops[i].lex ) { case STRING_EOL: { if ( *input == ' ' || *input == '\t' ) input++; int len = strcspn(input, "\r\n"); if ( input[len] == '\r' ) len++; if ( input[len] == '\n' ) len++; lex_new(VALUE, my_strndup(input, len)); input += len; goto next_symbol; } case STRING_EOL_S: { if ( *input == ' ' || *input == '\t' ) input++; spl_lex_string("\n", LEX_STRING_BACKSLASHES, 0); goto next_symbol; } case STRING_LABEL: { input += strspn(input, " \t\r\n"); int len = strspn(input, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); char *label = my_strndupa(input, len); char indenting_delim = 0; input += len; if ( *input == ' ' || *input == '\t' || *input == '\r' || *input == '\n' ) input++; else { indenting_delim = *(input++); if ( *input == ' ' || *input == '\t' || *input == '\r' || *input == '\n' ) input++; } char *end = strstr(input, label); if (!end) { my_asprintf(&lex_errstr, "End label missing."); longjmp(lex_goterr, __LINE__); } int id_status = 0; lex_new(VALUE, strndup_with_indenting_delim(input, end-input, indenting_delim, &id_status)); input = end + len; goto next_symbol; } case STRING_LABEL_S: { input += strspn(input, " \t\r\n"); int len = strspn(input, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); char *label = my_strndupa(input, len); char indenting_delim = 0; input += len; if ( *input == ' ' || *input == '\t' || *input == '\r' || *input == '\n' ) input++; else { indenting_delim = *(input++); if ( *input == ' ' || *input == '\t' || *input == '\r' || *input == '\n' ) input++; } spl_lex_string(label, LEX_STRING_BACKSLASHES, indenting_delim); goto next_symbol; } default: if (lex_ops[i].lex == SPECIALREF && lex_item_list_last && (lex_item_list_last->lex == ID || lex_item_list_last->lex == ']' || lex_item_list_last->lex == SPECIALREF)) lex_new('.', 0); if (lex_ops[i].lex == SPECIALREF) { char *lookupcode = ""; if (input[-2] == '*') lookupcode = ""; if (input[-2] == '+') lookupcode = "!CLS"; if (input[-2] == '/') lookupcode = "!ROOT"; if (input[-2] == '.') lookupcode = "!THIS"; lex_new(lex_ops[i].lex, strdup(lookupcode)); } else lex_new(lex_ops[i].lex, 0); goto next_symbol; } } if (*input == '[' && lex_item_list_last && (lex_item_list_last->lex == ID || lex_item_list_last->lex == ')' || lex_item_list_last->lex == ']' || lex_item_list_last->lex == SPECIALREF)) lex_new('.', 0); lex_new(*(input++), 0); goto next_symbol; } } longjmp(lex_goterr, __LINE__); lex_prog_return: lex_translate_ptr = old_lex_translate_ptr; lex_translate_count = old_lex_translate_count; } static char *last_debug_info; static void create_debug_op(int force) { char *newinfo; if (!gen_debug_info) return; int lineno = 1, charno = 1; const char *c = lex_last_item->src->text; while (c < lex_last_item->pos) if (*(c++) == '\n' ) lineno++, charno=1; else charno++; my_asprintf(&newinfo, "%d:%d:%s", lineno, charno, lex_last_item->src->name); if (!force && last_debug_info && !strcmp(last_debug_info, newinfo)) { free(newinfo); return; } if (last_debug_info) free(last_debug_info); last_debug_info = newinfo; spl_asm_add(as, SPL_OP_DBGSYM, newinfo); } static char *get_encoding_pragma(const char *text) { char *token = "#encoding "; int token_size = strlen(token); const char *begin = strstr(text, token); if ( !begin ) return 0; begin += token_size; token_size = strcspn(begin, " \r\n\t"); if ( token_size <= 0 ) return 0; return my_strndup(begin, token_size); } extern int spl_compiler(struct spl_asm *a, const char *prog, const char *name, spl_malloc_file_function *mff, int gd) { #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_lock(&compiler_lck); #endif as = a; malloc_file_func = mff; gen_debug_info = gd; no_checkp_insn = 0; php_like_tags_active = 0; php_like_tags_term = 0; define_list = 0; current_define = ""; define_recursion = 0; in_pragma_arg = 0; rvtolv_counter = 0; import_asm_label_counter = 0; if ( as->labels ) { spl_report(SPL_REPORT_COMPILER, as, "Compiler called with an assembler with non-empty label list!\n"); #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_unlock(&compiler_lck); #endif return -1; } encoding = get_encoding_pragma(prog); if (encoding) { prog = spl_utf8_import(prog, encoding); if (!prog) { free(encoding); spl_report(SPL_REPORT_COMPILER, as, "Character set (#encoding) '%s' is unknown!\n", encoding); #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_unlock(&compiler_lck); #endif return -1; } } if ( spl_utf8_check(prog) ) { spl_report(SPL_REPORT_COMPILER, as, "Compiler called with non-utf8 encoded program code!\n" "A decoder bug or just a missing '#encoding' pragma?\n"); if (encoding) { free(encoding); free((char*)prog); } #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_unlock(&compiler_lck); #endif return -1; } input = prog; lex_srcfile_current = malloc(sizeof(struct lex_srcfile)); lex_srcfile_current->next = 0; lex_srcfile_current->text = input; lex_srcfile_current->name = name; lex_srcfile_list = lex_srcfile_current; lex_item_list_first = 0; lex_item_list_last = 0; lex_last_item = 0; lex_errstr = strdup("Generic lexer error"); label_stack_index = 0; label_stack_counter = 0; label_stack[label_stack_index] = label_stack_counter; breakcont_stack_index = 0; breakcont_stack_counter = 0; breakcont_stack[breakcont_stack_index] = breakcont_stack_counter; packpatch_stack_index = 0; last_debug_info = 0; // the 'volatile' is to make the compiler happy. // (a volatile variable can't be clobbered by a 'longjmp') volatile int ret = 1; int errlnr = 0; // this is to make the compiler happy. // (the new prog_free_ptr can't be clobbered by a 'longjmp') void * volatile prog_free_ptr = (void *)prog; if ( !(errlnr = setjmp(lex_goterr)) ) { spl_lex_prog(0); ret = spl_yyparse (); } else { int lineno = 1, charno = 1; const char *c = lex_srcfile_current->text; const char *orig_input = c; while (c < input) if (*(c++) == '\n' ) lineno++, charno=1; else charno++; char *code; my_asprintf(&code, "%.40s", input-30 > orig_input ? input-30 : orig_input); for (int i=0; code[i]; i++) if ( *input == '\r' || code[i] == '\n' || code[i] == '\t' ) code[i] = ' '; spl_report(SPL_REPORT_LEXER, as, "[%d] near line %d, char %d in %s: %s\n>> %s\n %*s^\n", errlnr, lineno, charno, lex_srcfile_current->name, lex_errstr, code, 30, ""); free(code); } if ( !ret && packpatch_stack_index ) { spl_yyerror("Parser returned with non-empty packpatch stack!"); ret = 1; } if ( !ret && label_stack_index ) { spl_yyerror("Parser returned with non-empty label stack!"); ret = 1; } while ( define_list ) { struct define *next = define_list->next; free(define_list->name); free(define_list->text); if (define_list->args) free(define_list->args); free(define_list); define_list = next; } while ( lex_srcfile_list ) { struct lex_srcfile *next = lex_srcfile_list->next; if (next) { // we allocated those, so we can cast them // to non-const and free them. free((char*)lex_srcfile_list->text); free((char*)lex_srcfile_list->name); } free(lex_srcfile_list); lex_srcfile_list = next; } if (last_debug_info) free(last_debug_info); if (lex_last_item) free(lex_last_item); free(lex_errstr); spl_asm_resolve_labels(as); if (encoding) { free(encoding); free(prog_free_ptr); } #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_unlock(&compiler_lck); #endif return ret; } static void spl_yyerror (char const *err) { int lineno = 1, charno = 1; const char *c = lex_last_item->src->text; const char *orig_input = c; while (c < lex_last_item->pos) if (*(c++) == '\n' ) lineno++, charno=1; else charno++; char *code; my_asprintf(&code, "%.40s", lex_last_item->pos-30 > orig_input ? lex_last_item->pos-30 : orig_input); for (int i=0; code[i]; i++) if ( *input == '\r' || code[i] == '\n' || code[i] == '\t' ) code[i] = ' '; spl_report(SPL_REPORT_COMPILER, as, "near line %d, char %d in %s: %s\n>> %s\n %*s^\n", lineno, charno, lex_last_item->src->name, err, code, 30, ""); free(code); } spl-1.0pre6/qtdemo/0000755000000000000000000000000011300476113012730 5ustar rootrootspl-1.0pre6/qtdemo/qtdemo001.spl0000644000000000000000000000223410333176306015171 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * qtdemo001.spl: A simple Qt Hello World */ load "qt"; var a = new qt.QApplication(); var l = new qt.QLabel(undef); l.setText("Hello World!"); function click_callback(e) { debug "Click: ${e.x()} / ${e.y()}"; return 1; } qt_event_callback(l, click_callback, qt.QEvent.MouseButtonPress()); a.setMainWidget(l); l.show(); a.exec(); spl-1.0pre6/qtdemo/qtdemo002.ui0000644000000000000000000000465510335376146015030 0ustar rootroot Form1 Form1 0 0 562 202 A simple SPL/Qt Demo unnamed textEdit1 layout1 unnamed pushButton1 1 0 4 0 Click Me! pushButton2 1 0 1 0 Exit spl-1.0pre6/qtdemo/qtdemo002.spl0000644000000000000000000000450010333624641015170 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * qtdemo002.spl: A more advanced example for the Qt bindings */ load "qt"; var output_lines = <:> : This program is free software; you can redistribute it and/or modify : it under the terms of the GNU General Public License as published by : the Free Software Foundation; either version 2 of the License, or : (at your option) any later version. : --- : This program is distributed in the hope that it will be useful, : but WITHOUT ANY WARRANTY; without even the implied warranty of : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the : GNU General Public License for more details. : --- : You should have received a copy of the GNU General Public License : along with this program; if not, write to the Free Software : Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 US =~ /[^\n]*\n?/Ag;; var a = new qt.QApplication(); var d = qt_ui("qtdemo002.ui"); var b = qt_child(d, "pushButton1", "QPushButton", 1); var q = qt_child(d, "pushButton2", "QPushButton", 1); var t = qt_child(d, "textEdit1", "QTextEdit", 1); qt_event_callback(b, function(e) { if (e.type() == e.MouseButtonPress() || e.type() == e.MouseButtonDblClick()) { if (elementsof output_lines > 0) t.append(shift output_lines); b.setText("Thanks for clicking the button!"); return 1; } if (e.type() == e.MouseButtonRelease()) { b.setText("Click Me Again!"); return 1; } return 0; }); qt_connect(q, "clicked()", a, "quit()"); a.setMainWidget(d); d.show(); a.exec(); write(t.text()); spl-1.0pre6/qtdemo/qtdemo003.ui0000644000000000000000000000575410334060103015010 0ustar rootroot win win 0 0 782 423 Tiny XML Viewer (Another SPL/Qt Demo) unnamed XML Tree true true Node Type true true XPath true true tree layout2 unnamed edit true closeButton Close closeButton clicked() win close() spl-1.0pre6/qtdemo/qtdemo003.spl0000644000000000000000000000377010336066510015177 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * qtdemo003.spl: A Tiny XML Viewer */ load "qt"; load "xml"; load "file"; var xd = xml_parse(file_read("qtdemo003.ui")); var item_to_node; var qapp = new qt.QApplication(); var win = qt_ui("qtdemo003.ui"); function set_tree(list, node, insertafter) { var item = new qt.QListViewItem(list, qt_as("QListViewItem*", insertafter), node["."].name, node["."].type, node["."].path); if (node["."].path =~ ,^/[^/]+(/[^/]+)?$,) item.setOpen(1); item_to_node[item.ptr] = node; var lastitem = undef; foreach[] nextnode (node["@*"].nodes) lastitem = set_tree(item, nextnode, lastitem); foreach[] nextnode (node["*"].nodes) lastitem = set_tree(item, nextnode, lastitem); return item; } var list = qt_child(win, "tree", "QListView", 1); list.setSortColumn(-1); set_tree(list, xd["/*"].node, undef); function clickonitem(item) { if (defined item and declared item_to_node[item.ptr]) { var textarea = qt_child(win, "edit", "QTextEdit", 1); var text = item_to_node[item.ptr]["."].xml; while (text !~ /\n[^ ]/ and text =~ s/\n /\n/g) {} textarea.setText(text); } } qt_signal_callback(list, "clicked(QListViewItem*)", clickonitem); qapp.setMainWidget(win); win.show(); qapp.exec(); spl-1.0pre6/qtdemo/qtdemo004.ui0000644000000000000000000000722310333624641015016 0ustar rootroot Window Window 0 0 517 412 Hanoi Assistant (Demo for SPL/Qt and co-routines) unnamed progress edit true button false Next Move MenuBar fileNewAction image0 New &New Ctrl+N fileExitAction Exit E&xit helpAboutAction About &About 89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b0000008449444154388ded953d0a80300c465fc5c1b1ab9ba315970e1ec10b78d09e46f03871d2a93f127110fa204ba02f1f2150f80813e9c98bb7374dac2922c90270b32b06888a4b78ef8bf2562376a3c3761680633f84c85a5489b775a31ffa6c7255e26999000884647295d898ec41e8c4d7659486a976fc842aaee22acef0e60779eafb0927db432aa6e3988ccc0000000049454e44ae426082 fileExitAction activated() Window close() spl-1.0pre6/qtdemo/qtdemo004.spl0000644000000000000000000000534310333624641015200 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * qtdemo004.spl: An example for Qt and co-routines: Hanoi Assistent */ load "qt"; load "task"; var qapp = new qt.QApplication(); var win = qt_ui("qtdemo004.ui"); function about() { var edit = qt_child(win, "edit", "QTextEdit", 1); edit.setBold(1); edit.append(<:> : -------------------------------------------------------------- : ** Hanoi Assistent ** : A little demo for using SPL co-routines in SPL Qt applications. : Written by Clifford Wolf . : -------------------------------------------------------------- ); } var hanoi_counter; function hanoi_worker(from, to, temp, level) { if (level > 1) hanoi_worker(from, temp, to, level-1); var bar = qt_child(win, "progress", "QProgressBar", 1); bar.setProgress(++hanoi_counter); task_co_return("#$hanoi_counter: Move slide from stack $from to stack $to."); if (level > 1) hanoi_worker(temp, to, from, level-1); } function hanoi_start() { hanoi_counter = 0; task_kill("hanoi"); task_create("hanoi", "hanoi_worker(1, 3, 2, 5);"); var butt = qt_child(win, "button", "QPushButton", 1); butt.setEnabled(1); var edit = qt_child(win, "edit", "QTextEdit", 1); edit.clear(); var bar = qt_child(win, "progress", "QProgressBar", 1); bar.setPercentageVisible(1); bar.setProgress(0, 31); } function hanoi_next() { var next_move_desc = task_co_call("hanoi"); var edit = qt_child(win, "edit", "QTextEdit", 1); edit.setBold(0); edit.append(next_move_desc ~ "\n"); if (hanoi_counter == 31) { edit.setBold(1); edit.append("-- FINISHED --\n"); var butt = qt_child(win, "button", "QPushButton", 1); butt.setEnabled(0); } } qt_signal_callback(win.child("helpAboutAction", undef, 1), "activated()", about); qt_signal_callback(win.child("fileNewAction", undef, 1), "activated()", hanoi_start); qt_signal_callback(win.child("button", undef, 1), "clicked()", hanoi_next); qapp.setMainWidget(win); win.show(); about(); qapp.exec(); spl-1.0pre6/qtdemo/qtdemo005.spl0000644000000000000000000000411010333624641015170 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * qtdemo005.spl: A demo for the QCanvas class */ load "qt"; var a = new qt.QApplication(); var c = new qt.QCanvas(300, 150); var cv = new qt.QCanvasView(c); var col, brush, e; col[0] = qt_autodelete(new qt.QColor(200, 100, 100)); col[1] = qt_autodelete(new qt.QColor(100, 50, 250)); col[2] = qt_autodelete(new qt.QColor(100, 100, 0)); col[3] = qt_autodelete(new qt.QColor(200, 0, 150)); col[4] = qt_autodelete(new qt.QColor( 50, 250, 0)); col[5] = qt_autodelete(new qt.QColor( 20, 100, 10)); e[0] = new qt.QCanvasEllipse(200, 100, 12*16, 90*16, c); e[1] = new qt.QCanvasEllipse(200, 100, 102*16, 70*16, c); e[2] = new qt.QCanvasEllipse(200, 100, 172*16, 100*16, c); e[3] = new qt.QCanvasEllipse(200, 100, 272*16, 58*16, c); e[4] = new qt.QCanvasEllipse(200, 100, 330*16, 42*16, c); for (var i=0; i<5; i++) { brush[i] = new qt.QBrush(col[i]); e[i].setBrush(brush[i]); e[i].setX(150); e[i].setY(75); e[i].show(); } c.setAdvancePeriod(20); c.setBackgroundColor(col[5]); a.setMainWidget(cv); cv.show(); a.exec(); // QBrush objects are not derived from QObject, so they are not deleted // automatically. We eighter need to do it manually or use qt_autodelete() // as we have done with the QColor objects in this example. foreach[] o (brush) qt_delete(o); spl-1.0pre6/qtdemo/qtdemo006.spl0000644000000000000000000000355210337405523015202 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * qtdemo006.spl: A simple demo for writing kde applications */ load "kde"; var sites = [ [ name: "The SPL Homepage", url: "http://www.clifford.at/spl/" ], [ name: "SPL Module Docs", url: "http://www.clifford.at/spl/spldoc/" ], [ name: "Qt Reference Docs", url: "http://doc.trolltech.com/" ], [ name: "KDE Reference Docs", url: "http://developer.kde.org/documentation/" ] ]; qt_kdeinit("simplebrowser", "Simple HTML browser application", "1.0"); var kapp = new qt.KApplication(); var win = new qt.QVBox(); win.resize(800, 600); var selector = new qt.QComboBox(win); foreach i (sites) selector.insertItem(sites[i].name, i); var browser = new qt.KHTMLPart(win); qt_signal_callback(selector, "activated(int)", function(i) { browser.openURL(new qt.KURL(sites[i].url)); }); qt_signal_callback(browser.browserExtension(), "openURLRequest(const KURL&,const KParts::URLArgs&)", function(url) { browser.openURL(new qt.KURL(url)); }); browser.openURL(new qt.KURL("http://www.clifford.at/spl/")); win.show(); kapp.setMainWidget(win); kapp.exec(); spl-1.0pre6/qtdemo/qtdemo007.spl0000644000000000000000000000307210437047443015204 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * qtdemo007.spl: A simple demo for GL apps with QT in SPL */ load "qt"; load "gl"; object MyGLWidget { method initializeGL() { glClearColor(0.0, 0.0, 0.0, 0.0); glColor3f(0.0, 0.0, 1.0); glMatrixMode(glEnum("GL_PROJECTION")); glLoadIdentity(); glOrtho(-10.0, 10.0, -10.0, 10.0, -10.0, 10.0); } method paintGL() { glClear(glEnum("GL_COLOR_BUFFER_BIT")); glRectf(-5.0, 5.0, 5.0, -5.0); } method init(@args) { var obj = new qt.QGLWidget(@args); qt_virtual_callback(obj, "initializeGL", initializeGL); qt_virtual_callback(obj, "paintGL", paintGL); return obj; } } var a = new qt.QApplication(); var win = new qt.QVBox(); win.resize(400, 400); var c = new MyGLWidget(win); a.setMainWidget(win); win.show(); a.exec(); spl-1.0pre6/gc.c0000644000000000000000000000720311064463357012213 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * gc.c: The SPL garbage collector */ #include #include "spl.h" static int gc_node(int gc_pass, struct spl_node *node) { int size = 1; if ( !node ) return 0; if ( gc_pass ) { if (node->dump_tag != 1) return 0; node->dump_tag = 2; } else { if (node->dump_tag == 1) return 0; node->dump_tag = 1; } size += gc_node(gc_pass, node->ctx); size += gc_node(gc_pass, node->cls); struct spl_node_sub *s = node->subs_begin; while (s) { size += gc_node(gc_pass, s->node); s = s->next; } return size; } static int gc_task(int gc_pass, struct spl_task *task) { int size = 1; size += gc_node(gc_pass, task->ctx); struct spl_node_stack *s = task->stack; while (s) { size += gc_node(gc_pass, s->node); s = s->next; } return size; } static int gc_vm(int gc_pass, struct spl_vm *vm) { int size = 1; size += gc_node(gc_pass, vm->root); struct spl_task *t = vm->task_list; while (t) { size += gc_task(gc_pass, t); t = t->next; } return size; } int spl_gc(struct spl_vm *vm) { struct spl_node *n; int old_free_counter = spl_state_counter_free_get(); vm->gc_last_ic = vm->ic; vm->gc_last_allsize = spl_state_counter_malloc_get() - spl_state_counter_free_get(); n = vm->gc_list; if (!n) return 0; int size = 0; for ((void)0; n; n = n->gc_right) size += gc_node(0, n); size += gc_vm(0, vm); gc_vm(1, vm); vm->gc_last_treesize = size; rerun_gc: (void)0; int i, count = 0; for (n = vm->gc_list; n; n=n->gc_right) if ( n->dump_tag == 1 ) count++; struct spl_node **list = malloc( sizeof(struct spl_node *) * count ); for (i=0, n = vm->gc_list; n; (void)0) { struct spl_node *nextn = n->gc_right; n->flags &= ~SPL_NODE_FLAG_GC; n->gc_left = n->gc_right = 0; if ( n->dump_tag == 1 ) { list[i++] = n; n->ref_counter++; } n = nextn; } vm->gc_list=0; for (i=0; idump_tag = 2; if ( n->ctx ) { spl_put(vm, n->ctx); n->ctx = 0; } if ( n->cls ) { spl_put(vm, n->cls); n->cls = 0; } struct spl_node_sub *s = n->subs_begin; while (s) { struct spl_node_sub *sn = s->next; spl_put(vm, s->node); if (s->module) free(s->module); free(s->key); free(s); s=sn; } if ( n->subs_hash ) free(n->subs_hash); n->subs_hash = 0; n->subs_hash_size = 0; n->subs_counter = 0; n->subs_begin = 0; n->subs_end = 0; spl_put(vm, n); } free(list); if (vm->gc_list) goto rerun_gc; //note: free counter may have changed in threaded environments; return spl_state_counter_free_get() - old_free_counter; } int spl_gc_maybe(struct spl_vm *vm) { int treesize = vm->gc_last_treesize < 100 ? 100 : vm->gc_last_treesize; if (vm->ic - vm->gc_last_ic > treesize * 128) return spl_gc(vm); if (spl_state_counter_malloc_get() - spl_state_counter_free_get() > vm->gc_last_allsize * 2) return spl_gc(vm); return -1; } spl-1.0pre6/spl-config.templ0000644000000000000000000000036710441545543014562 0ustar rootroot#!/bin/sh case "$*" in --cflags) echo @CFLAGS@ ;; --ldflags) echo @LDFLAGS@ ;; --ldlibs) echo @LDLIBS@ ;; --moddir) echo @MODDIR@ ;; *) echo "Usage: $0 { --cflags | --ldflags | --ldlibs | --moddir }" exit 1 esac exit 0 spl-1.0pre6/spldoc.spl0000644000000000000000000001451510437047443013463 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * spldoc.spl: Generate HTML from SPLDOC comments */ load "file"; load "encode_xml"; if (not declared parameters or not declared parameters.infile) { write("Need parameter 'infile' for input file!\n"); exit; } if (not declared parameters or not declared parameters.outdir) { write("Need parameter 'outdir' for output directory!\n"); exit; } var input_file = parameters.infile; var output_dir = parameters.outdir; var input; var module_name; while (input_file =~ /([^,]+)/) { module_name = $1; input ~= file_read($1); input_file =~ s/([^,]+)//; } input_file =~ s/,//g; module_name =~ s/^.*\/(mod_)//; module_name =~ s/\.(spl|c)$//; var html_part1; var html_part2; var ascii = module_name ~!= "builtins" ? 'Module "$module_name"\n' : 'Builtins Function Library\n'; ascii ~= ascii =~ s/[^\n]/=/Rg; var isfirst = 1; var current_object; var objectre = 'object|interface|namespace'; function text2html(input, currobj) { var text = xml::input; while (text =~ /\[\[([^\]]+)\]\]/) { var rawlinkdata = $1; var href, link; rawlinkdata =~ /(?P[^:]+:|)(?P[^\.]*\.|)(?P[A-Za-z0-9_]*)(?P.*)/I; if (d1 =~ /([^:]+)/) { href="$1.html#"; if ( "$d2$d3$d4" ~== "" ) link ~= '"$1"'; } else href = "${module_name}.html#"; if (d2 ~== ".") href ~= "$currobj."; else if (d2 =~ /([^\.]+)/) { href ~= d2; link ~= '$1' ~ '.'; } href ~= d3; link ~= '$d3'; if (d4 ~!= "") link ~= '$d4'; text =~ s/\[\[([^\]]+)\]\]/$link<\/b><\/a>/; } return text; } function text2ascii(input, currobj) { var text = input; while (text =~ /\[\[([^\]]+)\]\]/) { var rawlinkdata = $1; var href, link; rawlinkdata =~ /(?P[^:]+:|)(?P[^\.]*\.|)(?P[A-Za-z0-9_]*)(?P.*)/I; if (d1 =~ /([^:]+)/ and "$d2$d3$d4" ~== "" ) link ~= '"$1"'; if (d2 =~ /([^\.]+)/) link ~= '$1.'; link ~= d3; if (d4 ~!= "") link ~= '$d4'; text =~ s/\[\[([^\]]+)\]\]/$link/; } return text; } // extract intro var intro = "No documentation available for this module."; if (input =~ s/\/\*\*[ \t]*?\n(.*?)\*\///s) { var rawtext = $1 =~ s/^[ \t]*\* ?//Rmg; intro = text2html(rawtext, undef); ascii ~= "\n${text2ascii(rawtext, undef)}"; } // extract remaining comments var lasttype; while (input =~ s/\/\*\*[ \t]*?\n(?P.*?)\*\/[ \t]*\n[ \t\/]*(?P[^\n]+)//s) { import $$; // clear comment signs from text text =~ s/^[ \t]*\* ?//mg; var rawtext = text; // extract type and name from declaration decl =~ /^(?P\S+)\s+(?P([A-Za-z0-9_]+(,\s+)?)+)(?P[^;\{]*)/I; var aname = name =~ s/,\s+/_/Rg; // this is needed for creating links var current_object2 = current_object; // clear old object if this is a new one if (type =~ /^($objectre)$/) { if (defined current_object) html_part1 ~= "
\n}\n"; current_object = undef; current_object2 = name; } else rest ~= ";"; // convert text to html text = text2html(text, current_object2); // create 1st HTML snippet html_part1 ~= <> ${ isfirst ? '' : type =~ /^($objectre)$/ || (type ~!= lasttype && lasttype !~ /^($objectre)$/) ? '

$type ${ defined current_object ? '$current_object.' : '' }$name$rest

    ${ text =~ s/^\s*(.*?)\s*$/$1/Rs }

; // create text output var title = "$type ${ defined current_object ? '$current_object.' : ''}$name$rest"; ascii ~= <> $title ${ title =~ s/./${ defined current_object ? '~' : '-'}/Rg } ${text2ascii(rawtext, undef)} ; // set new object if (type =~ /^($objectre)$/) current_object = name; lasttype = type; isfirst = 0; } if (defined current_object) html_part1 ~= "
\n}\n"; html_part1 =~ s,\s+
\s+\{\s+
\s+\}, { },g; var html = <> ${ module_name ~!= "builtins" ? "SPL Module Reference: $module_name" : "SPL Builtins Function Library" }

${ module_name ~!= "builtins" ? "SPL Module Reference: $module_name" : "SPL Builtins Function Library" }


load "$module_name";
    ${ intro =~ s/^\s*(.*?)\s*$/$1/Rs }

$html_part1 $html_part2


Generated by SPLDOC. http://www.clifford.at/spl/
; file_write("${output_dir}/${module_name}.html", html); file_write("${output_dir}/${module_name}.txt", ascii); spl-1.0pre6/webspld.c0000644000000000000000000004244211064463357013266 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * webspld.c: A HTTP server (using httpsrv.c) with an empedded SPL engine */ #define TCP_PORT 3054 #include #include #include #include #include #include #include #include #include #include #include #include #ifdef ENABLE_PTHREAD_SUPPORT # include # include #endif #include "spl.h" #include "webspl_common.h" #include "compat.h" #define error() \ do { \ fprintf(stderr, "At %s:%d: %s\n", \ __FILE__, __LINE__, strerror(errno)); \ exit(1); \ } while(0) struct pool_entry { struct spl_vm *vm; char *session; time_t ping; #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_t lck; #endif }; static int pool_size = 32; static struct pool_entry *pool = 0; static char daemon_basedir[PATH_MAX] = "."; struct code_cache { char *filename; struct spl_code *code; struct code_cache_check *checks; time_t last_use; }; struct code_cache_check { char *filename; time_t cached_mtime; dev_t cached_dev; ino_t cached_ino; struct code_cache_check *next; }; static int code_cache_size = 32; struct code_cache *code_cache_list = 0; #ifdef ENABLE_PTHREAD_SUPPORT static pthread_mutex_t pool_lck = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t code_cache_lock = PTHREAD_MUTEX_INITIALIZER; static sem_t thread_pool_sem; static int threads = 1; #endif static FILE *profile_file = 0; static int profile_requests = 0; static int profile_create_new = 0; static int profile_resume_mem = 0; static int profile_resume_disk = 0; static int profile_compiler = 0; #ifdef ENABLE_PTHREAD_SUPPORT static pthread_mutex_t profile_lck = PTHREAD_MUTEX_INITIALIZER; #endif #ifdef ENABLE_PTHREAD_SUPPORT # define PROFILE_INC(__name) do { \ pthread_mutex_lock(&profile_lck); \ profile_ ## __name ++; \ pthread_mutex_unlock(&profile_lck); \ } while(0) #else # define PROFILE_INC(__name) do { profile_ ## __name ++; } while(0) #endif static volatile int got_sigint = 0; static int listenfd; static void sigint_handler(int dummy UNUSED); static void sigint_handler(int dummy UNUSED) { if (!got_sigint) { got_sigint = 1; printf("\nCaught SIGINT (Ctrl-C) or SIGTERM.\n"); } return; } static void help(int ret) { printf("\n"); printf("Usage: webspld [Options]\n"); printf("\n"); printf(" -d Daemonize (fork to background)\n"); printf(" -l logfile Write all output to this logfile\n"); printf(" -p prof-log Write profiling data to this logfile\n"); printf("\n"); printf(" -P tcp-port Listen on this TCP port (default=%d)\n", TCP_PORT); printf(" -A ip-address Listen on this IP address (default=INADDR_ANY)\n"); printf("\n"); printf(" -S size Set VM pool size (default=32)\n"); printf(" -C size Set code cache size (default=32)\n"); #ifdef ENABLE_PTHREAD_SUPPORT printf(" -T threads Number of threads (default=1)\n"); printf("\n"); printf("The number of threads must be less or equal the VM pool size!\n"); #endif printf("\n"); exit(ret); } static struct spl_vm *webspld_vm_pool_get(const char *session, int create) { #ifdef ENABLE_PTHREAD_SUPPORT retry_entry_point:; pthread_mutex_lock(&pool_lck); #endif int ses_len = strcspn(session, ":"); char *ses_id = my_strndupa(session, ses_len); int oldest_entry = 0; time_t oldest_entry_ping = pool[0].ping; for (int i=0; i retry in 1 second.\n", i); pthread_mutex_unlock(&pool_lck); my_sleep(1); goto retry_entry_point; } pthread_mutex_unlock(&pool_lck); #endif return pool[i].vm; } if (oldest_entry_ping > pool[i].ping) { oldest_entry_ping = pool[i].ping; oldest_entry = i; } } char restore_file_name[1024]; snprintf(restore_file_name, 1024, "webspl_cache/%s.spld", ses_id); FILE *restore_file = fopen(restore_file_name, "r"); if (!create && !restore_file) { #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_unlock(&pool_lck); #endif return 0; } #ifdef ENABLE_PTHREAD_SUPPORT if (pthread_mutex_trylock(&pool[oldest_entry].lck)) { if (restore_file) fclose(restore_file); printf("Can't lock slot #%d -> retry in 1 second.\n", oldest_entry); pthread_mutex_unlock(&pool_lck); my_sleep(1); goto retry_entry_point; } #endif if (pool[oldest_entry].vm) { time_t now = time(0); int idle = now - pool[oldest_entry].ping; printf("Creating VM in slot #%d (old session was idle for %d:%02d:%02d).\n", oldest_entry, idle / (60*60), (idle/60) % 60, idle % 60); expire_dumpdir(0, EXPIRE_DUMPDIR_TIMEOUT, EXPIRE_DUMPDIR_INTERVAL); char dump_file_name[1024]; snprintf(dump_file_name, 1024, "webspl_cache/%s.spld", pool[oldest_entry].session); FILE *dump_file = fopen(dump_file_name, "w"); if ( dump_file ) { printf("Dumping old session to `%s'.\n", dump_file_name); if (spl_dump_ascii(pool[oldest_entry].vm, dump_file)) printf("Dumping `%s' failed!\n", dump_file_name); fclose(dump_file); } else printf("Can't write dumpfile `%s': %s\n", dump_file_name, strerror(errno)); spl_vm_destroy(pool[oldest_entry].vm); free(pool[oldest_entry].session); } else printf("Creating VM in slot #%d (slot was empty).\n", oldest_entry); pool[oldest_entry].vm = spl_vm_create(); pool[oldest_entry].session = strdup(ses_id); pool[oldest_entry].ping = time(0); my_asprintf(&pool[oldest_entry].vm->path, ".:./spl_modules:%s/spl_modules:%s", daemon_basedir, spl_system_modules_dir()); pool[oldest_entry].vm->codecache_dir = strdup("./webspl_cache"); pool[oldest_entry].vm->runloop = spl_simple_runloop; spl_builtin_register_all(pool[oldest_entry].vm); spl_clib_reg(pool[oldest_entry].vm, "write", spl_mod_cgi_write, 0); if (restore_file) { spl_restore_ascii(pool[oldest_entry].vm, restore_file); fclose(restore_file); unlink(restore_file_name); PROFILE_INC(resume_disk); } else PROFILE_INC(create_new); #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_unlock(&pool_lck); #endif return pool[oldest_entry].vm; } static void webspld_vm_pool_put(struct spl_vm *vm) { #ifdef ENABLE_PTHREAD_SUPPORT for (int i=0; icode) { printf("Freeing VM in slot #%d (script terminated).\n", i); spl_vm_destroy(pool[i].vm); free(pool[i].session); pool[i].vm = 0; pool[i].session = 0; pool[i].ping = 0; } pthread_mutex_unlock(&pool[i].lck); } #endif } static void webspld_vm_pool_cleanup() { #ifdef ENABLE_PTHREAD_SUPPORT /* locking everything */ pthread_mutex_lock(&pool_lck); #endif for (int i=0; ichecks; while (chk) { struct code_cache_check *next_chk = chk->next; free(chk->filename); free(chk); chk = next_chk; } if (c->filename) free(c->filename); if (c->code) spl_code_put(c->code); memset(c, 0, sizeof(struct code_cache)); } static struct code_cache *wrap_spl_malloc_file_c; static void *wrap_spl_malloc_file(const char *filename, int *size) { struct code_cache *c = wrap_spl_malloc_file_c; struct code_cache_check *chk = c->checks; while (chk) { if (!strcmp(chk->filename, filename)) goto do_not_add_twice; chk = chk->next; } struct stat sb; int rc = lstat(filename, &sb); if (rc) return 0; chk = malloc(sizeof(struct code_cache_check)); chk->filename = strdup(filename); chk->cached_mtime = sb.st_mtime; chk->cached_dev = sb.st_dev; chk->cached_ino = sb.st_ino; chk->next = c->checks; c->checks = chk; do_not_add_twice: return spl_malloc_file(filename, size); } static struct spl_code *cached_filename_to_codepage(struct spl_vm *vm, const char *file) { #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_lock(&code_cache_lock); #endif struct code_cache *c = 0; struct code_cache *oldest_c = code_cache_list; struct spl_code *code = 0; for (int i=0; ilast_use > code_cache_list[i].last_use) oldest_c = code_cache_list + i; if (!code_cache_list[i].last_use) continue; if (strcmp(code_cache_list[i].filename, file)) continue; struct code_cache_check *chk = code_cache_list[i].checks; while (chk) { struct stat sb; int rc = lstat(chk->filename, &sb); if (rc || chk->cached_mtime != sb.st_mtime || chk->cached_dev != sb.st_dev || chk->cached_ino != sb.st_ino) goto flush_this_entry; chk = chk->next; } c = code_cache_list + i; break; flush_this_entry: free_code_cache(code_cache_list + i); if (oldest_c->last_use > code_cache_list[i].last_use) oldest_c = code_cache_list + i; break; } if (!c) { c = oldest_c; free_code_cache(c); wrap_spl_malloc_file_c = c; PROFILE_INC(compiler); printf("Compiling `%s' (cache bay %d).\n", file, (int)(c - code_cache_list)); char *spl_source = wrap_spl_malloc_file(file, 0); if (!spl_source) { spl_report(SPL_REPORT_HOST, vm, "Can't read script file!\n"); #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_unlock(&code_cache_lock); #endif return 0; } struct spl_asm *as = spl_asm_create(); as->vm = vm; if ( spl_compiler(as, spl_source, file, wrap_spl_malloc_file, 1) ) { spl_asm_destroy(as); free(spl_source); #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_unlock(&code_cache_lock); #endif return 0; } spl_asm_add(as, SPL_OP_HALT, "1"); spl_optimizer(as); code = spl_asm_dump(as); code->id = strdup(file); spl_asm_destroy(as); free(spl_source); c->last_use = time(0); c->filename = strdup(file); c->code = spl_code_get(code); } else { printf("Using cached code page (cache bay %d).\n", (int)(c - code_cache_list)); c->last_use = time(0); code = spl_code_get(c->code); } #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_unlock(&code_cache_lock); #endif return code; } struct handle_conn_args { int fd; struct sockaddr_in addr; }; static void *handle_conn(void *args_p) { struct handle_conn_args *args = args_p; printf("\nConnection from %s:%u.\n", inet_ntoa(args->addr.sin_addr), ntohs(args->addr.sin_port)); PROFILE_INC(requests); handle_http_request(args->fd, webspld_vm_pool_get, webspld_vm_pool_put, code_cache_list ? cached_filename_to_codepage : 0); if (close(args->fd) < 0) error(); printf("Connection closed.\n"); #ifdef ENABLE_PTHREAD_SUPPORT sem_post(&thread_pool_sem); free(args); #endif return 0; } static void listen_loop() { struct sockaddr_in addr; socklen_t addrlen; time_t last_profile_out = time(0); int rc, fd; printf("Listening...\n"); #ifdef ENABLE_PTHREAD_SUPPORT sem_init(&thread_pool_sem, 0, threads); #endif while (!got_sigint) { struct timeval select_timeout; select_timeout.tv_sec = 3; select_timeout.tv_usec = 0; fd_set listen_fds; FD_ZERO(&listen_fds); FD_SET(listenfd, &listen_fds); rc = select(listenfd+1, &listen_fds, NULL, NULL, &select_timeout); if (rc < 0 && (errno == EINTR || errno == EAGAIN)) continue; if (rc == 0) continue; if (rc < 0) error(); addrlen = sizeof(addr); fd = accept(listenfd, (struct sockaddr *) &addr, &addrlen); if (fd < 0 && (errno == EINTR || errno == EAGAIN)) continue; if (fd < 0) error(); #ifdef ENABLE_PTHREAD_SUPPORT struct handle_conn_args *args = malloc(sizeof(struct handle_conn_args)); args->addr = addr; args->fd = fd; while (sem_wait(&thread_pool_sem) == -1) { /* loop */ } pthread_t dummy_thread_id; if (pthread_create(&dummy_thread_id, 0, handle_conn, args) != 0) error(); pthread_detach(dummy_thread_id); #else struct handle_conn_args args; args.addr = addr; args.fd = fd; handle_conn(&args); #endif if (profile_file) { time_t now = time(0); if (now > last_profile_out+60) { #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_lock(&profile_lck); #endif fprintf(profile_file, "%15Ld %15Ld %7d %7d %7d %7d %7d\n", (long long)last_profile_out, (long long)now, profile_requests, profile_create_new, profile_resume_mem, profile_resume_disk, profile_compiler); fflush(profile_file); last_profile_out = now; profile_requests = 0; profile_create_new = 0; profile_resume_mem = 0; profile_resume_disk = 0; profile_compiler = 0; #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_unlock(&profile_lck); #endif } } } #ifdef ENABLE_PTHREAD_SUPPORT printf("Waiting for worker threads to shut down..\n"); for (int i=0; i pool_size) help(1); #endif if (daemonize) { close(0); close(1); close(2); open("/dev/null", O_RDONLY); open("/dev/null", O_WRONLY); open("/dev/null", O_WRONLY); if (fork()) return 0; } if (logfile) { close(1); close(2); open(logfile, O_CREAT|O_WRONLY|O_APPEND, 0666); open(logfile, O_CREAT|O_WRONLY|O_APPEND, 0666); } int rc, on = 1; struct sockaddr_in addr; struct linger sl = { 1, 5 }; getcwd(daemon_basedir, PATH_MAX); printf("Loading SPL CGI module.\n"); SPL_REGISTER_BUILTIN_MODULE(cgi); spl_report = spl_mod_cgi_reportfunc; printf("Allocating VM pool (%d slots).\n", pool_size); pool = calloc(pool_size, sizeof(struct pool_entry)); if (code_cache_size > 0) { printf("Allocating code cache (%d bays).\n", code_cache_size); code_cache_list = calloc(code_cache_size, sizeof(struct code_cache)); } create_dumpdir(0); expire_dumpdir(0, EXPIRE_DUMPDIR_TIMEOUT, EXPIRE_DUMPDIR_INTERVAL); #ifdef ENABLE_PTHREAD_SUPPORT for (int i=0; i&2 else exec 3> /dev/null fi if [ "$1" = "-v" ]; then shift else exec 2> /dev/null fi cc_lang="c" cc_main="" retcode="0" result="" test_prog() { if echo "$1" | $CC $CFLAGS $LDFLAGS $2 \ -x $cc_lang - -o $tmpfile >&2 then result=1; else result=0; fi } test_header() { local includes="" for hdr in $1; do includes="$includes #include <$hdr>" done; shift test_prog "$includes int main() { $cc_main return 0; }" "$@" } { echo "SYSTEM = $1" echo "CC = $CC" echo "CXX = $CXX" echo "CFLAGS = $CFLAGS" echo "LDFLAGS = $LDFLAGS" } >&2 if [ -f config.cache ]; then . ./config.cache eval "cached_result=\$cached_syscheck_result_$1" if [ -n "$cached_result" ]; then echo $cached_result exit $retcode fi fi tmpfile=`mktemp /tmp/spl_syscheck.XXXXXX` echo -n "Checking for $1:" >&3 case "$1" in pcre) test_header "pcre.h" "$(pcre-config --cflags)" ;; expat) test_header "expat.h" ;; sqlite) test_header "sqlite3.h" ;; mysql) test_header "mysql/mysql.h" ;; postgres) test_header "libpq-fe.h" "-I$(pg_config --includedir)/." ;; libxml2) test_header "libxml/parser.h" "$(xml2-config --cflags)" ;; libxslt) test_header "libexslt/exslt.h" "$(xslt-config --cflags)" ;; libsdl) test_header "SDL/SDL_image.h" "$(sdl-config --cflags)" ;; libcurl) cc_main="if (CURLOPT_FTP_ACCOUNT != 0) {}"; test_header "curl/curl.h" "$(curl-config --cflags)" ;; libuuid) test_header "uuid/uuid.h" "-luuid" ;; libfann) if pkg-config --silence-errors --modversion fann | grep -qx '2.0.0' then result=1; else result=0; fi ;; wscons) result=0 [ -f /usr/include/dev/wscons/wsconsio.h ] && result=1 ;; epfb) result=0 [ -c /dev/epfb ] && result=1 [ -c /dev/epfb0 ] && result=1 ;; opengl|opengl_file) set -vx result=0 for file in /usr/include/GL/gl.h /usr/X11/include/GL/gl.h /usr/X11R6/include/GL/gl.h; do [ -f "$file" ] && { result="$file"; break; } done if [ "$1" = "opengl" ]; then [ "$result" == "0" ] || result=1 else [ "$result" == "0" ] && result="" fi ;; libsmoke) cc_lang="c++"; CC="$CXX" CFLAGS="`echo $CFLAGS | sed 's,-std=gnu99,,'`" test_header "smoke.h" ## remove the following line to build SPL with Qt 3.x support ## result="0" ;; libsmoke_config) echo "#include " | $CC $CFLAGS -E - > $tmpfile grep -q mf_ctor $tmpfile || echo "-DSMOKE_WITHOUT_MF_CTOR" ;; readline) test_header "stdio.h readline/readline.h" ;; gettext_libc) if $CC -o $tmpfile -x c - >&2 <<- EOT #include #include int main() { textdomain(0); return 0; } EOT then result=1; else result=0; fi ;; gettext_intl) if $CC -o $tmpfile -x c - -lintl >&2 <<- EOT #include #include int main() { textdomain(0); return 0; } EOT then result=1; else result=0; fi ;; pthread) if $CC -pthread -o $tmpfile -x c - >&2 <<- EOT #include int main() { pthread_t foobar; pthread_create(&foobar, 0, 0, 0); return 0; } EOT then result=1; else result=0; fi ;; extra_cflags) for dir in \ /usr/include/kde \ /usr/include/qt3 \ /opt/kde*/include \ /opt/qt3*/include \ /opt/mysql*/include \ /usr/lib/qt*/include \ /usr/lib32/qt*/include \ /usr/lib64/qt*/include \ /usr/local/include \ /opt/local/include \ /usr/nekoware/include \ /usr/nekoware/mysql/include \ /usr/nekoware/pgsql/include \ /usr/pkg/include do [ -d $dir ] && echo "-I$dir" done ;; extra_ldflags) for dir in \ /opt/kde*/lib \ /opt/kde*/lib32 \ /opt/kde*/lib64 \ /opt/qt*/lib \ /opt/qt*/lib32 \ /opt/qt*/lib64 \ /opt/mysql*/lib \ /opt/mysql*/lib32 \ /opt/mysql*/lib64 \ /usr/lib/qt*/lib \ /usr/lib32/qt*/lib \ /usr/lib64/qt*/lib \ /usr/lib/mysql \ /usr/lib32/mysql \ /usr/lib64/mysql \ /usr/local/lib \ /opt/local/lib \ /usr/local/lib32 \ /opt/local/lib32 \ /usr/local/lib64 \ /opt/local/lib64 \ /usr/nekoware/lib \ /usr/nekoware/mysql/lib \ /usr/nekoware/pgsql/lib \ /usr/pkg/lib do [ -d $dir ] && echo "-L$dir" done ;; *) echo "Usage: $0 {mode}" retcode=1 esac if [ -n "$result" ]; then echo "cached_syscheck_result_$1=$result" >> config.cache [ "$result" = "0" ] && echo " not found." >&3 [ "$result" = "0" ] || echo " found." >&3 else echo " done." >&3 fi if [ -n "$result" ]; then echo $result fi rm -f $tmpfile exit $retcode spl-1.0pre6/string.c0000644000000000000000000001170411064463357013131 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * string.c: SPL string implementation */ #include #include #include #include #include "spl.h" #include "compat.h" #define newlen(a,b) (a+b < a || a+b >= ~0U >> 1 ? ~0U : a+b) struct spl_string *spl_string_new(int flags, struct spl_string *left, struct spl_string *right, char *text, struct spl_code *code) { struct spl_string *s = calloc(1, sizeof(struct spl_string)); if (flags & SPL_STRING_NOAUTOGET) { s->left = left; s->right = right; } else { s->left = spl_string_get(left); s->right = spl_string_get(right); } if (s->left) { s->left_len = s->left->total_len; if (s->left->flags & SPL_STRING_UTF8) s->flags |= SPL_STRING_UTF8; } if (s->right) { s->right_len = s->right->total_len; if (s->right->flags & SPL_STRING_UTF8) s->flags |= SPL_STRING_UTF8; } s->flags = flags & ~SPL_STRING_NOAUTOGET; s->ref_counter = 1; if (text) { int i = 0; while (text[i]) if (text[i++] & 0x80) s->flags |= SPL_STRING_UTF8; s->text = text; s->text_len = i; } if (code) { s->code = spl_code_get(code); s->flags |= SPL_STRING_STATIC; } s->total_len = newlen(s->total_len, s->left_len); s->total_len = newlen(s->total_len, s->text_len); s->total_len = newlen(s->total_len, s->right_len); if (s->flags & SPL_STRING_DOTCAT) s->total_len = newlen(s->total_len, 1); return s; } struct spl_string *spl_string_printf(int flags, struct spl_string *left, struct spl_string *right, const char *fmt, ...) { char *text = 0; if (fmt) { va_list ap; va_start(ap, fmt); my_vasprintf(&text, fmt, ap); va_end(ap); } return spl_string_new(flags, left, right, text, 0); } struct spl_string *spl_string_split(struct spl_string *s, unsigned int offset, unsigned int len) { if (!s) { assert(len == 0); return 0; } assert(offset+len <= s->total_len); if (len < 128) { char *text = spl_string(s); return spl_string_new(0, 0, 0, my_strndup(text+offset, len), 0); } else { struct spl_string *r = spl_string_new(0, s, 0, 0, 0); r->left_offset = offset; r->left_len = len; r->total_len = len; return r; } } struct spl_string *spl_string_get(struct spl_string *s) { if (s) s->ref_counter++; return s; } void spl_string_put(struct spl_string *s) { if (s && --(s->ref_counter) == 0) { spl_string_put(s->left); spl_string_put(s->right); spl_code_put(s->code); if ((s->flags & SPL_STRING_STATIC) == 0 && s->text) free(s->text); free(s); } } static char *spl_string_write(struct spl_string *s, int *flags_p, char *t, unsigned int offset, unsigned int len) { if (!s) return t; assert(offset+len <= s->total_len); if (s->left_len > offset) { unsigned int left_len = s->left_len - offset; if (left_len > len) left_len = len; if (left_len) { t = spl_string_write(s->left, flags_p, t, s->left_offset + offset, left_len); len -= left_len; } offset = 0; } else offset -= s->left_len; if ((s->flags & SPL_STRING_DOTCAT) != 0) { if (offset) { offset--; } else { *(t++) = '.'; len--; } } if (s->text_len > offset) { unsigned int text_len = s->text_len - offset; if (text_len > len) text_len = len; if (text_len) { int found_utf8 = 0; for (unsigned int i=0; itext[offset+i]) & 0x80) found_utf8 = 1; if (found_utf8) *flags_p |= SPL_STRING_UTF8; len -= text_len; t += text_len; } offset = 0; } else offset -= s->text_len; return spl_string_write(s->right, flags_p, t, offset, len); } char *spl_string(struct spl_string *s) { if (!s) return ""; if (!s->left && !s->right) return s->text ? s->text : ""; s->flags &= ~SPL_STRING_UTF8; assert(s->total_len != ~0U); char *newtext = malloc(s->total_len + 1); *(spl_string_write(s, &s->flags, newtext, 0, s->total_len)) = 0; spl_string_put(s->left); spl_string_put(s->right); spl_code_put(s->code); s->left = s->right = 0; s->left_offset = s->right_offset = 0; s->left_len = s->right_len = 0; s->code = 0; if ( (s->flags & SPL_STRING_STATIC) == 0 && s->text ) free(s->text); s->text_len = s->total_len; s->text = newtext; s->flags = s->flags & ~(SPL_STRING_STATIC|SPL_STRING_DOTCAT); return newtext; } spl-1.0pre6/README.WEBSPL0000644000000000000000000002550010437047443013326 0ustar rootroot WebSPL and WSF Tutorial ======================= This is a short tutorial to WebSPL and WSF. It is not a reference manual, so also have a look at the "wsf" module, the "cgi" module and related modules in the module references. If you have not yet set up your Web Server to handle webspl scripts, please have a look at the README file or the 'What is SPL?' chapter in this manual. WebSPL Introduction ------------------- WebSPL is an SPL runtime for CGI programming. There are two implementations of WebSPL: The CGI script "webspl.cgi", for integrating WebSPL with the Apache webserver using an 'action-handler', and "webspld", a stand-alone HTTP server. The SPL sources inlcude an example "httpd.conf" file for using "webspl.cgi" with the Apache webserver. The "webspld" program prints its own usage info when called with '--help'. The interesting thing about WebSPL is, that, other than usual CGI scripts, a WebSPL script is not executed once for each HTTP request. Instead, there is just one (virtual) SPL process for a session and the WebSPL script can pause it's execution at any time, wait for the user to do something and continue execution as soon as the webbrowser sends the next HTTP request for the session. This is a tutorial - so here is our first example program: /* -- webspltut/webspltut00.webspl -- */ Whenever task_pause() is called, the program execution stops and what has been written since the last task_pause() (or the beginning of the program execution) is displayed in the web browser. As soon as the user reacts (i.e. clicks on "next" in this example), the execution of the program is continued and task_pause() returns. The query string parameter 'sid' is special: It always contains the session id and must not be used for anything else in WebSPL applications. If the script is using SPL tasks, it is also possible to include the ID of the task which should be woken up in the 'sid' parameter. E.g. WSF is doing that. WSF Introduction ---------------- WSF (WebSPL Forms) is a library for doing web application development with WebSPL. WSF provides a widget based interface and so allows the development of web applications with a look&feel like normal GUI programs. But it is a bad choice for creating dynamic webpages since example given the browser back button must not be used in WSF applications. For this (and other) reasons it is recommended to open WSF applications in browser popup windows without browser toolbars. There are two base classes in WSF: WsfComponent and WsfDocument. The WsfDocument object handles a browser window. Usually there is only one instance of that object in a program, but if you want to use WSF to manage multiple windows, one WsfDocument instance per browser window is required. This WsfDocument instance is usually (i.e. per convention, there is no technical reason for doing so) assigned to a global variable named "page". The content of a browser window is organised as a tree of WsfComponent objects. The root component must be assigned to the "root" member variable of the "page" object. The WsfComponent object itself has a member variable called "children" which is an array/hash of the child components. In most cases objects derived from WsfComponent are used instead of WsfComponent directly. One of these derived objects is WsfDisplay from the "wsf_display" module (WsfDocument and WsfComponent are declared in the "wsf" module). The HTML text which should be displayed in a WsfDisplay object is simply passed to the constructor. So here is a very simple WSF "Hello World" application: /* -- webspltut/webspltut01.webspl -- */ The page.main() does never return. The default behavior of the WsfComponent object is to simply print the html content of all its children in its get_html() method. So it is pretty useful as a simple container for other more complex objects derived from WsfComponent: /* -- webspltut/webspltut02.webspl -- */ But this application doesn't do much. So, lets create our own object derived from WsfComponent which implements a simple counter: /* -- webspltut/webspltut03.webspl -- */ So, when creating your own WSF Components, you need to overload the get_html() method with something which returns the HTML code for your component and overload the main() method with the main program for the task managing this component. The task executing the main() method is created automatically by the object constructor (and killed by the destroy() method). The function task_co_return() must be called by main() to wait for user events. The variable 'dirty' must be set to 1 when anything happend with an effect on the get_html() output. The root element of the HTML code generated by get_html() must have its 'id' attribute set to the value of the 'id' variable. There are methods (such as the add_href() used in the example) for creating links, formulas, etc. Have a look at the module reference for the "wsf" module for a full overview of the WsfDocument and the WsfComponent objects. Adding Menus ~~~~~~~~~~~~ Applications always have some type of menu. There is a generic WSF component called WsfMenu (from the "wsf_menu" module) which implements nice menus. The menu entries are function pointers which are called when the entry has been clicked: /* -- webspltut/webspltut04.webspl -- */ The HTML code generated by WsfMenu should be the first real code in the webpage. So the example is using a simple WsfComponent as root component and WsfMenu as its first child (0). The 2nd child (1) is set to WsfDisplay objects by the menu callbacks to display messages. Example Application: A very simple CMS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Now lets write a real application: a very simple CMS. Don't expect too much: It is not much more than just a web-based file manager for HTML files. Do not wonder: the login for the login screen is covered in the next section. /* -- webspltut/webspltut05.webspl -- */ Please run the application before you continue reading this tutorial (just to make sure you know what this is all about). The application defines two Objects: Editpage for dialog which lets the user edit a page and Newpage for the dialog which lets the user create new pages. The application also defines the function create_menu() for (re-)creating the menu. The main program just creates the page object, the root component, calls create_menu() and enters page.main(). The create_menu() function .......................... This function creates the menu as child component of the root component. The "Pages" menu is dynamically generated from the list of *.html files in the current directory. Whenever files are added or removed, create_menu() must be called in order to update this menu. A closure is used to store the filename to be opened together with the callback function which is called when an entry in the "Pages" menu is clicked. The Editpage Object ................... Editpage is directly dervied from WsfComponent. The filename of the file to be edited is passed as an argument to the constructor and copied to the variable 'filename' there. The methods get_html() and main() are overloaded. The get_html() method simply displays an HTML form for editing the page. The page content is loaded from the HTML file. Note how the 'xml::' and 'js::' encoding functions are used to ensure the correctness of the generated HTML code. The main() method simply writes back the data to the file, or removes the file if the user clicked on the "Remove" button. Since main() is automatically called in an endless-loop, I did not add the "while (1) { ... }" loop here explicitly. When main() is removing the file, the menu is updated by calling create_menu() and the Editpage component is replacing itself with a simple WsfComponent instance. The Newpage Object .................. The Newpage opject (also directly dervied from WsfComponent) is even simpler. get_html() just displays an HTML form again. When the form is submitted and main() returns from task_co_return(), it simply creates the new page (after cleaning up the filename), calls create_menu() and replaces itself with an Editpage instance for the newly created file. Note that a Here-Document with indenting character is used when creating the HTML text for the new page. This avoids ugly leading blanks in the HTML file while keeping the indenting in the program intact. Login screen and opening a popup window ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The last example is using a pretty nice mechanism for doing authentication and opening a popup window. Before creating the 'page' object and passing control to WSF, this code is executed: /* -- webspltut/webspltut06.spl -- */ There is nothing wrong with already accepting parameters at program startup. The program expects username and password in the query string parameters "user" and "pass". If they are not present or wrong, the login screen will be printed and the program exits. Only if correct authentication tokens are passed, the program continues execution and initializes the WSF framework. It has shown that this approach is very useful and much easier than actually including this step in the application itself. One of the benefits of this approach is that a developer can pass the authentication tokens already in the query string and so bypass the additional screen. That's very comfortable for testing the application during the development. Some applications even allow 'deep-linking' to some dialogs this way to make testing easier. This approach also allows to open the application in a popup window easily. Opening a popup window makes sense since we do not want the user to click on the browser 'back' button or bookmark any deep links of the application. It doesn't really prevent the user from doing such stuff - but it makes it hard enough to avoid most of the troubles. Opening the popup window is simply done by printing the javascript code and then calling task_pause(). As soon as the the browser tries to load the content of the popup window, the session is resumed and task_pause() returns. W2T Introduction ---------------- The W2T (Web 2.0 Toolkit) module provides another framework for web application development. W2T applications only send an initial XML document (usually XHTML or SVG) to the browser and everything after is done using AJAX XML RPC calls. The W2T module is basically a collection of SPL and JavaScript functions which provide functionality for updating the XML DOM tree displayed in the browser window from server-side SPL and call server-side SPL event callbacks from browser-side JavaScript code. Here is a small W2T example program which is using SVG. Note that at the time of this writing Firefox 1.5 is the only browser which can be used as frontend for W2T SVG programs: /* -- webspl_demo/w2tdemo_svg.webspl -- */ The W2T module also includes some JavaScript helper functions for doing animations and other funny stuff. W2T is under development and a more detailed documentation will be provided when it is finished. spl-1.0pre6/webspltut/0000755000000000000000000000000011300476113013470 5ustar rootrootspl-1.0pre6/webspltut/webspltut00.webspl0000644000000000000000000000210710237623237017110 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * webspltut00.webspl: An example from the WebSPL Tutorial */ load "task"; load "cgi"; for (var i=0; i<10; i++) { write(<> [$i] Hello World!   [next] ); task_pause(); } write("That's it!\n"); spl-1.0pre6/webspltut/webspltut01.webspl0000644000000000000000000000200110240067373017077 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * webspltut01.webspl: An example from the WebSPL Tutorial */ load "wsf"; load "wsf_display"; var page = new WsfDocument(); page.root = new WsfDisplay("Hello World!"); page.main(); spl-1.0pre6/webspltut/webspltut02.webspl0000644000000000000000000000213010240067373017103 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * webspltut02.webspl: An example from the WebSPL Tutorial */ load "wsf"; load "wsf_display"; var page = new WsfDocument(); page.root = new WsfComponent(); page.root.children[0] = new WsfDisplay("Hello"); page.root.children[1] = new WsfDisplay("World!"); page.main(); spl-1.0pre6/webspltut/webspltut03.webspl0000644000000000000000000000301110240071322017070 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * webspltut03.webspl: An example from the WebSPL Tutorial */ load "wsf"; object Counter WsfComponent { var counter_value = 100; method get_html() { return <>
Hello World! [$counter_value]
Up Down
; } method main() { while (1) { task_co_return(); if (declared cgi.param.mode && cgi.param.mode ~== "up") counter_value++; if (declared cgi.param.mode && cgi.param.mode ~== "down") counter_value--; dirty = 1; } } } var page = new WsfDocument(); page.root = new Counter(); page.main(); spl-1.0pre6/webspltut/webspltut04.webspl0000644000000000000000000000266510241657676017136 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * webspltut04.webspl: An example from the WebSPL Tutorial */ load "wsf"; load "wsf_display"; load "wsf_menu"; var menu = [ "Foo" => [ "Entry 1" => function() { page.root.child_set(1, new WsfDisplay("Foo Entry 1")); }, "Entry 2" => function() { page.root.child_set(1, new WsfDisplay("Foo Entry 2")); }, "Entry 3" => function() { page.root.child_set(1, new WsfDisplay("Foo Entry 3")); } ], "Bar" => function() { page.root.child_set(1, new WsfDisplay("This is Bar")); } ]; var page = new WsfDocument(); page.root = new WsfComponent(); page.root.children[0] = new WsfMenu(menu); page.main(); spl-1.0pre6/webspltut/webspltut05.webspl0000644000000000000000000000753710241075053017121 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * webspltut05.webspl: An example from the WebSPL Tutorial */ load "wsf"; load "wsf_display"; load "wsf_menu"; load "file"; load "encode_js"; object Editpage WsfComponent { var filename; method get_html() { var data = file_read(filename); return <>

Edit page `${xml::filename =~ s/\.html$//R}':


; } method main() { task_co_return(); if (declared cgi.param.remove) { file_delete(filename); create_menu(); page.root.child_set("content", new WsfComponent()); } else { file_write(filename, cgi.param.data); dirty = 1; } } method init(_filename) { filename = _filename; return *WsfComponent.init(); } } object Newpage WsfComponent { method get_html() { return <>

Create a new page:

Filename:

Page Title:

; } method main() { task_co_return(); var pagetitle = xml::cgi.param.pagetitle; var filename = xml::cgi.param.filename; filename =~ s/^(.*\/|)\.*//; filename =~ s/(? : : : : ${xml::pagetitle} : : :

${xml::pagetitle}

: : EOT; file_write(filename, html); create_menu(); page.root.child_set("content", new Editpage(filename)); } } function create_menu() { var menu; menu["Pages"] = ({ var pages; var ls = file_list("."); foreach i (ls) if ( ls[i] =~ /^(?P(?P.*)\.html)$/ ) { import $$; pages[title] = function() { page.root.child_set("content", new Editpage(filename)); }; } return pages; }); menu["New Page"] = function() { page.root.child_set("content", new Newpage()); }; page.root.child_set("menu", new WsfMenu(menu)); } // hide login logic because it is covered in a different tutorial section #file-as-code webspltut06.spl var page = new WsfDocument(); page.root = new WsfComponent(); create_menu(); page.main(); �����������������������������������������������������������������������������������������������������������������������������������������������������������������spl-1.0pre6/webspltut/index.html��������������������������������������������������������������������0000644�0000000�0000000�00000001522�10240731011�015456� 0����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <title>WebSPL Tutorial Example Programs

WebSPL Tutorial Example Programs

spl-1.0pre6/webspltut/webspltut06.spl0000644000000000000000000000420210240731011016376 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * webspltut06.webspl: An example from the WebSPL Tutorial */ // this is included from webspltut05.webspl before creating the 'page' object. if (not declared cgi.param.user or cgi.param.user ~!= "demo" or not declared cgi.param.pass or cgi.param.pass ~!= "demo") { write(<>

 

Login

Username and password are both "demo".

Username:
Password:
Do not open a popup window.
); exit; } if (not declared cgi.param.nopopup) { write(<> ); task_pause(); } spl-1.0pre6/make-abick.sh0000644000000000000000000000062510562034342013765 0ustar rootroot#!/bin/sh set -ue oldabi="$( grep '^#define SPL_ABICKSUM ' spl.h | cut -f3 -d' '; )" newabi="$( grep -v '^#define SPL_ABI' spl.h | cksum | cut -f1 -d' '; )" echo "** Patching ABI checksum (new: $newabi, old: $oldabi) to spl.h." rm -f spl.h.new sed -e 's,^\(#define SPL_ABICKSUM\).*,\1 '"$newabi"',' \ -e '/^#define SPL_ABI(/ s,\(SPL_ABI\)_[0-9]*,\1_'$newabi',' spl.h.new mv spl.h.new spl.h spl-1.0pre6/hanoi.spl0000644000000000000000000000307010236106505013257 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * hanoi.spl: Playing towers of hanoi in an endless loop */ load "termio"; var stacks; for (var i=10; i>0; i--) push stacks[0], i; function draw() { termio_clear(); for (var s=0; s<3; s++) { var line = 0; foreach i (stacks[s]) { termio_setpos(s * 25 + 5, 15 - line++); for (var j=stacks[s][i]; j<10; j++) write(" "); for (var j=0; j #include #include #include "spl.h" #include "compat.h" #include "mod_sql.h" extern void SPL_ABI(spl_mod_sql_sqlite_init)(struct spl_vm *vm, struct spl_module *mod, int restore); extern void SPL_ABI(spl_mod_sql_sqlite_done)(struct spl_vm *vm, struct spl_module *mod); struct callback_args { struct spl_task *task; struct spl_node *result; }; int callback(void *ca_vp, int argc, char **values, char **names) { struct callback_args *ca = ca_vp; struct spl_node *n = spl_get(0); for (int i=0; itask, n, name_base, values[i] ? SPL_NEW_STRING_DUP(values[i]) : spl_get(0), SPL_CREATE_LOCAL); } spl_create(ca->task, ca->result, NULL, n, SPL_CREATE_LOCAL); return 0; } static struct spl_node *sqlite_query_callback(struct spl_task *task, void *backend_data, const char *query) { sqlite3 *db = backend_data; char *errmsg = 0; int rc; struct spl_node *result = spl_get(0); struct callback_args ca; ca.task = task; ca.result = result; while (1) { rc = sqlite3_exec(db, query, callback, &ca, &errmsg); if ( rc != SQLITE_BUSY ) break; sleep(1); } if( rc != SQLITE_OK ) { spl_put(task->vm, result); spl_clib_exception(task, "SqlEx", "description", SPL_NEW_SPL_STRING(spl_string_printf(0, 0, 0, "SQLite: SQL Error on '%s': %s!\n", query, errmsg ? errmsg : "Unknown error")), NULL); return 0; } return result; } static void sqlite_close_callback(struct spl_vm *vm UNUSED, void *backend_data) { sqlite3_close(backend_data); } static void sqlite_open_callback(struct spl_task *task, struct spl_node *node, const char *data) { struct sql_hnode_data *hnd = malloc(sizeof(struct sql_hnode_data)); char *filename; if (data[0] != '/') my_asprintf(&filename, "%s/%s", task->vm->current_dir_name ? task->vm->current_dir_name : ".", data); else filename = strdup(data); sqlite3 *backend_data; int rc = sqlite3_open(filename, &backend_data); hnd->backend_data = backend_data; if( rc ) { spl_clib_exception(task, "SqlEx", "description", SPL_NEW_SPL_STRING(spl_string_printf(0, 0, 0, "SQLite: Can't open database %s: %s!\n", data, sqlite3_errmsg(hnd->backend_data))), NULL); sqlite3_close((sqlite3*)hnd->backend_data); free(filename); free(hnd); return; } hnd->query_callback = sqlite_query_callback; hnd->close_callback = sqlite_close_callback; node->hnode_data = hnd; free(filename); } void SPL_ABI(spl_mod_sql_sqlite_init)(struct spl_vm *vm, struct spl_module *mod UNUSED, int restore) { if (!restore) spl_module_load(vm, "sql", 0); sql_register_backend(vm, "sqlite", sqlite_open_callback); } void SPL_ABI(spl_mod_sql_sqlite_done)(struct spl_vm *vm, struct spl_module *mod UNUSED) { sql_unregister_backend(vm, "sqlite"); } spl-1.0pre6/spl_modules/mod_termio.c0000644000000000000000000000472511064463357016314 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * mod_termio.c: Simple terminal IO library */ /** * Terminal IO module * * This module provides simple functions for controlling ANSI terminals. */ #include #include #include #include "spl.h" #include "compat.h" extern void SPL_ABI(spl_mod_termio_init)(struct spl_vm *vm, struct spl_module *mod, int restore); extern void SPL_ABI(spl_mod_termio_done)(struct spl_vm *vm, struct spl_module *mod); /** * This function sets the current curos position * The X and Y coordinates are relative to the upper left corner. * The upper left corner has the coordinates (1, 1). */ // builtin termio_setpos(x, y) static struct spl_node *handler_termio_setpos(struct spl_task *task, void *data UNUSED) { int x = spl_clib_get_int(task); int y = spl_clib_get_int(task); printf("\e[%d;%dH", y, x); fflush(stdout); return 0; } /** * This function clears the screen. */ // builtin termio_clear() static struct spl_node *handler_termio_clear(struct spl_task *task UNUSED, void *data UNUSED) { printf("\e[2J"); fflush(stdout); return 0; } /** * This function sleeps for one second. */ // builting termio_sleep() static struct spl_node *handler_termio_sleep(struct spl_task *task UNUSED, void *data UNUSED) { my_sleep(1); return 0; } void SPL_ABI(spl_mod_termio_init)(struct spl_vm *vm, struct spl_module *mod UNUSED, int restore UNUSED) { spl_clib_reg(vm, "termio_setpos", handler_termio_setpos, 0); spl_clib_reg(vm, "termio_clear", handler_termio_clear, 0); spl_clib_reg(vm, "termio_sleep", handler_termio_sleep, 0); } void SPL_ABI(spl_mod_termio_done)(struct spl_vm *vm UNUSED, struct spl_module *mod UNUSED) { return; } spl-1.0pre6/spl_modules/mod_bits.c0000644000000000000000000000647411064463357015761 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * mod_bits.c: Bit manipulation functions */ /** * Bits Module * * This module adds built-in functions for bit manipulations. */ #include "spl.h" #include "compat.h" extern void SPL_ABI(spl_mod_bits_init)(struct spl_vm *vm, struct spl_module *mod, int restore); extern void SPL_ABI(spl_mod_bits_done)(struct spl_vm *vm, struct spl_module *mod); /** * Do a bitwise AND between all arguments and return the result. */ //builtin bits_and(@values); static struct spl_node *handler_bits_and(struct spl_task *t, void *d UNUSED) { int value = ~0; while (spl_clib_get_argc(t)) value &= spl_clib_get_int(t); return SPL_NEW_INT(value); } /** * Do a bitwise OR between all arguments and return the result. */ //builtin bits_or(@values); static struct spl_node *handler_bits_or(struct spl_task *t, void *d UNUSED) { int value = 0; while (spl_clib_get_argc(t)) value |= spl_clib_get_int(t); return SPL_NEW_INT(value); } /** * Do a bitwise XOR between all arguments and return the result. */ //builtin bits_xor(@values); static struct spl_node *handler_bits_xor(struct spl_task *t, void *d UNUSED) { int value = 0; while (spl_clib_get_argc(t)) value ^= spl_clib_get_int(t); return SPL_NEW_INT(value); } /** * Do a bitwise NOT of the argument and return the result. */ //builtin bits_not(value); static struct spl_node *handler_bits_not(struct spl_task *t, void *d UNUSED) { return SPL_NEW_INT(~spl_clib_get_int(t)); } /** * Do a bitwise SHIFT-LEFT of the argument and return the result. */ //builtin bits_shl(value, bits); static struct spl_node *handler_bits_shl(struct spl_task *t, void *d UNUSED) { int value = spl_clib_get_int(t); int bits = spl_clib_get_int(t); return SPL_NEW_INT(value << bits); } /** * Do a bitwise SHIFT-RIGHT of the argument and return the result. */ //builtin bits_shr(value, bits); static struct spl_node *handler_bits_shr(struct spl_task *t, void *d UNUSED) { int value = spl_clib_get_int(t); int bits = spl_clib_get_int(t); return SPL_NEW_INT(value >> bits); } void SPL_ABI(spl_mod_bits_init)(struct spl_vm *vm, struct spl_module *mod UNUSED, int restore UNUSED) { spl_clib_reg(vm, "bits_and", handler_bits_and, 0); spl_clib_reg(vm, "bits_or", handler_bits_or, 0); spl_clib_reg(vm, "bits_xor", handler_bits_xor, 0); spl_clib_reg(vm, "bits_not", handler_bits_not, 0); spl_clib_reg(vm, "bits_shl", handler_bits_shl, 0); spl_clib_reg(vm, "bits_shr", handler_bits_shr, 0); } void SPL_ABI(spl_mod_bits_done)(struct spl_vm *vm UNUSED, struct spl_module *mod UNUSED) { return; } spl-1.0pre6/spl_modules/mod_wsf_display.spl0000644000000000000000000000363110401562174017677 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * mod_wsf_display.spl: Simple WSF Component for showing html content */ /** * A module which implements a simple WSF Component for displaying stuff. */ load "wsf"; /** * A WSF Component for displaying stuff. * * It always returns HTML following the schema: * * <$tag $attributes id="$id">$html_text * * Derived from [[wsf:WsfComponent]]. */ object WsfDisplay WsfComponent { /** * The tag name used for the output of this component. */ var tag = "div"; /** * Additional attributes to be added in the HTML output. */ var attributes; /** * The inner HTML of this component. */ var html_text; /** * Overloaded [[wsf:WsfComponent.get_html()]] */ method get_html() { return '<$tag $attributes id="$id">$html_text'; } /** * Set [[.html_text]] and mark this component as dirty to refresh * the output. */ method set_html(t) { html_text = t; dirty = 1; } /** * Constructor. * The [[.html_text]] variable will be set to the value passed as * parameter. */ method init(t) { html_text = t; return *WsfComponent.init(); } } spl-1.0pre6/spl_modules/mod_cgi.c0000644000000000000000000005064411257144473015557 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * mod_cgi.c: Module for CGI namespace */ /** * Module for writing CGIs */ #include #include #include #include #include #include #include #include #include #include "spl.h" #include "compat.h" #include "webspl_common.h" extern void SPL_ABI(spl_mod_cgi_init)(struct spl_vm *vm, struct spl_module *mod, int restore); extern void SPL_ABI(spl_mod_cgi_done)(struct spl_vm *vm, struct spl_module *mod); #define auto_printf(...) \ do { \ if (ctx && ctx->outfile) \ fprintf(ctx->outfile, __VA_ARGS__); \ else \ printf(__VA_ARGS__); \ } while (0) static char *get_param(struct cgi_params_t *p, const char *name) { while (p) { if (!strcmp(p->key, name)) return p->value; p = p->next; } return 0; } static char *get_cookie(struct cgi_cookie_t *c, const char *name) { while (c) { if (!strcmp(c->key, name)) return c->value; c = c->next; } return 0; } static char *url_decode(char *s) { int size = 0; char *ret; for (int i=0; s[i]; i++, size++) if (s[i] == '%' && s[i+1] && s[i+2]) i+=2; ret = malloc(size+1); int j=0; for (int i=0; s[i]; i++, j++) { if ( s[i] == '+' ) ret[j] = ' '; else if (s[i] == '%' && s[i+1] && s[i+2]) { char num[3] = { s[i+1], s[i+2], 0 }; ret[j] = strtol(num, 0, 16); i += 2; } else if ( s[i] == '+' ) ret[j] = ' '; else ret[j] = s[i]; if (ret[j] == '\r') j--; } assert(j <= size); ret[j] = 0; return ret; } static void parse_query_string_multiformdat(struct cgi_context *ctx, char *data, int data_len, char *boundary) { char *data_end = data + data_len; char *real_boundary; // boundary is prefixed with '\r\n--' my_asprintf(&real_boundary, "\r\n--%s", boundary); // find first element data = strstr(data, boundary); if (!data) return; while (*data && data < data_end) { char *paraname = 0; char *filename = 0; // skip boundary marker data += strlen(boundary); if (*data == '\r') data++; if (*data == '\n') data++; // parse headers while (1) { if (*data == '\r') data++; char *stop = strchr(data, '\n'); if (!stop || stop == data) break; if (strncasecmp(data, "content-disposition:", strlen("content-disposition:"))) goto parse_next_header; data += strlen("content-disposition:"); data += strspn(data, " \t"); while (data < stop) { char *data_start = data; data += strspn(data, " \t"); char *n = data; int n_len = strcspn(data, "=;\n"); data += n_len; if (*data == '=') data++; if (*data == '"') { data++; char *v = data; int v_len = strcspn(data, "\"\n"); data += v_len; if (*data == '"') data++; if (!strncasecmp(n, "name", n_len)) { if (paraname) free(paraname); paraname = my_strndup(v, v_len); } if (!strncasecmp(n, "filename", n_len)) { if (filename) free(filename); filename = my_strndup(v, v_len); } } if (*data == ';') data++; if (data == data_start) break; } parse_next_header: data = stop+1; } // extract data if (*data == '\n') data++; char *stop = my_memmem(data, data_end - data, real_boundary, strlen(real_boundary)); if (!stop) stop = data_end; int part_len = stop - data; // store in cgi ctx if (paraname) { struct cgi_params_t *p = calloc(1, sizeof(struct cgi_params_t)); p->key = paraname; if (filename) { p->value = filename; p->file_data = data; p->file_size = part_len; } else { p->value = malloc(part_len+1); memcpy(p->value, data, part_len); p->value[part_len] = 0; } if (spl_utf8_check(p->value)) { char *old_value = p->value; p->value = spl_utf8_import(old_value, "latin_1"); free(old_value); /* this should never happen */ if (!p->value) p->value = strdup(""); } p->next = ctx->params; ctx->params = p; } else { free(paraname); if (filename) free(filename); } // next element data = stop + 4; } free(real_boundary); } static void parse_query_string(struct cgi_context *ctx, char *data, int data_len, char *type) { if (type) ctx->post_type = strdup(type); if (type && !strncasecmp(type, "text/", 5)) { ctx->post_data = strdup(data); return; } if (type && !strncasecmp(type, "multipart/form-data;", strlen("multipart/form-data;"))) { char *t = strstr(type, "boundary="); if (t) { t += strlen("boundary="); char *boundary = my_strndup(t, strcspn(t, " \t\n")); if (*boundary) { if (data_len < 0) data_len = strlen(data); parse_query_string_multiformdat(ctx, data, data_len, boundary); } free(boundary); } return; } char *my_query_string = strdup(data); char *token = 0, *qsbuffer = my_query_string; while ( (token=my_strsep(&qsbuffer, "&")) ) { struct cgi_params_t *p = calloc(1, sizeof(struct cgi_params_t)); char *enc_value = strchr(token, '='); if (!enc_value) { p->key = url_decode(token); p->value = strdup(p->key); } else { *(enc_value++) = 0; p->key = url_decode(token); p->value = url_decode(enc_value); } if (spl_utf8_check(p->value)) { char *old_value = p->value; p->value = spl_utf8_import(old_value, "latin_1"); free(old_value); /* this should never happen */ if (!p->value) p->value = strdup(""); } p->next = ctx->params; ctx->params = p; } free(my_query_string); } static void parse_cookie_string(struct cgi_context *ctx, char *cookie_string) { while (*cookie_string) { int n_len = strcspn(cookie_string, "=;"); char *n = malloc(n_len+1); snprintf(n, n_len+1, "%.*s", n_len, cookie_string); cookie_string += n_len; while (*cookie_string == '=') cookie_string++; int v_len = strcspn(cookie_string, ";"); char *v = malloc(v_len+1); snprintf(v, v_len+1, "%.*s", v_len, cookie_string); cookie_string += v_len; while (*cookie_string == ';' || *cookie_string == ' ') cookie_string++; struct cgi_cookie_t *c = malloc(sizeof(struct cgi_cookie_t)); c->next = ctx->cookies; c->key = n; c->value = v; ctx->cookies = c; if (spl_utf8_check(c->value)) { char *old_value = c->value; c->value = spl_utf8_import(old_value, "latin_1"); free(old_value); /* this should never happen */ if (!c->value) c->value = strdup(""); } } } // copied from mod_file.c #define GET_REAL_FILENAME \ char *real_filename; \ if (task->vm->current_dir_name && *filename != '/') { \ int len = 2 + strlen(filename) + \ strlen(task->vm->current_dir_name); \ real_filename = my_alloca(len); \ snprintf(real_filename, len, "%s/%s", \ task->vm->current_dir_name, \ filename); \ } else \ real_filename = filename; /** * This function can be used to save an uploaded file to the local filesystem. * The 1st parameter is the name of the upload form field and the 2nd parameter * the local filename. * * The file will be truncated if it exists already and created if it does not. * * The pseudo-variable 'cgi.param.' contains the name of the file. The * content of the uploaded file can't be accessed directly. * * This function returns the number of bytes written to the harddisk or undef * for an error. */ // builtin cgi_userfile_save(paramname, filename) static struct spl_node *spl_mod_cgi_userfile_save(struct spl_task *task, void *data UNUSED) { char *paramname = spl_clib_get_string(task); char *filename = spl_clib_get_string(task); struct cgi_context *ctx = task->vm->cgi_ctx; if ( !ctx ) { spl_report(SPL_REPORT_RUNTIME, task, "CGI: No CGI context found!\n"); return 0; } struct cgi_params_t *p = ctx->params; while (p) { if (!strcmp(p->key, paramname) && p->file_data) { GET_REAL_FILENAME int fd = open(real_filename, O_WRONLY|O_CREAT|O_TRUNC|MY_O_BINARY, 0666); if (!fd) return 0; for (int i=0, rc; i < p->file_size; i+=rc) { rc = write(fd, p->file_data + i, p->file_size - i); if ( rc <= 0 ) { close(fd); return 0; } } close(fd); return SPL_NEW_INT(p->file_size); } p = p->next; } return 0; } /** * This function sends the specified text to the web browser. It also creates * the HTTP header when it is called the first time for an HTTP request. */ // builtin cgi_write(text) struct spl_node *spl_mod_cgi_write(struct spl_task *task, void *data UNUSED) { char *text = spl_clib_get_string(task); struct cgi_context *ctx = task->vm->cgi_ctx; if ( !ctx ) { spl_report(SPL_REPORT_RUNTIME, task, "CGI: No CGI context found!\n"); return 0; } if (ctx->content_type) { if (!strncmp(ctx->content_type, "text/", 5)) auto_printf("Content-Type: %s; charset=UTF-8\r\n\r\n", ctx->content_type); else auto_printf("Content-Type: %s\r\n\r\n", ctx->content_type); free(ctx->content_type); ctx->content_type = 0; } auto_printf("%s", text); // fflush(ctx->outfile ? ctx->outfile : stdout); return 0; } /** * This namespace holds all the CGI specific data, like query string parameters * or browser type. */ // namespace cgi /** * A hash with all query string parameters, parameter name as key and * parameter value as value. * * This variable is read-only. */ // var param; /** * A hash with all cookies passed by the browser. Writing to this hash is only * possible as long as no [[cgi_write()]] has been isued. */ // var cookie; /** * A hash with all config variables read from the webspl.conf files. * Example given: * * # In webspl.conf * [ myapp.webspl ] * myapp.dbtype = sqlite * myapp.database = /var/lib/myapp/database.bin * * // In the script * var db = sql_connect(cgi.config.myapp.dbtype, cgi.config.myapp.database); */ // var config; /** * The HTTP content-type of the document to be send back to the browser. This * variable is write-only and must be set before [[cgi_write()]] is called * the first time. It is automatically reset to "text/html" for each new HTTP * response. */ // var content_type = "text/html"; /** * When this variable has a non-zero value, the output created by debug * statements is not sent to the browser. This variable is write-only. It * is automatically reset to '0' for each new HTTP response. */ // var silent_debug = 0; /** * The current session id (with the now running task) * * This variable is read-only. */ // var sid; /** * The current session id (without task) * * This variable is read-only. */ // var sid_vm; /** * The task part from the session id requested by the user. * * This variable is read-only. */ // var sid_task; /** * The session id as requested by the user (vm and task). * * This variable is read-only. */ // var sid_passed; /** * The url requested by the browser (without the query string). This is useful * for building self-references. * * This variable is read-only. */ // var url; /** * The identification string sent by the user agent. * * This variable is read-only. */ // var agent; /** * The IP address of the peer. * * This variable is read-only. */ // var peerip; /** * The mime type of the data sent in a POST request. * * This variable is read-only. */ // var post_type; /** * The data sent in a POST request when it is a text/ mime type. * * This variable is read-only. */ // var post_data; static void handler_cgi_node(struct spl_task *task, struct spl_vm *vm, struct spl_node *node UNUSED, struct spl_hnode_args *args, void *data UNUSED) { const char *key = args->key ? args->key : ""; while (*key == '?') key++; if ( args->action == SPL_HNODE_ACTION_DUMP ) return; struct cgi_context *ctx = vm->cgi_ctx; if ( !ctx ) { if ( args->action != SPL_HNODE_ACTION_PUT ) spl_report(SPL_REPORT_RUNTIME, task, "CGI: No CGI context found!\n"); return; } if ( args->action == SPL_HNODE_ACTION_CREATE ) { char *value = spl_get_string(args->value); if ( !strcmp(key, "content_type") ) { if (ctx->content_type) { free(ctx->content_type); ctx->content_type = strdup(value); } else spl_report(SPL_REPORT_RUNTIME, task, "CGI: Trying to set MIME Type after the HTTP header has been finalized!\n"); return; } if ( !strncmp(key, "cookie.", 6) ) { const char *c = key+7; while (*c == '?') c++; if (ctx->content_type) { auto_printf("Set-Cookie: %s=%s\n", c, value); } else spl_report(SPL_REPORT_RUNTIME, task, "CGI: Trying to set cookie after the HTTP header has been finalized!\n"); return; } if ( !strcmp(key, "silent_debug") ) { ctx->silent_debug = atoi(value); return; } args->value = 0; return; } if ( args->action != SPL_HNODE_ACTION_LOOKUP ) return; if ( !strcmp(key, "sid") ) { char *this_session; my_asprintf(&this_session, "%.*s:%s", (int)strcspn(ctx->session, ":"), ctx->session, task->id); args->value = SPL_NEW_STRING(this_session); } else if ( !strcmp(key, "sid_vm") ) { char *this_session; my_asprintf(&this_session, "%.*s", (int)strcspn(ctx->session, ":"), ctx->session); args->value = SPL_NEW_STRING(this_session); } else if ( !strcmp(key, "sid_task") ) args->value = SPL_NEW_STRING_DUP( ctx->session + (int)strcspn(ctx->session, ":") ); else if ( !strcmp(key, "sid_passed") ) args->value = SPL_NEW_STRING_DUP(ctx->session); else if ( !strcmp(key, "url") && ctx->url ) args->value = SPL_NEW_STRING_DUP(ctx->url); else if ( !strcmp(key, "agent") && ctx->agent ) args->value = SPL_NEW_STRING_DUP(ctx->agent); else if ( !strcmp(key, "peerip") && ctx->peerip ) args->value = SPL_NEW_STRING_DUP(ctx->peerip); else if ( !strcmp(key, "post_type") && ctx->post_type ) args->value = SPL_NEW_STRING_DUP(ctx->post_type); else if ( !strcmp(key, "post_data") && ctx->post_data ) args->value = SPL_NEW_STRING_DUP(ctx->post_data); else if ( !strncmp(key, "param.", 6) ) { char *p = spl_hash_decode(key+6); const char *s = get_param(ctx->params, p); if (s) args->value = SPL_NEW_STRING_DUP(s); free(p); } else if ( !strncmp(key, "cookie.", 7) ) { char *c = spl_hash_decode(key+7); const char *s=get_cookie(ctx->cookies, c); if (s) args->value = SPL_NEW_STRING_DUP(s); free(c); } else if ( !strncmp(key, "config.", 7) ) { char *c = spl_hash_decode(key+7); const char *s=cgi_config_get_str(ctx->config, c); if (s) args->value = SPL_NEW_STRING_DUP(s); free(c); } } struct cgi_context *spl_mod_cgi_get_cgi_ctx(struct http_request *req, struct cgi_config *cfg) { struct cgi_context *ctx = calloc(1, sizeof(struct cgi_context)); ctx->content_type = strdup("text/html"); ctx->config = cfg; if (req) { if (req->url) ctx->url = strdup(req->url); struct http_request_hdr *hdr = req->headers; while (hdr) { if (!strcmp(hdr->name, "user-agent")) ctx->agent = strdup(hdr->value); if (!strcmp(hdr->name, "cookie")) parse_cookie_string(ctx, hdr->value); hdr = hdr->next; } if (req->query) parse_query_string(ctx, req->query, -1, NULL); if (req->data) parse_query_string(ctx, req->data, req->data_len, req->data_type); if (req->peerip) ctx->peerip = strdup(req->peerip); ctx->req = req; } else { char *e; e = getenv("REDIRECT_URL"); if (e) ctx->url = strdup(e); e = getenv("HTTP_USER_AGENT"); if (e) ctx->agent = strdup(e); e = getenv("REMOTE_ADDR"); if (e) ctx->peerip = strdup(e); e = getenv("QUERY_STRING"); if (e) parse_query_string(ctx, e, -1, NULL); e = getenv("HTTP_COOKIE"); if (e) parse_cookie_string(ctx, e); e = getenv("REQUEST_METHOD"); if (e && !strcmp(e, "POST")) { static char *buf = NULL; int buf_size = 1024; int buf_pos = 0; if (buf) free(buf); buf = malloc(1024 + 10); while (1) { if (buf_pos > buf_size-512) { buf_size += 1024; buf = realloc(buf, buf_size + 10); } int rc = read(0, buf+buf_pos, buf_size-buf_pos); if (rc <= 0) break; buf_pos += rc; } buf[buf_pos] = 0; e = getenv("CONTENT_TYPE"); parse_query_string(ctx, buf, buf_pos, e); } } ctx->session = get_param(ctx->params, "sid"); if(!ctx->session) { const char *sessioncookie = cgi_config_get_str(cfg, "spl.sessioncookie"); if (sessioncookie) ctx->session = get_cookie(ctx->cookies, sessioncookie); } for (int i=0; ctx->session && ctx->session[i]; i++) { if ( ctx->session[i] >= '0' && ctx->session[i] <= '9' ) continue; if ( ctx->session[i] >= 'A' && ctx->session[i] <= 'Z' ) continue; if ( ctx->session[i] >= 'a' && ctx->session[i] <= 'z' ) continue; if ( i && ctx->session[i] == ':' ) break; ctx->session = 0; } ctx->session = strdup(ctx->session ? ctx->session : ""); ctx->report_count = 0; return ctx; } void spl_mod_cgi_free_cgi_ctx(struct cgi_context *ctx) { while (ctx->params) { struct cgi_params_t *n = ctx->params->next; free(ctx->params->key); free(ctx->params->value); free(ctx->params); ctx->params = n; } while (ctx->cookies) { struct cgi_cookie_t *n = ctx->cookies->next; free(ctx->cookies->key); free(ctx->cookies->value); free(ctx->cookies); ctx->cookies = n; } if (ctx->content_type) free(ctx->content_type); if (ctx->session) free(ctx->session); if (ctx->url) free(ctx->url); if (ctx->agent) free(ctx->agent); if (ctx->peerip) free(ctx->peerip); if (ctx->post_type) free(ctx->post_type); if (ctx->post_data) free(ctx->post_data); free(ctx); } void spl_mod_cgi_reportfunc(int type, void *desc, const char *fmt, ...) { va_list ap; va_start(ap, fmt); char *msg = spl_report_string(type, desc, fmt, ap); va_end(ap); struct cgi_context *ctx = 0; if ( desc ) switch (type & 0x000f) { case SPL_REPORT_HOST: case SPL_REPORT_RESTORE: { struct spl_vm *vm = desc; ctx = vm->cgi_ctx; break; } case SPL_REPORT_ASSEMBLER: case SPL_REPORT_COMPILER: case SPL_REPORT_LEXER: { struct spl_asm *as = desc; if (as->vm) ctx = as->vm->cgi_ctx; break; } case SPL_REPORT_RUNTIME: { struct spl_task *task = desc; ctx = task->vm->cgi_ctx; break; } } if ( ctx && ctx->outfile ) { printf("### "); for (int i=0; msg[i]; i++) if (msg[i] == '\n' && msg[i+1]) printf("\n### "); else printf("%c", msg[i]); fflush(stdout); } if (ctx && ctx->silent_debug && (type & SPL_REPORT_DEBUG)) goto skip_debug_browser; if (!ctx || ctx->content_type) { auto_printf("Content-Type: text/html; charset=UTF-8\r\n\r\n"); if (ctx) { free(ctx->content_type); ctx->content_type = 0; } } if (ctx) ctx->report_count++; auto_printf("
", ctx ? ctx->report_count : 0);
	for (int i=0; msg[i]; i++)
		switch (msg[i])
		{
		case '<':
			auto_printf("<");
			break;
		case '>':
			auto_printf(">");
			break;
		case '&':
			auto_printf("&");
			break;
		default:
			auto_printf("%c", msg[i]);
		}
	auto_printf("
\n"); if (ctx) { if ( ctx->report_count < 4 ) { auto_printf("\n"); } if ( ctx->report_count == 4 ) { auto_printf("\n"); } } fflush(ctx && ctx->outfile ? ctx->outfile : stdout); skip_debug_browser: free(msg); } void SPL_ABI(spl_mod_cgi_init)(struct spl_vm *vm, struct spl_module *mod, int restore) { if (!vm->cgi_ctx) vm->cgi_ctx = spl_mod_cgi_get_cgi_ctx(0, 0); spl_clib_reg(vm, "cgi_write", spl_mod_cgi_write, 0); spl_clib_reg(vm, "cgi_userfile_save", spl_mod_cgi_userfile_save, 0); spl_hnode_reg(vm, "cgi_node", handler_cgi_node, 0); if (!restore) spl_hnode(vm, vm->root, "cgi", "cgi_node", mod); } void SPL_ABI(spl_mod_cgi_done)(struct spl_vm *vm, struct spl_module *mod UNUSED) { if (vm->cgi_ctx) { spl_mod_cgi_free_cgi_ctx(vm->cgi_ctx); vm->cgi_ctx = 0; } return; } spl-1.0pre6/spl_modules/mod_wsf.spl0000644000000000000000000004573610427671020016165 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * mod_wsf.spl: WebSPL Forms Library */ /** * WebSPL Forms Library * * This module provides a frame work for doing application development * with WebSPL. The basic idea is to split down the user interface into * so-called components. Each component provides a part of the DOM tree * which is displayed in the browser window. There is a seperate program * task for each component - so they can act as independent as the * programmer wants. * * This module provides [[WsfComponent]], the base object for WSF Components, * and [[WsfDocument]], the object which manages the interaction with the * Browser. * * Other Modules provide additional WSF Components. E.g.: * * [[wsf_debug:WsfDebug]] [[wsf_dialog:WsfDialog]] * [[wsf_display:WsfDisplay]] [[wsf_edit:WsfEdit]] * [[wsf_edit_sql:WsfEditSql]] [[wsf_graph:WsfGraph]] * [[wsf_menu:WsfMenu]] [[wsf_switch:WsfSwitch]] * * If the browser has support for it, [[WsfDocument]] does only send those * parts of the DOM tree to the browser which have actually changed and replace * them 'in place' in the current page using a little JavaScript hack. */ load "task"; load "cgi"; load "encode_xml"; /** * Checks the HTTP Agent string and auto-detects if the browser is able * to synamically update the DOM tree. * * It is possible to specify methods as arguments in the order of preference. * Then this methods are checked in that order. E.g.: * * var method = wsf_get_domupdate_status("iframe", "xmlhttprequest"); * * Will return "iframe", "xmlhttprequest" or "none". * * If no arguments are given the function eighter returns "iframe" or * "none". The "xmlhttprequest" method is left out in this case. * * Usually this is only used internally by the [[WsfDocument]] object to * initialize [[WsfDocument.domupdate]] and doesn't need to be called by the * user. */ function wsf_get_domupdate_status(@methods) { // Agent examples, Mozilla: "Mozilla/5.0 (X11; U; Linux i686; en-US; // rv:1.7) Gecko/20040917 Firefox/0.8" // Agent examples, KHTML: // "Mozilla/5.0 (compatible; Konqueror/3.3; Linux) (KHTML, like Gecko)" // "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/125.5.6 (KHTML, like Gecko) Safari/125.12" if (elementsof methods == 0) methods = [ "iframe" ]; if (not declared cgi.agent) return "none"; foreach[] m (methods) { if (m ~== "none") return "none"; if (m ~== "iframe") { if (cgi.agent =~ /Mozilla/) { if (cgi.agent =~ /KHTML, like Gecko/) continue; if (cgi.agent =~ /compatible/) continue; return "iframe"; } continue; } if (m ~== "xmlhttprequest") { if (cgi.agent =~ /Mozilla/) return "xmlhttprequest"; continue; } } return "none"; } /** * The base WsfComponent object. All other Wsf Components are derived from * this Object. */ object WsfComponent { /** * This counter is used by the constructor to create the [[.id]] for * new instances of Wsf Components. */ static id_counter = 0; /** * The id of this component instance. It is set automatically by the * contructor and must be included as "id" attribute in the top element * of the HTML tree returned by [[.get_html()]]. * * This also is the name of the task created for this component by the * constructor. */ var id; /** * The sid (session id) for the task running this component. It must * be included as parameter 'sid' in all query strings. The methods * [[.add_action()]], [[.add_href()]] and [[.add_javascript()]] should * be used for creating links, etc. */ var sid; /** * This variable must be set to 1 whenever it is neccessary to call * [[.get_html()]] again and refresh the browser view of this component. * * It is also possible to set this to 1 if there have been any changes * in the [[.children]] array. */ var dirty = 0; /** * An array of child components. The keys may be freely choosen. The * [[.get_html()]] method must run [[.get_html()]] for all children and * include the return value in its own output. */ var children; /** * The name of the main task running the [[WsfDocument]] instance which * is responsible for this component. This is automatically set by * [[WsfDocument]] when checking for set dirty flags and by the * [[.child_set()]] method. */ var main_task; /** * Whenever a form is generated by [[.get_html()]], this method must be * used to create the "action" attribute in the
tag. A hidden * input field for the "sid" parameter mus also be generated. E.g.: * * * * ... *
*/ method add_action(url) { if ( WsfDocument.domupdate ~== "iframe") return 'target="wsftarget" action="${xml::url}"'; if ( WsfDocument.domupdate ~== "xmlhttprequest") return 'onsubmit="return wsf_req_form(this, \'${xml::url}\');"'; return 'action="${xml::url}"'; } /** * Whenever a HTML link is created by [[.get_html()]], this method * must be used to create the "href" attribute in the tag. E.g.: * * Foobar */ method add_href(url) { if ( WsfDocument.domupdate ~== "iframe") return 'target="wsftarget" href="${xml::url}"'; if ( WsfDocument.domupdate ~== "xmlhttprequest") return 'href="javascript:wsf_req_link(\'${xml::url}\')"'; return 'href="${xml::url}"'; } /** * Whenever JavaScript is used in the code created by [[.get_html()]], * this method must be used to create the statement which sets * location.href to the new value. E.g.: * * onClick="${ add_javascript("${cgi.url}?sid=${sid}&foo=bar") }" * * the URL will be quoted with single quotes in the generated code. So * there is no problem with embedding it using "onFoobar" JavaScript * event handlers. No additional quoting is done. So something like that * for creating query string parameters with JavaScript is possible too: * * onClick="${ add_javascript("${cgi.url}?sid=${sid}&foo=' + (3+5) + '") }" */ method add_javascript(url) { if ( WsfDocument.domupdate ~== "iframe") return "wsftarget.location.href='$url'"; if ( WsfDocument.domupdate ~== "xmlhttprequest") return "wsf_req_link('$url')"; return "location.href='$url'"; } /** * A simple method for calling [[.get_html_cached()]] on all children * and concatenating the results. */ method get_html_children() { var html; foreach i (children) { html = html ~ children.[i].get_html_cached(); } return html; } var __WsfComponent_get_html_cached_data; /** * This method returns the HTML code for this component. If the * [[.dirty]] flag is set, [[.get_html()]] is called to generate * the HTML code. Otherwise a cached version of the HTML code is * returned. */ method get_html_cached() { if (not dirty and defined __WsfComponent_get_html_cached_data) return __WsfComponent_get_html_cached_data; dirty = 0; return __WsfComponent_get_html_cached_data = get_html(); } /** * The method for creating the HTML code. The top HTML element must * have the attribute "id" set to the value of the [[.id]] member * variable. The default behavior is to simply return: * * '
\n' ~ get_html_children() ~ '
\n' */ method get_html() { return '
\n' ~ get_html_children() ~ '
\n'; } /** * The main function for the task running this component. It is first * called by the constructor and is running until it calls * [[task:task_co_return()]]. * * After that, [[.get_html()]] is called by the [[WsfDocument]] object * to create the HTML representation. * * The [[task:task_co_return()]] function returns when the user has done * something in his browser window which effects this component; i.e. * has clicked a link or submitted a form generated by [[.get_html()]]. * * Then this function can react to the event and call [[task:task_co_return()]] * again when it has finished processing this event. Don't forget to * set [[.dirty]] to 1 if [[.get_html()]] needs to be called again. * * If this method does not call [[task:task_co_return()]], the task will hang * in an endless loop. So don't forget to do that! */ method main() { task_co_return(); } /** * Create (substitute) a child component. * The 1st parameter is the key in the [[.children]] array, the 2nd * parameter the new component object. */ method child_set(name, obj) { if (declared children[name]) children[name].destroy(); task_co_setdefault(obj.id, obj.main_task = main_task); children[name] = obj; dirty = 1; } /** * Remove a child component. * The parameter is the key in the [[.children]] array. */ method child_remove(name) { if (declared children[name]) { children[name].destroy(); delete children[name]; dirty = 1; } } /** * The destructor. It needs to be called when the object isn't needed * anymore to kill the task assigned to that object. It also calls the * destroy method of all child components. * * It is save to call that from the [[.main()]] method. If you do so, * killing the task is postponed until [[.main()]] calls [[task:task_co_return()]] * the next time. [[.main()]] will then never return from this function * again. */ method destroy() { foreach i (children) { children.[i].destroy(); delete children.[i]; } task_late_kill(id); } /** * The constructor. */ method init() { id = "wsfc_" ~ (++id_counter); sid = cgi.sid_vm ~ ":" ~ id; task_create(id, "while (1) { main(); }", this); task_public(id); task_co_call(id); return this; } } /** * The WsfDocument object. There must be one WsfDocument object for * every browser window which is under control of WSF. Usually it * is instanciated and assigned to a variable called 'page', then * initialized and finally the [[.main()]] method is called. E.g.: * * var page = new WsfDocument(); * page.title = "My Application Title"; * page.root = new MyFunnyRootWsfComponent(); * page.main(); * * The [[.main()]] method never returns. */ object WsfDocument { /** * This variable is automatically set when the module is loaded. * It contains the return code of [[wsf_get_domupdate_status()]]. * * It is possible to change that variable before instanciating * WsfDocument the first time. After that, the variable shouldn't * be touched anymore. * * Additionally it is possible to change that variable by passing * a cgi query string parameter on program startup: * * wsf_domupdate={ iframe | xmlhttprequest | none } * * If the domupdate mechanism is running in "iframe" mode, * the iframe can be set visible by passing the query string * parameter "wsf_showiframe". * * The "xmlhttprequest" method is probably the best implementation, * but right now it is not possible to do file uploads using this * method. */ static domupdate = wsf_get_domupdate_status(); /** * If [[.domupdate]] is set to "iframe" and this variable is set to 1, * the iframe used for the domupdate mechanism will be visible. This * is only of interest for debugging purposes. */ static showiframe = 0; if ( declared cgi.param.wsf_domupdate ) domupdate = cgi.param.wsf_domupdate; if ( declared cgi.param.wsf_showiframe ) showiframe = cgi.param.wsf_showiframe; /** * This is the root component for this WsfDocument. It must be set * to an instance of [[WsfComponent]] (or any derived object) before * the [[.main()]] method is called. */ var root; /** * The content for the tag generated by this object. This * can not be changed later, if [[.domupdate]] is set to "iframe". * So it must be set before calling [[.main()]], or not at all. */ var title = ""; /** * HTML code to be included between <head> and </head>. * Must be set before calling [[.main()]], or not at all. */ var html_head = ""; /** * HTML attributes to be set in the <body> tag. * Must be set before calling [[.main()]], or not at all. */ var body_attr = ""; /** * A hash with arrays of callback functions. Use [[.callback_add]] * and [[.callback_del]] to maintain the entries. */ var callbacks; /** * Add a callback function to the [[.callbacks]] data structure. * * The following callback types are called by this object: * * pre_update called before the html (xml) output is created. * post_update called after the html (xml) output is created. */ method callback_add(type, func) { push callbacks[type], func; } /** * Add a callback function from the [[.callbacks]] data structure. */ method callback_del(type, func) { foreach i (callbacks[type]) if (callbacks[type][i] ^== func) delete callbacks[type][i]; } /** * Call all callbacks of a type. The callback is passed the type as * first parameter and this [[WfsDocument]] object as 2nd parameter. * The named parameters are also passed thru to the callback functions. */ method callback_call(type, %options) { foreach[] c (callbacks[type]) c(type, this, %options); } /** * This method implements the main loop of a [[WsfDocument]]. * It must be called after setting up the variables described * below. * * It handles the creation of HTML pages which are then passed * to the browser. Whenever the [[WsfComponent.main()]] method * calls task_co_return() after processing a user event, control * is passed back to this method so it can update the browser window. * * This function does never return. */ method main() { function do_updates2(comp) { if (comp.main_task ~!= task_getname()) { comp.main_task = task_getname(); task_co_setdefault(comp.id, task_getname()); } foreach i (comp.children) do_updates2(comp.children.[i]); } function do_updates(comp, parents) { var html; if (comp.dirty or not defined comp.__WsfComponent_get_html_cached_data) { do_updates2(comp); html ~= comp.get_html_cached(); if ( domupdate ~== "iframe" ) { html ~= <<EOT <script><!-- copy_to_parent("${comp.id}"); //--></script> EOT; } else if ( domupdate ~== "xmlhttprequest" ) { /* nothing to do */ } else { foreach p (parents) parents[p].dirty = 1; } } else { push parents, comp; foreach i (comp.children) html ~= do_updates(comp.children.[i], parents); pop parents; } return html; } while (1) { // Not stating we are XHTML - this will give us // much more freedom with bad html code... // // <?xml version="1.0" encoding="iso-8859-1"?> // <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" // "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> // <html xmlns="http://www.w3.org/1999/xhtml"> write(<<EOT <html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8" /> <title>${title} ${html_head} EOT); if ( domupdate ~== "iframe" ) { if ( showiframe ) write(< EOT); else write(<

' : '
' } ${ defined current_object ? '    ' : '' }
$type $name$rest ${ type =~ /^($objectre)$/ ? "
\n{" : "" } ; // create 2nd HTML snippet html_part2 ~= <>

", flags, indenting_delim); else lex_new(VALUE, strdup("")); lex_new(')', 0); return; } if ( !strcmp(tag, "foreach") ) { if ( !attr_var || !attr_list ) { my_asprintf(&lex_errstr, " requires attributes 'var' and 'list'."); longjmp(lex_goterr, __LINE__); } lex_new(EF_BEGIN, 0); lex_new(VAR, 0); lex_new(ID, strdup("#iret")); lex_new(';', 0); lex_new(FOREACH, 0); spl_lex_prog_string(strdup(attr_var)); lex_new('(', 0); spl_lex_prog_string(strdup(attr_list)); lex_new(')', 0); lex_new(ID, strdup("#iret")); lex_new('=', 0); lex_new(ID, strdup("#iret")); lex_new(CAT, 0); spl_lex_string("", flags, indenting_delim); lex_new(';', 0); lex_new(RETURN, 0); lex_new(ID, strdup("#iret")); lex_new(';', 0); lex_new(EF_END, 0); return; } if ( !strcmp(tag, "indent") ) { lex_new('(', 0); spl_lex_string("", flags, attr_char ? *attr_char : 0); lex_new(')', 0); return; } if ( !strcmp(tag, "else") ) { my_asprintf(&lex_errstr, "Found tag without prior or tag."); longjmp(lex_goterr, __LINE__); } my_asprintf(&lex_errstr, "Unknown tag: %s", tag); longjmp(lex_goterr, __LINE__); } static char *strndup_with_indenting_delim(const char *str, int len, int indenting_delim, int *id_status) { if (!indenting_delim) return my_strndup(str, len); char *text = malloc(len + 1); int i=0, j=0; while (str[i] && i < len) { if (!*id_status) { if (!strchr(" \t\n\r", str[i])) { if (str[i] == indenting_delim) { if (str[++i] == ' ') i++; } *id_status = 1; continue; } } else { if (str[i] == '\n') *id_status = 0; text[j++] = str[i]; } i++; } text[j++] = 0; return realloc(text, j); } static void spl_lex_string(char *term, int flags, int indenting_delim) { int len, tlen = term ? strlen(term) : 0; int old_php_like_tags_active; int old_php_like_tags_indenting_delim; int old_lex_translate_count; struct lex_item *old_lex_translate_ptr; int called_in_php_like_tags_context = 0; int term_is_newline = 0; int id_status = 0; old_lex_translate_count = lex_translate_count; old_lex_translate_ptr = lex_translate_ptr; if (lex_item_list_last && lex_item_list_last->lex == ID) { char *translate_id = lex_item_list_last->text; int translate_id_len = strlen(translate_id); if (!strcmp(translate_id, "_")) { translate_id = 0; } else { if (translate_id_len < 3 || translate_id[0] != '_' || translate_id[translate_id_len-1] != '_') goto not_a_translate_call; translate_id = my_strndup(translate_id+1, translate_id_len-2); } lex_translate_ptr = lex_item_list_last; lex_item_list_last->lex = TRANSLATE_PREFIX; free(lex_item_list_last->text); lex_item_list_last->text = 0; flags &= ~LEX_STRING_SPLTAGS; lex_new(TRANSLATE_SEPERATOR, 0); if (translate_id) lex_new(VALUE, translate_id); else lex_new(UNDEF, 0); not_a_translate_call:; } if ( term && !strcmp(term, "\n") ) { term_is_newline = 1; tlen = 0; } old_php_like_tags_active = php_like_tags_active; old_php_like_tags_indenting_delim = php_like_tags_indenting_delim; php_like_tags_active = 0; if ( term && !strcmp(term, "") ) { php_like_tags_active = 1; called_in_php_like_tags_context = 1; indenting_delim = php_like_tags_indenting_delim; term = php_like_tags_term; tlen = term ? strlen(term) : 0; lex_new(ID, strdup("#tpldata")); lex_new(SAPPEND, 0); } if (!called_in_php_like_tags_context && (flags & LEX_STRING_SPLTAGS)) { lbstack_push(); lex_new(RVALUE_CONTEXT_BEGIN, 0); } for (len = 0; input[len]; len++) { if ( flags & (LEX_STRING_BACKSLASHES|LEX_STRING_REGEX) ) { if ( input[len] == '\\' && input[len+1] ) { len++; continue; } } if ( term ) { if ( term_is_newline ) { if (input[len] == '\r' || input[len] == '\n') { if (input[len] == '\r') len++; if (input[len] == '\n') len++; break; } } else if ( !strncmp(input+len, term, tlen) ) break; } if ( flags & LEX_STRING_SPLTAGS ) { if ( !strncmp(input+len, ""); input += 2; lex_new(ID, strdup("#tpldata")); lex_new(SAPPEND, 0); len = -1; continue; } } if ( input[len] == '$' && (!(flags & LEX_STRING_REGEX) || isalpha((unsigned char)input[len+1]) || strchr("_({$:[", input[len+1])) ) { spl_lex_new_string(strndup_with_indenting_delim(input, len, indenting_delim, &id_status), flags); input += len + 1; if (lex_translate_ptr) { char *translate_token; my_asprintf(&translate_token, "{%d}", lex_translate_count++); lex_new_translate(CAT, 0); lex_new_translate(VALUE, translate_token); lex_new(TRANSLATE_SEPERATOR, 0); } else lex_new(CAT, 0); switch ( *input ) { case '(': lex_new(EF_BEGIN, 0); input++; spl_lex_prog(")"); lex_new(EF_END, 0); input++; break; case '{': lex_new('(', 0); input++; spl_lex_prog("}"); lex_new(')', 0); input++; break; case '@': case '#': case ']': { char prog[3] = { '$', *input, 0 }; spl_lex_prog_string(strdup(prog)); input++; break; } case '$': case ':': case '?': len = 0; goto skip_2nd_cat_op; case ' ': case '\t': case '\r': case '\n': len = -1; while (*input == ' ' || *input == '\t') input++; if (*input == '\r') input++; if (*input == '\n') input++; goto skip_2nd_cat_op; case '[': len = -1; while (*input && *input != ']') input++; if (*input == ']') input++; goto skip_2nd_cat_op; case '<': { char *regex_result_id; int inner_len = strcspn(++input, ">"); my_asprintf(®ex_result_id, "#reres.%.*s", inner_len, input); lex_new(ID, regex_result_id); input += inner_len; if (*input == '>') input++; break; } case '-': case '+': case '0' ... '9': { char *regex_result_id; int inner_len = 1; /* strspn(input, "0123456789"); */ if (*input == '-' || *input == '+') my_asprintf(®ex_result_id, "#reres.%s", *input == '-' ? "=NC" : "=LC"); else my_asprintf(®ex_result_id, "#reres.%.*s", inner_len, input); lex_new(ID, regex_result_id); input += inner_len; break; } case 'a' ... 'z': case 'A' ... 'Z': case '_': { int inner_len = 0; do inner_len++; while ( (input[inner_len] >= 'a' && input[inner_len] <= 'z') || (input[inner_len] >= 'A' && input[inner_len] <= 'Z') || (input[inner_len] >= '0' && input[inner_len] <= '9') || input[inner_len] == '_' || input[inner_len] == '.'); while (input[inner_len-1] == '.') inner_len--; lex_new('(', 0); spl_lex_prog_string(my_strndup(input, inner_len)); lex_new(')', 0); input += inner_len; break; } default: my_asprintf(&lex_errstr, "Unrecognised $ substitution in string."); longjmp(lex_goterr, __LINE__); break; } lex_new_translate(CAT, 0); len = -1; skip_2nd_cat_op:; } } spl_lex_new_string(strndup_with_indenting_delim(input, len, indenting_delim, &id_status), flags); input += len + tlen; if (php_like_tags_active) { lex_new(';', 0); lex_new(INSERT_PROG_END, 0); lex_new(VALUE, strdup("push \"#tpldata\"")); lex_new(';', 0); } if (!called_in_php_like_tags_context && (flags & LEX_STRING_SPLTAGS)) { lex_new(RVALUE_CONTEXT_END, 0); lbstack_pop(); } if (lex_translate_ptr && !old_lex_translate_ptr) { lex_new(TRANSLATE_END, 0); lex_translate_count = old_lex_translate_count; lex_translate_ptr = old_lex_translate_ptr; } php_like_tags_active = old_php_like_tags_active; php_like_tags_indenting_delim = old_php_like_tags_indenting_delim; } static void spl_lex_prog_string(char *text) { const char *oldinput = input; struct lex_srcfile *oldsrc = lex_srcfile_current; lex_srcfile_current = malloc(sizeof(struct lex_srcfile)); lex_srcfile_current->text = input = text; my_asprintf((char**)&lex_srcfile_current->name, "%s:byte(%d)", oldsrc->name, (int)(oldinput - oldsrc->text)); lex_srcfile_current->next = lex_srcfile_list; lex_srcfile_list = lex_srcfile_current; spl_lex_prog(0); lex_srcfile_current = oldsrc; input = oldinput; } static void spl_lex_prog(char *term) { int tlen = term ? strlen(term) : 0; int last_was_id = 0; struct lex_item *old_lex_translate_ptr = lex_translate_ptr; int old_lex_translate_count = lex_translate_count; lex_translate_ptr = 0; lex_translate_count = 0; next_symbol: if ( last_was_id > 0 ) last_was_id--; input += strspn(input, " \t\r\n"); switch (*input) { case 0: if (!term) goto lex_prog_return; my_asprintf(&lex_errstr, "Unexpected end-of-file (expected '%s').", term); longjmp(lex_goterr, __LINE__); case '"': case '\'': { char *new_term = my_strndupa(input++, 1); spl_lex_string(new_term, LEX_STRING_BACKSLASHES, 0); goto next_symbol; } case '#': if ((input[1] < 'a' || input[1] > 'z') && input[1] != '!') goto just_a_normal_operator; input++; spl_lex_pragma(); goto next_symbol; case '.': if (input[1] < '0' || input[1] > '9') goto just_a_normal_operator; case '0' ... '9': if (input[0] == '0' && input[1] == 'x') { char *buffer; my_asprintf(&buffer, "%ld", strtol(input+2, (char**)&input, 16)); lex_new(VALUE, buffer); } else if (input[0] == '0' && input[1] == 'o') { char *buffer; my_asprintf(&buffer, "%ld", strtol(input+2, (char**)&input, 8)); lex_new(VALUE, buffer); } else if (input[0] == '0' && input[1] == 'b') { char *buffer; my_asprintf(&buffer, "%ld", strtol(input+2, (char**)&input, 2)); lex_new(VALUE, buffer); } else { int len = strspn(input, "0123456789."); while (input[len-1] == '.') len--; lex_new(VALUE, my_strndup(input, len)); input += len; } goto next_symbol; case '$': { char *regex_result_id; int len = 0; switch (input[1]) { case '<': input += 2; len = strcspn(input, ">"); my_asprintf(®ex_result_id, "#reres.%.*s", len++, input); break; case '$': len = 2; regex_result_id = strdup("#reres"); break; case '-': len = 2; regex_result_id = strdup("#reres.=NC"); break; case '+': len = 2; regex_result_id = strdup("#reres.=LC"); break; case '0' ... '9': input += 1; len = 1; /* strspn(input, "0123456789"); */ my_asprintf(®ex_result_id, "#reres.%.*s", len, input); break; case '@': len = 2; regex_result_id = strdup("#array"); break; case '#': len = 2; regex_result_id = strdup("#index"); break; case '[': case ']': len = 2; lex_new('(', 0); lex_new(LNOT, 0); lex_new(DEFINED, 0); lex_new('(', 0); if (input[1] == '[') lex_new(PREV, 0); else lex_new(NEXT, 0); lex_new(ID, strdup("#array")); lex_new(',', 0); lex_new(ID, strdup("#index")); lex_new(')', 0); lex_new(')', 0); goto ignore_regex_result_id; default: goto just_a_normal_operator; } lex_new(ID, regex_result_id); ignore_regex_result_id: input += len; last_was_id = 2; goto next_symbol; } case 'a' ... 'z': case 'A' ... 'Z': case '_': { int len = 0; while (1) { if (input[len] >= 'a' && input[len] <= 'z') len++; else if (input[len] >= 'A' && input[len] <= 'Z') len++; else if (input[len] >= '0' && input[len] <= '9') len++; else if (input[len] == '_') len++; else break; } char *text = my_strndup(input, len); input += len; if (!lex_item_list_last || lex_item_list_last->lex != '.' ) for (int i=0; lex_keywords[i].text; i++) if ( !strcmp(lex_keywords[i].text, text) ) { lex_new(lex_keywords[i].lex, 0); free(text); goto next_symbol; } if (!in_pragma_arg) for (struct define *def = define_list; def; def=def->next) { if ((!def->active || strcmp(current_define, def->name)) && !strcmp(def->name, text)) { char *expanded_text = strdup(def->text); free(text); if (define_recursion > 1000) { my_asprintf(&lex_errstr, "Endless recursion in macro expansion."); longjmp(lex_goterr, __LINE__); } struct define *args_start = 0; struct define *args_stop = 0; if (def->args) { char *this_args = strdup(def->args); char *this_arg, *this_args_p = this_args; input += strcspn(input, "("); if (*input) input++; this_arg = my_strsep(&this_args_p, ","); while (this_arg) { struct define *adef = calloc(1, sizeof(struct define)); adef->isarg = 1; adef->name = strdup(this_arg); in_pragma_arg=1; const char *begin = input; spl_lex_prog(this_args_p ? "," : ")"); adef->text=my_strndup(begin, input-begin); if (*input) input++; in_pragma_arg=0; adef->next = define_list; define_list = args_start = adef; if (!args_stop) args_stop = adef; this_arg = my_strsep(&this_args_p, ","); } free(this_args); int new_expanded_len = 1; for (int i=0; expanded_text[i]; i++) { if (expanded_text[i] == '<') { struct define *idef = args_start; while (idef) { if (!strncmp(expanded_text+i+1, idef->name, strlen(idef->name)) && expanded_text[i+1+strlen(idef->name)] == '>') { new_expanded_len += strlen(idef->text); i += strlen(idef->name) + 1; break; } if (idef == args_stop) break; idef = idef->next; } } new_expanded_len++; } char *new_expanded_text = malloc(new_expanded_len); int new_expanded_text_pos = 0; for (int i=0; expanded_text[i]; i++) { if (expanded_text[i] == '<') { struct define *idef = args_start; while (idef) { if (!strncmp(expanded_text+i+1, idef->name, strlen(idef->name)) && expanded_text[i+1+strlen(idef->name)] == '>') { strcpy(new_expanded_text+new_expanded_text_pos, idef->text); new_expanded_text_pos += strlen(idef->text); i += strlen(idef->name) + 1; goto do_not_copy_char; } if (idef == args_stop) break; idef = idef->next; } } new_expanded_text[new_expanded_text_pos++] = expanded_text[i]; do_not_copy_char:; } free(expanded_text); new_expanded_text[new_expanded_text_pos] = 0; expanded_text = new_expanded_text; } def->active = 1; define_recursion++; char *old_current_define = current_define; current_define = def->name; spl_lex_prog_string(expanded_text); current_define = old_current_define; define_recursion--; def->active = 0; struct define *idef = define_list; struct define **lastp = &define_list; while (idef) { if (idef == args_start) { *lastp = idef->next; free(idef->name); free(idef->text); if (idef->args) free(idef->args); free(idef); if (idef == args_stop) break; idef = args_start = *lastp; } else { lastp = &idef->next; idef = idef->next; } } goto next_symbol; } } lex_new(ID, text); last_was_id = 2; goto next_symbol; } case ']': lex_new(']', 0); input++; last_was_id = 2; goto next_symbol; case '}': case ')': if ( term && !strncmp(input, term, tlen) ) goto lex_prog_return; my_asprintf(&lex_errstr, "Unexpected '%c'.", *input); longjmp(lex_goterr, __LINE__); case '{': if (input[1] == '[') { lex_new(BLK_BEGIN_NOCTX, 0); input += 2; } else { lex_new('{', 0); input++; } spl_lex_prog("}"); lex_new('}', 0); input++; goto next_symbol; case '(': for (int i=0; lex_ops[i].text; i++) if ( !strncmp(input, lex_ops[i].text, strlen(lex_ops[i].text)) ) goto just_a_normal_operator; if ( input[1] == '{' ) { lex_new(EF_BEGIN, 0); input+=2; spl_lex_prog("})"); lex_new(EF_END, 0); input+=2; } else { if ( lex_item_list_last && lex_item_list_last->lex == ID ) { if ( lex_item_list_last->prev && lex_item_list_last->prev->lex != '.' && lex_item_list_last->prev->lex != ']' && lex_item_list_last->prev->lex != '&' && lex_item_list_last->prev->lex != '$' && lex_item_list_last->prev->lex != MUL && lex_item_list_last->prev->lex != FUNCTION && lex_item_list_last->prev->lex != METHOD && lex_item_list_last->prev->lex != FOREACH && lex_item_list_last->prev->lex != NEW ) lex_item_list_last->lex = FUNC_ID; if ( !lex_item_list_last->prev ) lex_item_list_last->lex = FUNC_ID; } lex_new('(', 0); input++; spl_lex_prog(")"); lex_new(')', 0); input++; } goto next_symbol; case '=': case '!': { if (input[1] != '~') goto just_a_normal_operator; int not_mode = *input == '!'; int eval_mode = 0; int len = 0; input += 2; input += strspn(input, " \t\r\n"); static char seplist[] = "/:,!%@"; char sepstr[] = "X"; struct lex_item *subst_lex = 0; if ( input[0] == 's' && strchr(seplist, input[1]) ) { sepstr[0] = input[1]; subst_lex = lex_new(not_mode ? NREGEX_SUBST : REGEX_SUBST, 0); input += 2; spl_lex_string(sepstr, LEX_STRING_REGEX, 0); lex_new(REGEX_SEP, 0); spl_lex_string(sepstr, LEX_STRING_REGEX, 0); lex_new(REGEX_SEP, 0); } else if ( input[0] == 'e' && strchr(seplist, input[1]) ) { eval_mode = 1; sepstr[0] = input[1]; subst_lex = lex_new(not_mode ? NREGEX_EVAL : REGEX_EVAL, 0); input += 2; spl_lex_string(sepstr, LEX_STRING_REGEX, 0); lex_new(REGEX_SEP, 0); } else if ( strchr(seplist, input[0]) ) { sepstr[0] = input[0]; lex_new(not_mode ? NREGEX : REGEX, 0); input++; spl_lex_string(sepstr, LEX_STRING_REGEX, 0); lex_new(REGEX_SEP, 0); } else { my_asprintf(&lex_errstr, "Error in regex syntax."); longjmp(lex_goterr, __LINE__); } while ( isalpha((unsigned char)input[len]) ) len++; char *mod = my_strndup(input, len); if (subst_lex && strchr(mod, 'R')) { if (subst_lex->lex == REGEX_SUBST) subst_lex->lex = REGEX_SUBST_R; if (subst_lex->lex == NREGEX_SUBST) subst_lex->lex = NREGEX_SUBST_R; if (subst_lex->lex == REGEX_EVAL) subst_lex->lex = REGEX_EVAL_R; if (subst_lex->lex == NREGEX_EVAL) subst_lex->lex = NREGEX_EVAL_R; } if (eval_mode) { char *new_mod; if (strchr(mod, 'N') || strchr(mod, 'P') || strchr(mod, 'A') || strchr(mod, 'I') || strchr(mod, 'E') || strchr(mod, 'L')) { my_asprintf(&lex_errstr, "Error in regex syntax."); longjmp(lex_goterr, __LINE__); } my_asprintf(&new_mod, "%sEANP", mod); free(mod); mod = new_mod; } lex_new(VALUE, mod); input += len; lex_new(REGEX_SEP, 0); goto next_symbol; } case '/': if (input[1] == '/') { while (*input && *input != '\r' && *input != '\n') input++; goto next_symbol; } if (input[1] == '*') { while (input[0] && input[1] && (input[0] != '*' || input[1] != '/')) input++; if (*input) input++; if (*input) input++; goto next_symbol; } goto just_a_normal_operator; case '<': { int i = 1, with_indent = 0; if (input[i] == ':') { with_indent = 1; i++; } while (isalpha((unsigned char)input[i])) i++; if (input[i] == '>') { char endtag[i+32]; snprintf(endtag, i+32, "", i-(1+with_indent), input+1+with_indent); input += i+1; if (with_indent) input += strspn(input, " \t\r\n"); spl_lex_string(endtag, LEX_STRING_SPLTAGS, with_indent ? ':' : 0); goto next_symbol; } goto just_a_normal_operator; } case '?': if (php_like_tags_active && input[1] == '>') { if (!strcmp(term, "?>")) goto lex_prog_return; input += 2; spl_lex_string("", LEX_STRING_SPLTAGS, 0); goto next_symbol; } goto just_a_normal_operator; just_a_normal_operator: default: { if ( term && !strncmp(input, term, tlen) ) goto lex_prog_return; for (int i=0; lex_ops[i].text; i++) if ( !strncmp(input, lex_ops[i].text, strlen(lex_ops[i].text)) ) { input += strlen(lex_ops[i].text); switch ( lex_ops[i].lex ) { case STRING_EOL: { if ( *input == ' ' || *input == '\t' ) input++; int len = strcspn(input, "\r\n"); if ( input[len] == '\r' ) len++; if ( input[len] == '\n' ) len++; lex_new(VALUE, my_strndup(input, len)); input += len; goto next_symbol; } case STRING_EOL_S: { if ( *input == ' ' || *input == '\t' ) input++; spl_lex_string("\n", LEX_STRING_BACKSLASHES, 0); goto next_symbol; } case STRING_LABEL: { input += strspn(input, " \t\r\n"); int len = strspn(input, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); char *label = my_strndupa(input, len); char indenting_delim = 0; input += len; if ( *input == ' ' || *input == '\t' || *input == '\r' || *input == '\n' ) input++; else { indenting_delim = *(input++); if ( *input == ' ' || *input == '\t' || *input == '\r' || *input == '\n' ) input++; } char *end = strstr(input, label); if (!end) { my_asprintf(&lex_errstr, "End label missing."); longjmp(lex_goterr, __LINE__); } int id_status = 0; lex_new(VALUE, strndup_with_indenting_delim(input, end-input, indenting_delim, &id_status)); input = end + len; goto next_symbol; } case STRING_LABEL_S: { input += strspn(input, " \t\r\n"); int len = strspn(input, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); char *label = my_strndupa(input, len); char indenting_delim = 0; input += len; if ( *input == ' ' || *input == '\t' || *input == '\r' || *input == '\n' ) input++; else { indenting_delim = *(input++); if ( *input == ' ' || *input == '\t' || *input == '\r' || *input == '\n' ) input++; } spl_lex_string(label, LEX_STRING_BACKSLASHES, indenting_delim); goto next_symbol; } default: if (lex_ops[i].lex == SPECIALREF && lex_item_list_last && (lex_item_list_last->lex == ID || lex_item_list_last->lex == ']' || lex_item_list_last->lex == SPECIALREF)) lex_new('.', 0); if (lex_ops[i].lex == SPECIALREF) { char *lookupcode = ""; if (input[-2] == '*') lookupcode = ""; if (input[-2] == '+') lookupcode = "!CLS"; if (input[-2] == '/') lookupcode = "!ROOT"; if (input[-2] == '.') lookupcode = "!THIS"; lex_new(lex_ops[i].lex, strdup(lookupcode)); } else lex_new(lex_ops[i].lex, 0); goto next_symbol; } } if (*input == '[' && lex_item_list_last && (lex_item_list_last->lex == ID || lex_item_list_last->lex == ')' || lex_item_list_last->lex == ']' || lex_item_list_last->lex == SPECIALREF)) lex_new('.', 0); lex_new(*(input++), 0); goto next_symbol; } } longjmp(lex_goterr, __LINE__); lex_prog_return: lex_translate_ptr = old_lex_translate_ptr; lex_translate_count = old_lex_translate_count; } static char *last_debug_info; static void create_debug_op(int force) { char *newinfo; if (!gen_debug_info) return; int lineno = 1, charno = 1; const char *c = lex_last_item->src->text; while (c < lex_last_item->pos) if (*(c++) == '\n' ) lineno++, charno=1; else charno++; my_asprintf(&newinfo, "%d:%d:%s", lineno, charno, lex_last_item->src->name); if (!force && last_debug_info && !strcmp(last_debug_info, newinfo)) { free(newinfo); return; } if (last_debug_info) free(last_debug_info); last_debug_info = newinfo; spl_asm_add(as, SPL_OP_DBGSYM, newinfo); } static char *get_encoding_pragma(const char *text) { char *token = "#encoding "; int token_size = strlen(token); const char *begin = strstr(text, token); if ( !begin ) return 0; begin += token_size; token_size = strcspn(begin, " \r\n\t"); if ( token_size <= 0 ) return 0; return my_strndup(begin, token_size); } extern int spl_compiler(struct spl_asm *a, const char *prog, const char *name, spl_malloc_file_function *mff, int gd) { #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_lock(&compiler_lck); #endif as = a; malloc_file_func = mff; gen_debug_info = gd; no_checkp_insn = 0; php_like_tags_active = 0; php_like_tags_term = 0; define_list = 0; current_define = ""; define_recursion = 0; in_pragma_arg = 0; rvtolv_counter = 0; import_asm_label_counter = 0; if ( as->labels ) { spl_report(SPL_REPORT_COMPILER, as, "Compiler called with an assembler with non-empty label list!\n"); #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_unlock(&compiler_lck); #endif return -1; } encoding = get_encoding_pragma(prog); if (encoding) { prog = spl_utf8_import(prog, encoding); if (!prog) { free(encoding); spl_report(SPL_REPORT_COMPILER, as, "Character set (#encoding) '%s' is unknown!\n", encoding); #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_unlock(&compiler_lck); #endif return -1; } } if ( spl_utf8_check(prog) ) { spl_report(SPL_REPORT_COMPILER, as, "Compiler called with non-utf8 encoded program code!\n" "A decoder bug or just a missing '#encoding' pragma?\n"); if (encoding) { free(encoding); free((char*)prog); } #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_unlock(&compiler_lck); #endif return -1; } input = prog; lex_srcfile_current = malloc(sizeof(struct lex_srcfile)); lex_srcfile_current->next = 0; lex_srcfile_current->text = input; lex_srcfile_current->name = name; lex_srcfile_list = lex_srcfile_current; lex_item_list_first = 0; lex_item_list_last = 0; lex_last_item = 0; lex_errstr = strdup("Generic lexer error"); label_stack_index = 0; label_stack_counter = 0; label_stack[label_stack_index] = label_stack_counter; breakcont_stack_index = 0; breakcont_stack_counter = 0; breakcont_stack[breakcont_stack_index] = breakcont_stack_counter; packpatch_stack_index = 0; last_debug_info = 0; // the 'volatile' is to make the compiler happy. // (a volatile variable can't be clobbered by a 'longjmp') volatile int ret = 1; int errlnr = 0; // this is to make the compiler happy. // (the new prog_free_ptr can't be clobbered by a 'longjmp') void * volatile prog_free_ptr = (void *)prog; if ( !(errlnr = setjmp(lex_goterr)) ) { spl_lex_prog(0); ret = spl_yyparse (); } else { int lineno = 1, charno = 1; const char *c = lex_srcfile_current->text; const char *orig_input = c; while (c < input) if (*(c++) == '\n' ) lineno++, charno=1; else charno++; char *code; my_asprintf(&code, "%.40s", input-30 > orig_input ? input-30 : orig_input); for (int i=0; code[i]; i++) if ( *input == '\r' || code[i] == '\n' || code[i] == '\t' ) code[i] = ' '; spl_report(SPL_REPORT_LEXER, as, "[%d] near line %d, char %d in %s: %s\n>> %s\n %*s^\n", errlnr, lineno, charno, lex_srcfile_current->name, lex_errstr, code, 30, ""); free(code); } if ( !ret && packpatch_stack_index ) { spl_yyerror("Parser returned with non-empty packpatch stack!"); ret = 1; } if ( !ret && label_stack_index ) { spl_yyerror("Parser returned with non-empty label stack!"); ret = 1; } while ( define_list ) { struct define *next = define_list->next; free(define_list->name); free(define_list->text); if (define_list->args) free(define_list->args); free(define_list); define_list = next; } while ( lex_srcfile_list ) { struct lex_srcfile *next = lex_srcfile_list->next; if (next) { // we allocated those, so we can cast them // to non-const and free them. free((char*)lex_srcfile_list->text); free((char*)lex_srcfile_list->name); } free(lex_srcfile_list); lex_srcfile_list = next; } if (last_debug_info) free(last_debug_info); if (lex_last_item) free(lex_last_item); free(lex_errstr); spl_asm_resolve_labels(as); if (encoding) { free(encoding); free(prog_free_ptr); } #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_unlock(&compiler_lck); #endif return ret; } static void spl_yyerror (char const *err) { int lineno = 1, charno = 1; const char *c = lex_last_item->src->text; const char *orig_input = c; while (c < lex_last_item->pos) if (*(c++) == '\n' ) lineno++, charno=1; else charno++; char *code; my_asprintf(&code, "%.40s", lex_last_item->pos-30 > orig_input ? lex_last_item->pos-30 : orig_input); for (int i=0; code[i]; i++) if ( *input == '\r' || code[i] == '\n' || code[i] == '\t' ) code[i] = ' '; spl_report(SPL_REPORT_COMPILER, as, "near line %d, char %d in %s: %s\n>> %s\n %*s^\n", lineno, charno, lex_last_item->src->name, err, code, 30, ""); free(code); } spl-1.0pre6/report.c0000644000000000000000000000660511064463357013142 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * report.c: Functions for error-reporting */ #include #include #include #include #include "spl.h" #include "compat.h" spl_report_func *spl_report = spl_report_func_stderr; void spl_set_report_func(spl_report_func *f) { spl_report = f; } char *spl_report_type2txt(int type) { static char retbuf[100]; int i = 0; i += snprintf(retbuf+i, sizeof(retbuf)-i, "SPL"); if ( type & SPL_REPORT_DEBUG ) i += snprintf(retbuf+i, sizeof(retbuf)-i, " Debug"); else { if ( (type & 0x000f) == SPL_REPORT_ASSEMBLER ) i += snprintf(retbuf+i, sizeof(retbuf)-i, " Assembler"); if ( (type & 0x000f) == SPL_REPORT_COMPILER ) i += snprintf(retbuf+i, sizeof(retbuf)-i, " Compiler"); if ( (type & 0x000f) == SPL_REPORT_LEXER ) i += snprintf(retbuf+i, sizeof(retbuf)-i, " Lexer"); if ( (type & 0x000f) == SPL_REPORT_RUNTIME ) i += snprintf(retbuf+i, sizeof(retbuf)-i, " Runtime"); if ( (type & 0x000f) == SPL_REPORT_RESTORE ) i += snprintf(retbuf+i, sizeof(retbuf)-i, " Restore"); if ( type & SPL_REPORT_WARNING ) i += snprintf(retbuf+i, sizeof(retbuf)-i, " Warning"); else i += snprintf(retbuf+i, sizeof(retbuf)-i, " Error"); } return retbuf; } void spl_report_file(FILE *f, int type, void *desc, const char *fmt, va_list ap) { char *msg; my_vasprintf(&msg, fmt, ap); fprintf(f, "%s: %s", spl_report_type2txt(type), msg); free(msg); if ( (type & 0x000f) == SPL_REPORT_RUNTIME && desc && (type & SPL_REPORT_DEBUG) == 0) { struct spl_task *task = desc; if ((type & SPL_REPORT_WARNING) == 0) task->goterr = 1; spl_backtrace(task, f); } } char *spl_report_string(int type, void *desc, const char *fmt, va_list ap) { #if !defined USEWIN32API && !defined USECYGWINAPI && !defined USEMACOSXAPI && !defined USEBSDAPI && !defined USEIRIXAPI char *bp; size_t size; FILE *stream; stream = open_memstream(&bp, &size); spl_report_file(stream, type, desc, fmt, ap); fclose(stream); return bp; #else FILE *tempfile = tmpfile(); spl_report_file(tempfile, type, desc, fmt, ap); size_t size = ftell(tempfile); char *bp = malloc(size + 1); rewind(tempfile); fread(bp, size, 1, tempfile); bp[size] = 0; fclose(tempfile); return bp; #endif } void spl_report_func_stderr(int type, void *desc, const char *fmt, ...) { va_list ap; va_start(ap, fmt); spl_report_file(stderr, type, desc, fmt, ap); fflush(stderr); va_end(ap); } void spl_report_func_stdout(int type, void *desc, const char *fmt, ...) { va_list ap; va_start(ap, fmt); spl_report_file(stdout, type, desc, fmt, ap); fflush(stderr); va_end(ap); } spl-1.0pre6/README0000644000000000000000000002312510431425060012321 0ustar rootroot What is SPL ? ============= Overview -------- SPL is an embeddable programming language with a wide range of features: * complete stateful. It is at any point possible to interrupt a running SPL script, dump its entire state to disk and resume later on. * feature-rich. SPL has native support for hashes and arrays, regular expressions, object oriented programming, etc. * dynamic. SPL is a fully dynamic language - with all the advantages and disadvantages. * c-style syntax. SPL has a c-style syntax (as well as many other languages such as Java, JavaScript, PHP, slang, etc). so it is easier to get started. * advanced string lexing. SPL allows the programmer to simply embed variables and complex expressions in strings and template files. E.g. this is very important for rapid development of web applications. * well-structured backend. The SPL runtime is not just one big blackbox. Instead, there is a clear and visible seperation of compiler, assembler, optimizer, virtual machine, etc. This makes it possible to easily adapt the library for your special needs when embedding it in your applications. * more, more, more,.. The Name "SPL" is a left-recursive acronym and expands to "SPL Programming Language". The name was meant to be pronounced as an acronym, but I've already heard people pronouncing it "spell", which is also fine with me. The SPL VM is a pure bytecode interpeter. Support for JIT compilation or generating machine code for the host CPU is not planned and doesn't make much sense for various technical reasons. The entire SPL toolchain (compiler, assembler, virtual machine, etc) is pretty small (about 100k on x86 architectures). The additional memory usage by the applications is rather small too. WebSPL is a pretty powerful framework for doing web application development with SPL. The interesting thing about WebSPL is that, other than usual CGI scripts, a WebSPL script is not executed once for each HTTP request. Instead there is one SPL process for each session and such a WebSPL script can pause it's execution at any time, wait for the user to do something and continue execution as soon as the user did something. In addition to that there is a module package called "WSF" (WebSPL Forms) which adds an additional abstraction layer between the application logic and the representation as web page. With this SPL modules, web application development becomes as easy as normal application development with well-designed widget sets. Why should I learn SPL ? ------------------------ Maybe you want to use some software which is either built with SPL or using SPL as embedded scripting language (such as QCAKE - www.qcake.org). In that case you have answered that question already. If you are doing Web Application development you defenitely want to have a look at the WebSPL framework (README.WEBSPL or chapter 3 in the manual). It is an entirely new concept of writing web applications. If you are looking for a scripting language to embed in your applications, SPL might be a great choice. Being easy to embed and to adapt was one of the SPL design goals. Don't look into SPL if you want to manage a lot of binary data. After all, it is a scripting language. In all other cases - SPL might still be very interesting for you. It is a very flexible and powerful programming language and maybe you find another interesting field in which SPL is at its full strengths. Executing SPL scripts on the command line ----------------------------------------- Often SPL is embedded in other applications. In this cases it depends on the application how the SPL scripts can be executed. But SPL can also be used on the command line using the 'splrun' program. E.g.: splrun hanoi.spl It is also possible to pass a small SPL script directly on the command line: splrun -q 'debug "Hello World!";' SPL is not optimized for fast compilation. Often compilation of a script takes longer than execution. If this is a problem for you, you should pre-compile your SPL scripts to SPL bytecode files: splrun -x hanoi.splb hanoi.spl Such SPL bytecode files can then be executed using the -c option: splrun -c hanoi.splb With the -m option it is even possible to create executeable files with the look and feel of compiled C programs: splrun -m hanoi hanoi.spl ./hanoi Usually it is a good idea to pass the option -e when compiling SPL scripts (or running not pre-compiled scripts). This option enables debug symbols so that runtime error messages are printed with the line number in which the error was triggered. Without this option only the offset in the current bytecode block is contained in runtime error messages. Calling 'splrun' without any parameter prints an overview of all available command line options. Little dump/restore demonstration --------------------------------- The 'splrun' runtime and the "termio" module implement some CLIB functions: write(text) write the text clear() clear the screen setpos(x,y) set a new cursor position sleep() sleep for 1 second These functions are used by the demo 'hanoi.spl' to play a little game of towers of hanoi: type "./splrun -d hanoi.spld hanoi.spl". If you interrupt the program by pressing Ctrl-C, the full VM state will be stored in the file 'hanoi.spld'. It can be resumed where its has been interrupted any time by calling "./splrun -R hanoi.spld". Documentation ------------- README ........... This short introduction (manual chapter 1) INSTALL .......... Installation Instructions (manual chapter 2) README.LANG ...... Language Reference Manual (manual chapter 3) README.WEBSPL .... WebSPL and WSF Tutorial (manual chapter 4) spldoc/ .......... SPL Module Reference (next manual chapters) README.API ....... C API Reference (the last chapter) The module references are built using SPLDOC. Simply run 'make spldoc' to create the "spldoc/" directory with the module documentations. The SPL Reference Manual is automatically generated from this sources. The PDF can be downloaded from the SPL homepage, a "make spldoc" also creates the "manual.tex" file. WebSPL ------ WebSPL makes use of the dump and restore features of SPL and creates a state over the stateless HTTP protocol. Applications can simply generate a webpage and then dump to disk and wait for the user to do something and resume when the user sends a new request. Install 'webspl.cgi' in your cgi-bin, copy the 'spl_modules/' directory to the same location and create a 'webspl_cache/' directory in cgi-bin as well. This directory must be writable by the apache user, it is used for dump files. Add somthing like this to your apache config (httpd.conf): AddType application/x-httpd-webspl .webspl Action application/x-httpd-webspl /cgi-bin/webspl.cgi You might also want to restrict access to files such as your templates to prohibit users from simply downloading them as ASCII files. Note that 'webspl.cgi' needs write-permissions in a subdirectory 'webspl_cache' of the directory which contains 'webspl.cgi'. For just trying out WebSPL, you can also simply start a seperate apache instance in the SPL source directory: httpd -d $PWD -c "DocumentRoot $PWD" -f httpd.conf mozilla http://localhost:3054/webspl_demo/friends.webspl mozilla http://localhost:3054/webspl_demo/wsfdemo.webspl mozilla http://localhost:3054/webspl_demo/wsfgraph.webspl mozilla http://localhost:3054/webspl_demo/wsf_dialog.webspl kill $( cat apache.pid ) The 'example-apps' package from the SPL webpage contains some additional SPL and WebSPL demo applications. E.g. the 'cfpmanager' and 'splticket' applicationes: mozilla http://localhost:3054/cfpmanager/cfpmanager.webspl mozilla http://localhost:3054/splticket/splticket.webspl But before you can use them, you need to run the 'setup.sh' script in the 'cfpmanager' or 'splticket' directory respectively. Instead of apache you can also use the native WebSPL HTTP server. It does not support stuff such as directory indexes and is not optimal for static content, but is really fast for executing WebSPL scripts because it can hold the sessions in memory and only dump them to disk when it is required and it caches the SPL VM bytecode and so avoids redundant recompilations of the same source file: ./webspld For best performance it is recommended to use the WebSPL HTTP server for the WebSPL scripts and Apache for the rest. One way of integrating them is using the Apache reverse proxy module. Both WebSPL runtimes (the cgi script and the HTTP server) are looking for files named 'webspl.conf' in the script directory and all parent directories. This webspl.conf files can be used to configure the WebSPL runtime environemnt and particular WebSPL scripts. These files contain sections, each section starting with '[ filename-pattern ]', containing key-value pairs: [ foo-*.webspl ] spl.expirelocation = /expired.html [ bar-*.webspl ] spl.sessioncookie = bar_sid spl.respawnsessions = 1 The configuration variable 'spl.expirelocation' contains a URL. Browsers will be redirected to that URL when the User is trying to resume an expired session. The configuration variable 'spl.sessioncookie' contains the name of a cookie which then can be used to pass the session-id (instead of the 'sid' query string variable). The configuration variable 'spl.respawnsessions', when set to 1, lets the WebSPL framework automatically start a new session when the session passed by the browser has expired. Any key-value pairs can be specified in the webspl.conf files. Only the few described here are used by the webspl runtime environment, but others can be interpreted by modules or applications (see 'cgi.config' in the module documentation of the "cgi" module). spl-1.0pre6/ac-spl.m40000644000000000000000000000342610310304024013054 0ustar rootroot# # SPL - The SPL Programming Language # Copyright (C) 2004, 2005 Clifford Wolf # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ac-spl.m4: The SPL autoconf (aclocal) module # AC_DEFUN([SPL_CHECK], [ AC_ARG_WITH(spl, AS_HELP_STRING([--with-spl=DIR], [prefix of the SPL installation]), spl_prefix="$withval", spl_prefix="") AC_MSG_CHECKING(for spl-config) spl_config_bin="spl-config" if test "x$spl_prefix" != "x"; then spl_config_bin="$spl_prefix/bin/$spl_config_bin" fi if $spl_config_bin --cflags > /dev/null 2> /dev/null then AC_MSG_RESULT(yes) AC_MSG_CHECKING(for spl cflags) SPL_CFLAGS="$( $spl_config_bin --cflags )" AC_MSG_RESULT($SPL_CFLAGS) AC_MSG_CHECKING(for spl ldflags) SPL_LDFLAGS="$( $spl_config_bin --ldflags )" AC_MSG_RESULT($SPL_LDFLAGS) AC_MSG_CHECKING(for spl ldlibs) SPL_LDLIBS="$( $spl_config_bin --ldlibs )" AC_MSG_RESULT($SPL_LDLIBS) ifelse([$1], , :, [$1]) else AC_MSG_RESULT([no (can not execute $spl_config_bin)]) SPL_CFLAGS=""; SPL_LDFLAGS=""; SPL_LDLIBS="" ifelse([$2], , :, [$2]) fi AC_SUBST(SPL_CFLAGS) AC_SUBST(SPL_LDFLAGS) AC_SUBST(SPL_LDLIBS) ]) spl-1.0pre6/webspl_demo/0000755000000000000000000000000011300476114013740 5ustar rootrootspl-1.0pre6/webspl_demo/entry_edit.spltpl0000644000000000000000000000127510350526163017357 0ustar rootroot
Name: Friends:
${friends_list}
Phone:
E-Mail:
Address:
spl-1.0pre6/webspl_demo/wsfd_calc.webspl0000644000000000000000000000235110235723304017106 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * wsf_dialog.webspl: Little demo for WSF Dialogs */ load "wsf_dialog"; load "file"; object WsfDialogFile WsfDialog { var filename; method save() { file_write(filename, tree2xml(xmltree)); } method init(file) { filename = file; return *WsfDialog.init(file_read(file)); } } var page = new WsfDocument(); page.root = new WsfDialogFile("wsfd_calc.xml"); page.root.show_edit_button = 1; page.main(); spl-1.0pre6/webspl_demo/webspl.conf0000644000000000000000000000034110352216362016104 0ustar rootroot # The sessioncookie.webspl script is an example for storing session IDs # in cookies. So it needs this settings in order to work correctly. # [ sessioncookie.webspl ] spl.sessioncookie = demosession spl.respawnsessions = 1 spl-1.0pre6/webspl_demo/w2tdemo_svg.webspl0000644000000000000000000000413010427662761017432 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2006 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * w2tdemo.webspl: Simple example for the w2t module. */ load "w2t"; var page = new W2t(); var xpos = 50; page.register_callback("clicked_on_box", function(xmlnode, word1, word2) { debug "Pushed the button: $word1 $word2"; var newboxid = page.getid(); function newboxclick() { page.response_add(<:> : ); } page.register_callback('${newboxid}_click', newboxclick); page.response_add(<:> : : : ); xpos += 20; }); // Set startup page page.startup = <:> : : : : : : : : : : ; page.run(); spl-1.0pre6/webspl_demo/window.spltpl0000644000000000000000000000340610350526163016516 0ustar rootroot
${content.title} X
${content.get_html()}
spl-1.0pre6/webspl_demo/wsfd_calc.xml0000644000000000000000000000667710210552637016433 0ustar rootroot 0 values.result = 0; values.in = ""; Reset values.result += values.in; values.in = ""; + values.result -= values.in; values.in = ""; - values.result *= values.in; values.in = ""; * if ( values.in == 0 ) debug "Division by zero!"; else values.result /= values.in; values.in = ""; / values.in ~= "1"; 1 values.in ~= "2"; 2 values.in ~= "3"; 3 values.in ~= "4"; 4 values.in ~= "5"; 5 values.in ~= "6"; 6 values.in ~= "7"; 7 values.in ~= "8"; 8 values.in ~= "9"; 9 values.in ~= "0"; 0 values.in =~ s/.$//; Delete spl-1.0pre6/webspl_demo/friends.webspl0000644000000000000000000001254210350526163016620 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * friends.webspl: A nice example for a WebSPL application */ load "task"; load "cgi"; /* * Friends Data */ var friends; object Friend { var id; static friends_id_counter = 1; var name = "Unnamed"; var phone = ""; var email = ""; var addr = ""; var links; var template = undef; var sid = undef; // interface for Win object var title = "unnamed"; var winid = undef; method get_html() { var friends_list = ""; var html; foreach f (links) { friends_list = friends_list ~ '(X)\n' ~ '${friends.[f].name}
\n'; } if ( template ~== "show" ) html = #file-as-template entry_show.spltpl; else if ( template ~== "edit" ) html = #file-as-template entry_edit.spltpl; return html; } method winmain(_sid) { title = name; sid = _sid; while (1) { template = "show"; bother_user(); if ( declared cgi.param.edit ) { template = "edit"; bother_user(); name = cgi.param.new_name; phone = cgi.param.new_phone; email = cgi.param.new_email; addr = cgi.param.new_addr; title = name; } if ( declared cgi.param.delfriend ) { delete friends.[id].links.[cgi.param.delfriend]; delete friends.[cgi.param.delfriend].links.[id]; } if ( declared cgi.param.delete ) { delete friends.[id]; foreach f (friends) delete friends.[f].links.[id]; windows.[winid].finish(); } } } method init(n) { if (defined n) name = n; id = "friend" ~ friends_id_counter++; friends.[id] = this; return id; } } // create some dummy data { function linkFriends(a, b) { friends.[a].links.[b] = 1; friends.[b].links.[a] = 1; } var raffi = new Friend("Raffi"); var karin = new Friend("Karin"); var stefan = new Friend("Stefan"); var philipp = new Friend("Philipp"); var kim = new Friend("Kim"); var thomas = new Friend("Thomas"); var brigitte = new Friend("Brigitte"); linkFriends(raffi, karin); linkFriends(raffi, kim); linkFriends(raffi, thomas); linkFriends(karin, kim); linkFriends(stefan, philipp); linkFriends(stefan, kim); linkFriends(philipp, kim); linkFriends(thomas, brigitte); } /* * Windowmanager */ var wmsid = cgi.sid; var windows; object Win { static windows_z_counter = 0; static windows_id_counter = 0; var id, sid; var content; var posx = 100; var posy = 100; var posz = 0; method get_html() { return #file-as-template window.spltpl; } method main() { content.winmain(cgi.sid); } method finish() { content.title = undef; content.winid = undef; delete windows.[id]; write( #file-as-template main.spltpl ); task_kill(id); } method init(content_ptr) { if ( defined content_ptr.winid ) { windows.[content_ptr.winid].posz = windows_z_counter++; return undef; } id = "win" ~ windows_id_counter++; windows.[id] = this; content = content_ptr; content.winid = id; posy = 50 + ((windows_id_counter-1) % 5)*20; posx = windows.[id].posy + 150; posz = windows_z_counter++; task_create(id, "main();", this); task_public(id); task_continue(id); return id; } } function menu_html() { var html = "

\n"; foreach f (friends) { html = html ~ '${friends.[f].name}
\n'; } html = html ~ '

\n'; html = html ~ 'Add New Entry
\n'; html = html ~ 'Link All Open
\n'; html = html ~ '

\n'; return html; } function windows_html() { var html = ""; foreach w (windows) { html = html ~ windows.[w].get_html(); } return html; } function bother_user() { write( #file-as-template main.spltpl ); task_pause(); } while (1) { if ( declared cgi.param.newfriend ) { new Win(friends.[new Friend()]); task_pause(); } else if ( declared cgi.param.newwin ) if ( defined new Win(friends.[cgi.param.newwin]) ) task_pause(); else bother_user(); else bother_user(); if ( declared cgi.param.win ) { var w = cgi.param.win; windows.[w].posz = Win.windows_z_counter++; if ( declared cgi.param.newx ) windows.[w].posx = cgi.param.newx; if ( declared cgi.param.newy ) windows.[w].posy = cgi.param.newy; if ( declared cgi.param.close ) windows.[w].finish(); } if ( declared cgi.param.link ) { foreach a (friends) { if ( defined friends.[a].winid ) foreach b (friends) { if ( defined friends.[b].winid && a ~!= b ) friends.[a].links.[b] = 1; } } } } spl-1.0pre6/webspl_demo/cookies.webspl0000644000000000000000000000240210436614152016615 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * cookies.webspl: Simple example for cookies */ load "cgi"; load "encode_xml"; if (declared cgi.param.cookieval) { cgi.cookie.demo = cgi.param.cookieval; write( Set cookie to '${xml::cgi.param.cookieval}'. ); } else { write(
New cookie value:

); } spl-1.0pre6/webspl_demo/wsfaction.webspl0000644000000000000000000000374110262752241017164 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * wsfaction.webspl: An example for the WsfAction module */ load "wsf"; load "wsf_action"; load "wsf_debug"; var counter = 100; var page = new WsfDocument(); page.root = new WsfAction(function() { return #file-as-template *demotpl; }, undef); page.main(); #embedded-file demotpl EOT

The counter has the value $counter.

var button_id = 0; function add_counter_button(offset) { action("count_${button_id}", function() { counter += offset; }); return ''; } ${ add_counter_button("-10") } ${ add_counter_button("-1") } ${ add_counter_button("+1") } ${ add_counter_button("+10") }

if ( not declared children.["debugger"] ) child_set("debugger", new WsfDebug()); return children.["debugger"].get_html_cached();
EOT spl-1.0pre6/webspl_demo/w2tdemo_html.webspl0000644000000000000000000000473410427662761017611 0ustar rootroot load "w2t"; var page = new W2t(); // --------------------------- Calculator --------------------------- object Calculator { static calculator_counter = 0; var myid; var mypage; var value = 0; var op_prefix = undef; method init(_page) { mypage = _page; myid = mypage.getid(); mypage.register_callback("${myid}_action", function(c, op, val) { switch { case not defined op_prefix: value = val; case op_prefix ~== "add": value .+= val; case op_prefix ~== "sub": value .-= val; case op_prefix ~== "mul": value .*= val; case op_prefix ~== "div": value ./= val; case op_prefix ~== "mod": value .%= val; case op_prefix ~== "pow": value .**= val; } op_prefix = op ~== "equal" ? undef : op; page.execute("document.getElementById('${myid}_value').value = $value"); }); mypage.register_callback("${myid}_close", function() { page.dom_remove(myid); }); mypage.dom_appendchild("calcs", <:> :
:

Calculator #${calculator_counter++}

: : : :
); return this; } } push W2t.styles, <:> : .calculator { : background: #aaf; : width: 20%; : height: 20%; : border: groove; : margin: 2%; : padding: 2%; : float: left; : } : .calculator input { : width: 80%; : margin: 10%; : } : .calculator button { : width: 20%; : margin: 1%; : } ; // --------------------------- Startup Page --------------------------- page.register_callback("newcalc", function() { new Calculator(page); }); page.startup = <:> : : : : Simple w2t demo : : : : : Open W2T Console
: Create new calculator :
: : ; page.run(); spl-1.0pre6/webspl_demo/wsfdemo.webspl0000644000000000000000000000575610365155131016642 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * wsfdemo.webspl: A simple WebSPL forms application */ load "wsf"; load "encode_xml"; object Calculator WsfComponent { var currentvalue = 0; var title = "Calculator"; var offmessage = "TURNED OFF"; method get_html() { if ( offmessage ~== "" ) return #file-as-template calc_on.spltpl; else return #file-as-template calc_off.spltpl; } method main() { while (1) { task_co_return(); if ( declared cgi.param.turnon ) offmessage = ""; if ( declared cgi.param.act ) { if ( cgi.param.act ~== 'add' ) currentvalue = currentvalue + cgi.param.num; if ( cgi.param.act ~== 'sub' ) currentvalue = currentvalue - cgi.param.num; if ( cgi.param.act ~== 'mul' ) currentvalue = currentvalue * cgi.param.num; if ( cgi.param.act ~== 'div' ) { if ( cgi.param.num == 0 ) { currentvalue = 0; offmessage = "DIV BY ZERO"; } else currentvalue = currentvalue / cgi.param.num; } } dirty = 1; } } } object Calclist WsfComponent { method get_html() { var html = '
'; html ~= 'Active DOM Update mechanism: '~ '${WsfDocument.domupdate}
\n'; var domupdlist = [ "none" => 0, "xmlhttprequest" => 0, "iframe" => 0 ]; foreach x ( domupdlist ) html ~= '  [ $x ]\n'; html ~= '  [ iframe+debug ]\n'; html ~= '  (not all methods work with all browsers, Mozilla/Firefox recommended)'; html ~= '

\n\n'; for (var i=0; i<32; i++) { if ( i%4 == 0 ) html ~= "\n"; else if ( i%4 == 3 ) html ~= "\n"; else html ~= "\n"; } html ~= '
${ children.[i].get_html_cached() }${ children.[i].get_html_cached() }
${ children.[i].get_html_cached() }

\n'; return html; } method init() { *WsfComponent.init(); for (var i=0; i<32; i++) { children.[i] = new Calculator(); children.[i].title = "Calc #$i"; } return this; } } var page = new WsfDocument(); page.root = new Calclist(); page.main(); spl-1.0pre6/webspl_demo/images/0000755000000000000000000000000011300476114015205 5ustar rootrootspl-1.0pre6/webspl_demo/images/wsf_graph_upX.gif0000644000000000000000000000007110230752564020515 0ustar rootrootGIF89a €ˆˆˆÿÿÿ!ù , Œp›¹Øœ‰Òö²Þ¼¿;spl-1.0pre6/webspl_demo/images/wsf_graph_right.gif0000644000000000000000000000012310230735736021056 0ustar rootrootGIF89a €ÿÿÿ!þCreated with The GIMP!ù , Œ §˜Ë½€„gÊhñÕ.lG;spl-1.0pre6/webspl_demo/images/wsf_graph_downX.gif0000644000000000000000000000007010230752564021037 0ustar rootrootGIF89a €ˆˆˆÿÿÿ!ù , Œ©Ë£ rš ^\;spl-1.0pre6/webspl_demo/images/wsf_graph_lineX.gif0000644000000000000000000000004310231467262021016 0ustar rootrootGIF89a€ˆˆˆÿÿÿ,D;spl-1.0pre6/webspl_demo/images/wsf_graph_leftX.gif0000644000000000000000000000007410230752564021026 0ustar rootrootGIF89a €ˆˆˆÿÿÿ!ù , Œ§˜Ë­€„fÊeiîuö!K;spl-1.0pre6/webspl_demo/images/wsf_graph_up.gif0000644000000000000000000000012210230735736020364 0ustar rootrootGIF89a €ÿÿÿ!þCreated with The GIMP!ù , Œp›¹Øœ‰Òö²Þ¼¿;spl-1.0pre6/webspl_demo/images/wsf_graph_rightX.gif0000644000000000000000000000007210230752564021207 0ustar rootrootGIF89a €ˆˆˆÿÿÿ!ù , Œ §˜Ë½€„gÊhñÕ.lG;spl-1.0pre6/webspl_demo/images/wsf_graph_down.gif0000644000000000000000000000012110230735736020706 0ustar rootrootGIF89a €ÿÿÿ!þCreated with The GIMP!ù , Œ©Ë£ rš ^\;spl-1.0pre6/webspl_demo/images/wsf_graph_line.gif0000644000000000000000000000010410230752564020665 0ustar rootrootGIF89að!ù!þCreated with The GIMP,D;spl-1.0pre6/webspl_demo/images/wsf_graph_trans.gif0000644000000000000000000000010410231707165021063 0ustar rootrootGIF89a€ÿÿÿÿÿÿ!þCreated with The GIMP!ù ,L;spl-1.0pre6/webspl_demo/images/wsf_graph_left.gif0000644000000000000000000000012510230735736020675 0ustar rootrootGIF89a €ÿÿÿ!þCreated with The GIMP!ù , Œ§˜Ë­€„fÊeiîuö!K;spl-1.0pre6/webspl_demo/main.spltpl0000644000000000000000000000451610517630526016142 0ustar rootroot${ "" /* just stop 'file' from believing this is HTML */ } Friends Manager - Example Application for WebSPL
${ menu_html() }

Friends Manager - A WebSPL Example Application

Warning: A browser with JavaScript and DOM support is required for using this application!

 

This is a little demonstration in using WebSPL for writing web applications. This (well-structured) program has less then 300 lines of SPL code and less then 200 lines of HTML templates. This is including the DOM-based windowing system used in this program, which is not a feature of WebSPL but implemented in this application.

This application does not use any database backends. So the data entered here is volatile and will be removed when you end your session.

This application is like a small address-book program. But in addition to the persons contact data it also stores their relationships to other persons. Click on the names in the menu on the left side to browse and edit the data. Click on "Add New Entry" to create additional entries and click on "Link All Open" to tell the system that all currently open windows represent a group of people which know each other.

Have fun!       -- Clifford Wolf

${ windows_html() } spl-1.0pre6/webspl_demo/sessioncookie.webspl0000644000000000000000000000234610352015253020037 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * sessioncookie.webspl: An example for storing the session id in a cookie * * Note: This only works because of the settings in "webspl.conf". */ load "cgi"; load "task"; cgi.cookie["demosession"] = cgi.sid_vm; for (var i=0; 1; i++) { write(<:> : [$i] Hello World!   : [next] : [restart] ); task_pause(); } spl-1.0pre6/webspl_demo/wsf_dialog.webspl0000644000000000000000000000201310235723304017272 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * wsf_dialog.webspl: Little demo for WSF Dialogs */ load "wsf"; load "wsf_dialog"; var page = new WsfDocument(); page.root = new WsfDialog( undef ); page.root.set_edit_mode(1); page.main(); spl-1.0pre6/webspl_demo/calc_off.spltpl0000644000000000000000000000046310350526163016743 0ustar rootroot

${xml::title}:   ${xml::offmessage}

[Turn On]
spl-1.0pre6/webspl_demo/calc_on.spltpl0000644000000000000000000000132710350526163016605 0ustar rootroot

${xml::title}:  

spl-1.0pre6/webspl_demo/wsfgraph.webspl0000644000000000000000000000661710416461505017016 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * wsfgraph.webspl: A simple demo for the WsfGraph class */ load "wsf"; load "wsf_graph"; load "wsf_display"; var description = <>

WsfGraph Example

This is a small example application for the WsfGraph Class, a WSF component for displaying and editing graphs. It is e.g. used in the admin interface of SPL Ticket for editing the ticket states and their relations.

The WsfGraph Class is using some advances JavaScript DOM stuff. It works with most browsers, but it works best with Mozilla Firefox.

You can click on the nodes and the connections and move the nodes.

; var node_list; object Node { var info; var name; var pos_x; var pos_y; var links; var color; method get_xywh() { return [ 'x' => pos_x, 'y' => pos_y, 'w' => 100, 'h' => 50 ]; } method set_xy(x, y) { pos_x = x; pos_y = y; info.set_html(description ~ "

Clicked on:

$name:
x=$pos_x
y=$pos_y

"); } method set_link(trg) { info.set_html(description ~ "

Clicked on:

$name -> ${node_list[trg].name}

"); } method get_color() { return color; } method get_html() { return "$name"; } method get_links() { return links; } method init(i, n, c, y, x, l) { info = i; name = n; color = c; pos_x = x; pos_y = y; links = l; return this; } } object Graphdemo WsfComponent { method get_html() { return <>
${children[0].get_html()} ${children[1].get_html()}
; } method init() { children[1] = new WsfDisplay(); children[1].set_html(description); children[0] = new WsfGraph( function() { return node_list; }, "images/wsf_graph_" ); children[0].iframe_attributes = 'style="width:100%; height:100%"'; children[0].iframe_header = ''; return *WsfComponent.init(); } } var page = new WsfDocument(); page.root = new Graphdemo(); node_list = [ 'n1' => new Node(page.root.children[1], "Node #1", "#aaffff", 200, 400, [ "n2", "n3", "n4", "n6" ] ), 'n2' => new Node(page.root.children[1], "Node #2", "#ffaaff", 300, 200, [ "n3", "n4", "n5", "n6" ] ), 'n3' => new Node(page.root.children[1], "Node #3", "#ffffaa", 100, 300, [ "n1" ] ), 'n4' => new Node(page.root.children[1], "Node #4", "#aaffaa", 200, 100, [ "n3", "n5" ] ), 'n5' => new Node(page.root.children[1], "Node #5", "#ffaaaa", 400, 100, [ "n6" ] ), 'n6' => new Node(page.root.children[1], "Node #6", "#aaaaff", 400, 400, [ "n5" ] ) ]; page.main(); spl-1.0pre6/webspl_demo/entry_show.spltpl0000644000000000000000000000071710350526163017412 0ustar rootroot
Name:${name} Friends:
${friends_list}
Phone:${phone}
E-Mail:${email}
Address:${addr}
[Delete]   [Edit]
spl-1.0pre6/utf8tab.h0000644000000000000000000000663010240436550013175 0ustar rootroot// Automatically generated from genutf8tab.sh // // This is not done by the makefile because genutf8tab.sh has // some dependencies and it is very likely that the character // set mappings described here will never change.. static unsigned char UTF8TAB_ISO8859_1[128][4] = { { 194,128,0 }, // 128 { 194,129,0 }, // 129 { 194,130,0 }, // 130 { 194,131,0 }, // 131 { 194,132,0 }, // 132 { 194,133,0 }, // 133 { 194,134,0 }, // 134 { 194,135,0 }, // 135 { 194,136,0 }, // 136 { 194,137,0 }, // 137 { 194,138,0 }, // 138 { 194,139,0 }, // 139 { 194,140,0 }, // 140 { 194,141,0 }, // 141 { 194,142,0 }, // 142 { 194,143,0 }, // 143 { 194,144,0 }, // 144 { 194,145,0 }, // 145 { 194,146,0 }, // 146 { 194,147,0 }, // 147 { 194,148,0 }, // 148 { 194,149,0 }, // 149 { 194,150,0 }, // 150 { 194,151,0 }, // 151 { 194,152,0 }, // 152 { 194,153,0 }, // 153 { 194,154,0 }, // 154 { 194,155,0 }, // 155 { 194,156,0 }, // 156 { 194,157,0 }, // 157 { 194,158,0 }, // 158 { 194,159,0 }, // 159 { 194,160,0 }, // 160 { 194,161,0 }, // 161 { 194,162,0 }, // 162 { 194,163,0 }, // 163 { 194,164,0 }, // 164 { 194,165,0 }, // 165 { 194,166,0 }, // 166 { 194,167,0 }, // 167 { 194,168,0 }, // 168 { 194,169,0 }, // 169 { 194,170,0 }, // 170 { 194,171,0 }, // 171 { 194,172,0 }, // 172 { 194,173,0 }, // 173 { 194,174,0 }, // 174 { 194,175,0 }, // 175 { 194,176,0 }, // 176 { 194,177,0 }, // 177 { 194,178,0 }, // 178 { 194,179,0 }, // 179 { 194,180,0 }, // 180 { 194,181,0 }, // 181 { 194,182,0 }, // 182 { 194,183,0 }, // 183 { 194,184,0 }, // 184 { 194,185,0 }, // 185 { 194,186,0 }, // 186 { 194,187,0 }, // 187 { 194,188,0 }, // 188 { 194,189,0 }, // 189 { 194,190,0 }, // 190 { 194,191,0 }, // 191 { 195,128,0 }, // 192 { 195,129,0 }, // 193 { 195,130,0 }, // 194 { 195,131,0 }, // 195 { 195,132,0 }, // 196 { 195,133,0 }, // 197 { 195,134,0 }, // 198 { 195,135,0 }, // 199 { 195,136,0 }, // 200 { 195,137,0 }, // 201 { 195,138,0 }, // 202 { 195,139,0 }, // 203 { 195,140,0 }, // 204 { 195,141,0 }, // 205 { 195,142,0 }, // 206 { 195,143,0 }, // 207 { 195,144,0 }, // 208 { 195,145,0 }, // 209 { 195,146,0 }, // 210 { 195,147,0 }, // 211 { 195,148,0 }, // 212 { 195,149,0 }, // 213 { 195,150,0 }, // 214 { 195,151,0 }, // 215 { 195,152,0 }, // 216 { 195,153,0 }, // 217 { 195,154,0 }, // 218 { 195,155,0 }, // 219 { 195,156,0 }, // 220 { 195,157,0 }, // 221 { 195,158,0 }, // 222 { 195,159,0 }, // 223 { 195,160,0 }, // 224 { 195,161,0 }, // 225 { 195,162,0 }, // 226 { 195,163,0 }, // 227 { 195,164,0 }, // 228 { 195,165,0 }, // 229 { 195,166,0 }, // 230 { 195,167,0 }, // 231 { 195,168,0 }, // 232 { 195,169,0 }, // 233 { 195,170,0 }, // 234 { 195,171,0 }, // 235 { 195,172,0 }, // 236 { 195,173,0 }, // 237 { 195,174,0 }, // 238 { 195,175,0 }, // 239 { 195,176,0 }, // 240 { 195,177,0 }, // 241 { 195,178,0 }, // 242 { 195,179,0 }, // 243 { 195,180,0 }, // 244 { 195,181,0 }, // 245 { 195,182,0 }, // 246 { 195,183,0 }, // 247 { 195,184,0 }, // 248 { 195,185,0 }, // 249 { 195,186,0 }, // 250 { 195,187,0 }, // 251 { 195,188,0 }, // 252 { 195,189,0 }, // 253 { 195,190,0 }, // 254 { 195,191,0 } // 255 }; spl-1.0pre6/asm.c0000644000000000000000000002707111064463357012407 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * asm.c: The assembler interface for easily creating bytecode files. */ #include #include #include #include #include #include #include #include "spl.h" struct spl_asm *spl_asm_create(void) { struct spl_asm *as = malloc(sizeof(struct spl_asm)); as->text = malloc(as->text_size_roof = 4096); as->data = malloc(as->data_size_roof = 4096); as->text_size = as->data_size = 0; as->labels = 0; return as; } void spl_asm_destroy(struct spl_asm *as) { free(as->text); free(as->data); free(as); } int spl_asm_newdata(struct spl_asm *as, const char *arg) { int data_size = strlen(arg)+1; int data_pos = 0; while ( data_pos < as->data_size ) { if ( !strcmp((char*)(as->data+data_pos), arg) ) goto found_data_match; // just using data_pos++ to find more matches // data_pos += strlen(as->data+data_pos)+1; data_pos++; } if ( as->data_size + data_size > as->data_size_roof ) { while ( as->data_size + data_size > as->data_size_roof ) as->data_size_roof *= 2; as->data = realloc(as->data, as->data_size_roof); } memcpy(as->data + as->data_size, arg, data_size); as->data_size += data_size; found_data_match: return data_pos; } int spl_asm_add(struct spl_asm *as, unsigned char op, const char *arg) { int text_size = 1; int data_pos = 0; int ret = as->text_size; if ( op >= 0x20 && op < 0x60 ) { if ( op == SPL_OP_PUSHC && !strcmp(arg, "0") ) op = SPL_OP_ZERO; if ( op == SPL_OP_PUSHC && !strcmp(arg, "1") ) op = SPL_OP_ONE; } if ( op < 0x60 ) { text_size = 5; if (op >= 0x20) data_pos = spl_asm_newdata(as, arg); } if ( as->text_size + text_size > as->text_size_roof ) { while ( as->text_size + text_size > as->text_size_roof ) as->text_size_roof *= 2; as->text = realloc(as->text, as->text_size_roof); } *(as->text+as->text_size) = op; as->text_size++; if ( op < 0x60 ) { spl_int_to_bytes(op >= 0x20 ? data_pos : 0, 4, as->text+as->text_size); as->text_size += 4; } return ret; } void spl_asm_setaddr(struct spl_asm *as, int pos, int addr) { spl_int_to_bytes(addr - (pos+5), 4, as->text+pos+1); } void spl_asm_shuffle(struct spl_asm *as, ...) { struct shuffle_ent; struct shuffle_ent { struct shuffle_ent *next; unsigned char *from, *to, *buffer; int size; }; struct shuffle_ent *list = 0; va_list ap; int arg; va_start(ap, as); while ( (arg=va_arg(ap, int)) >= 0 ) { struct shuffle_ent *e = malloc(sizeof(struct shuffle_ent)); e->next = list; list = e; e->size = arg; e->from = as->text+va_arg(ap, int); e->to = as->text+va_arg(ap, int); e->buffer = malloc(e->size); memcpy(e->buffer, e->from, e->size); } while ( list ) { struct shuffle_ent *e = list; struct spl_asm_label *l = as->labels; memcpy(e->to, e->buffer, e->size); while (l) { if ( (l->position >= e->from - as->text) && (l->position < e->from - as->text + e->size) ) l->position += e->to - e->from; for (int i=0; irefc; i++) if ( (l->ref[i] >= e->from - as->text) && (l->ref[i] < e->from - as->text + e->size) ) l->ref[i] += e->to - e->from; l = l->next; } list = e->next; free(e->buffer); free(e); } } static void spl_asm_fixup_text(struct spl_asm *as) { int i; for (i=0; itext_size; i++) { if ( as->text[i] >= 0x20 && as->text[i] < 0x60 ) { int arg_size = 4 - (as->text[i] & 3); int arg = spl_bytes_to_int(arg_size, as->text+i+1); spl_int_to_bytes(arg + (as->text_size - (i+arg_size+1)), arg_size, as->text+i+1); i += arg_size; } else if ( as->text[i] < 0x20 ) { int arg_size = 4 - (as->text[i] & 3); i += arg_size; } } } int spl_asm_write(struct spl_asm *as, int fd) { int i, rc; int ret = 0; spl_asm_fixup_text(as); for (i=0; i<16; i+=rc) { rc = write(fd, SPL_SIGNATURE+i, 16-i); if ( rc <= 0 ) { ret = -1; goto got_error; } } for (i=0; itext_size; i+=rc) { rc = write(fd, as->text+i, as->text_size-i); if ( rc <= 0 ) { ret = -1; goto got_error; } } for (i=0; idata_size; i+=rc) { rc = write(fd, as->data+i, as->data_size-i); if ( rc <= 0 ) { ret = -1; goto got_error; } } #if 0 int pagesize = getpagesize(); int filler = pagesize - (as->text_size + as->data_size) % pagesize; if (filler == pagesize) filler = 0; if ( filler ) { char *cbuf = calloc(1, filler); for (i=0; itext_size = 0; as->data_size = 0; return ret; } struct spl_code *spl_asm_dump(struct spl_asm *as) { unsigned char *code = malloc(as->text_size + as->data_size+16); struct spl_code *c = spl_code_get(0); memcpy(code, SPL_SIGNATURE, 16); spl_asm_fixup_text(as); memcpy(code+16, as->text, as->text_size); memcpy(code+as->text_size+16, as->data, as->data_size); c->size = as->text_size + as->data_size + 16; c->code_type = SPL_CODE_MALLOCED; c->code = code; as->text_size = 0; as->data_size = 0; return c; } const char *spl_asm_op2txt(int op) { if (op < 0x60) op = op & ~3; for (int i=0; spl_ops[i].name; i++) if (spl_ops[i].op == op) return spl_ops[i].name; return "???"; } int spl_asm_txt2op(const char *txt) { for (int i=0; spl_ops[i].name; i++) if ( !strcasecmp(spl_ops[i].name, txt) ) return spl_ops[i].op; return -1; } void spl_asm_print(struct spl_asm *as, int print_data_seg) { printf("# SPL Assembler Dump\n"); for (int i=0; itext_size; i++) { printf(":%-6d %-12s", i+16, spl_asm_op2txt(as->text[i])); if (as->text[i] < 0x60) { int arg, arg_size = 4 - (as->text[i] & 3); arg = spl_bytes_to_int(arg_size, as->text+i+1); if (as->text[i] < 0x20) printf(":%-15d", arg + i+arg_size+1 + 16); else { int oc = 0; putchar('"'); for (int j = 0; (as->data+arg)[j]; j++) switch ((as->data+arg)[j]) { case '\\': printf("\\\\"); oc += 2; break; case '\"': printf("\\\""); oc += 2; break; case '\n': printf("\\n"); oc += 2; break; case '\r': printf("\\r"); oc += 2; break; case '\t': printf("\\t"); oc += 2; break; default: putchar((as->data+arg)[j]); oc++; } putchar('"'); printf("%*s", oc < 14 ? 14-oc : 0, ""); } printf(" # (%d byte arg) %d", arg_size, arg); i += arg_size; } printf("\n"); } if (print_data_seg) { printf("# Data Segment:\n"); for (int i=0; idata_size; i++) { printf("# %5d: \"", i); while(as->data[i] && i < as->data_size) { switch (as->data[i]) { case '\\': printf("\\\\"); break; case '\"': printf("\\\""); break; case '\n': printf("\\n"); break; case '\r': printf("\\r"); break; case '\t': printf("\\t"); break; default: putchar(as->data[i]); } i++; } printf("\"\n"); } } printf("# Total size: %d bytes (%d text, %d data).\n", as->text_size + as->data_size, as->text_size, as->data_size); } static struct spl_asm_label *get_label(struct spl_asm *as, const char *name) { struct spl_asm_label *l = as->labels; while (l) { if (!strcmp(l->name, name)) return l; l = l->next; } l = calloc(1, sizeof(struct spl_asm_label)); l->name = strdup(name); l->position = -1; l->next = as->labels; as->labels = l; return l; } extern int spl_asm_setlabel(struct spl_asm *as, char *label, int pos) { struct spl_asm_label *l = get_label(as, label); if ( l->position >= 0 ) { spl_report(SPL_REPORT_ASSEMBLER, as, "Label '%s' redefined!\n", l->name); return -1; } l->position = pos; return 0; } extern void spl_asm_reflabel(struct spl_asm *as, char *label, int pos) { struct spl_asm_label *l = get_label(as, label); l->ref[l->refc++] = pos; } extern int spl_asm_resolve_labels(struct spl_asm *as) { struct spl_asm_label *o, *l = as->labels; while (l) { for (int i=0; i < l->refc; i++) { if ( l->position == -1) { spl_report(SPL_REPORT_ASSEMBLER, as, "Label '%s' not defined!\n", l->name); return -1; } spl_asm_setaddr(as, l->ref[i], l->position); } l = (o=l)->next; free(o->name); free(o); } as->labels = 0; return 0; } extern int spl_asm_parse_line(struct spl_asm *as, const char *line) { int this_position = as->text_size; line += strspn(line, " \t\n"); while ( *line == ':' ) { int label_len = strcspn(++line, " \t\n"); char label[label_len+1]; memcpy(label, line, label_len); label[label_len] = 0; line += label_len; line += strspn(line, " \t\n"); spl_asm_setlabel(as, label, as->text_size); } if ( !*line || *line == '#' ) return 0; { int op_name_len = strcspn(line, " \t\n"); char op_name[op_name_len+1]; memcpy(op_name, line, op_name_len); op_name[op_name_len] = 0; line += op_name_len; line += strspn(line, " \t\n"); int op = spl_asm_txt2op(op_name); if ( op < 0 ) { spl_report(SPL_REPORT_ASSEMBLER, as, "Opcode %s not defined!\n", op_name); return -1; } if ( op < 0x20 ) { if ( *line == ':' ) { int label_len = strcspn(++line, " \t\n"); char label[label_len+1]; memcpy(label, line, label_len); label[label_len] = 0; line += label_len; line += strspn(line, " \t\n"); spl_asm_reflabel(as, label, as->text_size); spl_asm_add(as, op, 0); } else if ( *line ) { int arg_len = strcspn(line, " \t\n"); char arg[arg_len+1]; memcpy(arg, line, arg_len); arg[arg_len] = 0; line += op_name_len; line += strspn(line, " \t\n"); int addr = strtol(arg, 0, 0); spl_asm_setaddr(as, spl_asm_add(as, op, 0), addr); } else { spl_report(SPL_REPORT_ASSEMBLER, as, "Argument for opcode %s missing!\n", op_name); return -1; } } else if ( op < 0x60 ) { if ( *line == '"' ) { int arg_len = 0; line++; for (int i=0; line[i] && line[i] != '"'; i++) { if (line[i] == '\\' && line[i+1]) i++; arg_len++; } char arg[arg_len+1]; for (int i=0; *line && *line != '"'; i++, line++) { if (*line == '\\' && line[1]) switch (*(++line)) { case 'n': arg[i] = '\n'; break; case 'r': arg[i] = '\r'; break; case 't': arg[i] = '\t'; break; default: arg[i] = *line; } else arg[i] = *line; } arg[arg_len] = 0; if (*line == '"') line++; line += strspn(line, " \t\n"); spl_asm_add(as, op, arg); } else if ( *line ) { int arg_len = strcspn(line, " \t\n"); char arg[arg_len+1]; memcpy(arg, line, arg_len); arg[arg_len] = 0; line += arg_len; line += strspn(line, " \t\n"); spl_asm_add(as, op, arg); } else { spl_report(SPL_REPORT_ASSEMBLER, as, "Argument for opcode %s missing!\n", op_name); return -1; } } else spl_asm_add(as, op, 0); } if ( *line && *line != '#' ) { spl_report(SPL_REPORT_ASSEMBLER, as, "Ignoring cruft (%s) at end of line!\n", line); return -1; } return this_position; } spl-1.0pre6/state.c0000644000000000000000000007475111064463357012756 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * state.c: Interface to the in-memory databases holding the VM state. */ #include #include #include #include #include #ifndef USEWIN32API # include # include #else # include # include # include #endif #ifdef ENABLE_PTHREAD_SUPPORT #include #endif #include "spl.h" #include "compat.h" static int spl_state_counter_malloc = 0; static int spl_state_counter_free = 0; #ifdef ENABLE_PTHREAD_SUPPORT static pthread_mutex_t spl_state_counter_mutex = PTHREAD_MUTEX_INITIALIZER; #endif void spl_state_counter_malloc_inc() { #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_lock(&spl_state_counter_mutex); #endif spl_state_counter_malloc++; #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_unlock(&spl_state_counter_mutex); #endif } int spl_state_counter_malloc_get() { #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_lock(&spl_state_counter_mutex); #endif int retval = spl_state_counter_malloc; #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_unlock(&spl_state_counter_mutex); #endif return retval; } void spl_state_counter_free_inc() { #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_lock(&spl_state_counter_mutex); #endif spl_state_counter_free++; #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_unlock(&spl_state_counter_mutex); #endif } int spl_state_counter_free_get() { #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_lock(&spl_state_counter_mutex); #endif int retval = spl_state_counter_free; #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_unlock(&spl_state_counter_mutex); #endif return retval; } struct spl_builtin_module *spl_builtin_module_list = 0; struct spl_vm *spl_vm_create(void) { struct spl_vm *vm = calloc(1, sizeof(struct spl_vm)); vm->root = spl_get(0); vm->root->ctx_type = SPL_CTX_ROOT; /* Allocate the initial array. We assume that there is almost no SPL script which loads no module. */ vm->clib_hash_size = 2; vm->clib_hash = calloc(vm->clib_hash_size, sizeof(*vm->clib_hash)); vm->clib_hash_count = 0; spl_state_counter_malloc_inc(); return vm; } void spl_vm_destroy(struct spl_vm *vm) { vm->destroy_in_progress = 1; while (vm->task_list) spl_task_destroy(vm, vm->task_list); spl_put(vm, vm->root); vm->root = 0; spl_gc(vm); for(int i = 0; i < vm->clib_hash_size; i++) { struct spl_clib *clib_list = vm->clib_hash[i]; while (clib_list) { struct spl_clib *n = clib_list->next; free(clib_list->name); free(clib_list); clib_list = n; } } while (vm->hnode_list) { struct spl_hnode *n = vm->hnode_list->next; free(vm->hnode_list->name); free(vm->hnode_list); vm->hnode_list = n; } spl_module_unload_all(vm); spl_state_counter_free_inc(); if (vm->clib_hash) free(vm->clib_hash); if (vm->current_dir_name) free(vm->current_dir_name); if (vm->codecache_dir) free(vm->codecache_dir); if (vm->path) free(vm->path); struct spl_node_stack *p = vm->free_node_stack_elements; while (p) { struct spl_node_stack *n = p->next; free(p); p = n; } free(vm); } struct spl_node_stack *spl_vm_malloc_node_stack_element(struct spl_vm *vm) { struct spl_node_stack *n; if (vm->free_node_stack_elements) { n = vm->free_node_stack_elements; vm->free_node_stack_elements = n->next; } else { n = malloc(sizeof(struct spl_node_stack)); } return n; } void spl_vm_free_node_stack_element(struct spl_vm *vm, struct spl_node_stack * const n) { n->next = vm->free_node_stack_elements; vm->free_node_stack_elements = n; } void spl_undumpable_inc(struct spl_vm *vm, const char *info) { if (info && !vm->undumpable_info) { vm->undumpable_info = info; vm->undumpable_info_counter = 1; } vm->undumpable++; } void spl_undumpable_dec(struct spl_vm *vm, const char *info) { if (vm->undumpable_info == info) { if (vm->undumpable_info_counter-- == 1) vm->undumpable_info = 0; } vm->undumpable--; } struct spl_code *spl_code_get(struct spl_code *code) { if (!code) { code = calloc(1, sizeof(struct spl_code)); spl_state_counter_malloc_inc(); } code->ref_counter++; return code; } void spl_code_put(struct spl_code *code) { if (!code) return; code->ref_counter--; if ( !code->ref_counter ) { if ( code->code ) switch ( code->code_type ) { case SPL_CODE_MALLOCED: free(code->code); break; case SPL_CODE_MAPPED: #ifndef USEWIN32API munmap(code->code, code->size); #else free(code->code); #endif break; } if ( code->sha1 ) { free(code->sha1); code->sha1 = 0; } if ( code->code_type != SPL_CODE_STATIC ) { spl_state_counter_free_inc(); if (code->id) free(code->id); free(code); } } } const char *spl_code_sha1(struct spl_code *code) { if (!code || !code->code) return 0; if (!code->sha1) { unsigned char md[20]; spl_sha1(code->code, code->size, md); code->sha1 = malloc(41); for (int i=0; i<20; i++) { code->sha1[i*2+0] = "0123456789ABCDEF"[(md[i] >> 4) & 0x0f]; code->sha1[i*2+1] = "0123456789ABCDEF"[(md[i] >> 0) & 0x0f]; } code->sha1[40] = 0; } return code->sha1; } struct spl_task *spl_task_lookup(struct spl_vm *vm, const char *id) { struct spl_task *task = vm->task_list; if (id) while ( task ) { if ( task->id && !strcmp(task->id, id) ) return task; task = task->next; } return 0; } struct spl_task *spl_task_create(struct spl_vm *vm, const char *id) { struct spl_task *task = spl_task_lookup(vm, id); if (task) return task; task = calloc(1, sizeof(struct spl_task)); spl_state_counter_malloc_inc(); if (id) task->id = strdup(id); task->ctx = spl_get(vm->root); task->next = vm->task_list; if (vm) { vm->task_list = task; task->vm = vm; } return task; } void spl_task_destroy(struct spl_vm *vm, struct spl_task *task) { struct spl_task *t = vm->task_list; struct spl_task *l = 0; while (t) { if ( t == task ) { struct spl_task *n = t->next; spl_put(vm, t->ctx); spl_code_put(t->code); spl_state_counter_free_inc(); if (t->module) free(t->module); while (t->stack) spl_put(vm, spl_pop(t)); if (t->id) free(t->id); free(t); if (l) l->next = n; else vm->task_list = n; t = n; } else t = (l=t)->next; } } void spl_task_setcode(struct spl_task *task, struct spl_code *code) { spl_code_put(task->code); task->code = code; task->code_ip = 0; task->debug_str = 0; } unsigned int spl_subs_hash(const char *key, int max_hash) { unsigned int c, hash = 0; const unsigned char *ukey = (const unsigned char *)key; /* hashing algorithms are fun. this one is from the sdbm package. */ while ( (c = *ukey++) ) hash = c + (hash << 6) + (hash << 16) - hash; return hash % max_hash; } /* this is a debug function for checking hash subs. if something * isn't correct anymore, this should segfault.. */ #if 0 static void spl_subs_knock_off(struct spl_node *node) { struct spl_node_sub *s; int max_count = 1024; int count = 0; for (s = node->subs_begin; s; s = s->next) { if ( s->key[0] ) __asm__ __volatile__ (""); assert( count++ < max_count ); } for (s = node->subs_end; s; s = s->last) { if ( s->key[0] ) __asm__ __volatile__ (""); assert( count++ < max_count ); } for (int i=0; isubs_hash_size; i++) for (s = node->subs_hash[i]; s; s = s->hash_next) { if ( s->key[0] ) __asm__ __volatile__ (""); assert( count++ < max_count ); } } #endif void spl_subs_hash_check(struct spl_node *node) { if ( node->subs_counter < 8 ) return; if ( node->subs_counter < node->subs_hash_size*2 ) return; if ( node->subs_hash ) free(node->subs_hash); node->subs_hash_size = node->subs_counter*2; if ( node->subs_hash_size < 64 ) node->subs_hash_size = 64; node->subs_hash = calloc(node->subs_hash_size, sizeof(struct spl_node_sub*)); struct spl_node_sub *s = node->subs_begin; while (s) { unsigned int hash = spl_subs_hash(s->key, node->subs_hash_size); s->hash_next = node->subs_hash[hash]; node->subs_hash[hash] = s; s = s->next; } } struct spl_node_sub *spl_sub_lookup(struct spl_node *node, const char *key) { const char *real_key = key; while ( *real_key == '?' ) real_key++; spl_subs_hash_check(node); struct spl_node_sub *s = node->subs_hash ? node->subs_hash [spl_subs_hash(real_key, node->subs_hash_size)] : node->subs_begin; while (s) { if (!strcmp(real_key, s->key)) { return s; } s = node->subs_hash ? s->hash_next : s->next; } return 0; } struct spl_node *spl_lookup(struct spl_task *task, struct spl_node *node, const char *key, int flags) { if ( !node || !key ) return 0; if ( *key == 0 ) { while (node && node->ctx && node->ctx_type == SPL_CTX_LOCAL) node = node->ctx; return node->ctx; } if ( !strcmp(key, "!CLS") ) { while (node && node->ctx && node->ctx_type == SPL_CTX_LOCAL) node = node->ctx; return node->cls; } if ( !strcmp(key, "!ROOT") ) { if (!task || !task->vm) return 0; return task->vm->root; } if ( !strcmp(key, "!THIS") ) { struct spl_node *n = task->ctx; while ( n ) { if ( n->cls ) { n = n->cls; break; } if ( n->ctx_type == SPL_CTX_OBJECT ) break; n = n->ctx; } if ( !n || n->ctx_type != SPL_CTX_OBJECT ) { spl_report(SPL_REPORT_RUNTIME, task, "Lookup of THIS outside of object context!\n"); return 0; } return n; } if ( node->hnode_name ) { if ( !task ) return 0; struct spl_node *result = spl_hnode_lookup(task, node, key, flags); if (result) spl_cleanup(task, result); if ( !result || (result->flags & SPL_NODE_FLAG_CLNULL) ) { if ( !strchr(key, '?') ) spl_report(SPL_REPORT_RUNTIME, task, "Use (lookup) of undeclared variable '%s'!\n", key); return 0; } return result; } /* A dot in the middle is an object reference, lookup head and tail */ char *obj_tail = strchr(key, '.'); if ( obj_tail ) { char obj_head[obj_tail - key + 1]; memcpy(obj_head, key, obj_tail - key); obj_head[obj_tail - key] = 0; obj_tail++; struct spl_node *o = spl_lookup(task, node, obj_head, flags); if (o) return spl_lookup(task, o, obj_tail, flags | SPL_LOOKUP_NOCTX); while ( obj_tail ) { if ( *obj_tail != '?' ) { spl_report(SPL_REPORT_RUNTIME, task, "Use (lookup) of variable '%s' in undeclared context!\n", obj_tail); break; } obj_tail = strchr(obj_tail, '.'); } return 0; } spl_subs_hash_check(node); struct spl_node_sub *s = spl_sub_lookup(node, key); if (s) { if (s->node && (s->node->flags & SPL_NODE_FLAG_STATIC) && !(flags & SPL_LOOKUP_NOSTATIC)) return s->node->ctx; return s->node; } if ( node->cls && node->ctx_type != SPL_CTX_OBJECT ) { struct spl_node *result = spl_lookup(task, node->cls, key, (flags | SPL_LOOKUP_NOCTX | SPL_LOOKUP_TEST)); if (result) return result; } else if ( node->cls ) { assert(node != node->cls); struct spl_node *result = spl_lookup(task, node->cls, key, (flags | SPL_LOOKUP_CLS | SPL_LOOKUP_NOSTATIC)); if (result) { if ((flags & SPL_LOOKUP_CLS) == 0) { if (result->flags & SPL_NODE_FLAG_METHOD) { struct spl_node *tmp = result; result = spl_copy(task->vm, result, node); if (result->ctx) spl_put(task->vm, result->ctx); if (result->cls) spl_put(task->vm, result->cls); if (tmp->cls) result->ctx = tmp->ctx ? spl_get(tmp->ctx) : 0; else result->ctx = tmp->ctx && tmp->ctx->ctx ? spl_get(tmp->ctx->ctx) : 0; result->cls = spl_get(node); spl_cleanup(task, result); } else if ((result->flags & SPL_NODE_FLAG_STATIC) == 0) { result = spl_copy(task->vm, result, node); spl_create(task, node, key, result, flags | SPL_CREATE_LOCAL); } } if (!(flags & SPL_LOOKUP_NOSTATIC) && (result->flags & SPL_NODE_FLAG_STATIC)) result = result->ctx; return result; } } if ( flags & SPL_LOOKUP_CLS ) return 0; if ( (flags & SPL_LOOKUP_NOCTX) == 0 && node->ctx ) { assert(node != node->ctx); return spl_lookup(task, node->ctx, key, flags); } if ( *key != '?' && !(flags & SPL_LOOKUP_TEST) ) { if (node->flags & SPL_NODE_FLAG_RERES) spl_report(SPL_REPORT_RUNTIME, task, "Use (lookup) of not existing regex capture $<%s>!\n", key); else spl_report(SPL_REPORT_RUNTIME, task, "Use (lookup) of undeclared variable '%s'!\n", key); } return 0; } struct spl_node *spl_create(struct spl_task *task, struct spl_node *node, const char *key, struct spl_node *newnode, int flags) { if ( !node ) return 0; if ( !key ) { key = my_alloca(32); snprintf((char*)key, 32, "%d", node->subs_next_idx); } if ( node->hnode_name ) { if ( !task ) return 0; struct spl_node *result = spl_hnode_create(task, node, key, newnode, flags); if (result) spl_cleanup(task, result); if ( !result || (result->flags & SPL_NODE_FLAG_CLNULL) ) { if ( !strchr(key, '?') ) spl_report(SPL_REPORT_RUNTIME, task, "Use (assignment) of undeclared variable '%s'!\n", key); return 0; } return result; } /* Writing context or class pointer - this is stuff for real men. ;-) */ if ( !key[0] || !strcmp(key, "!CLS") ) { while (node && node->ctx && node->ctx_type == SPL_CTX_LOCAL) node = node->ctx; if (node) { if (!key[0]) { if (node->ctx) spl_put(task->vm, node->ctx); node->ctx = newnode; } else { if (node->cls) spl_put(task->vm, node->cls); node->cls = newnode; } return newnode; } spl_put(task->vm, newnode); return 0; } /* Writing root pointer - this is insane, so we don't do it. */ if ( !strcmp(key, "!ROOT") ) { spl_report(SPL_REPORT_RUNTIME, task, "Writing to the root " "pointer is a bad idea and not supported!\n"); spl_put(task->vm, newnode); return 0; } /* Writing THIS pointer - this is insane, so we don't do it. */ if ( !strcmp(key, "!THIS") ) { spl_report(SPL_REPORT_RUNTIME, task, "Writing to the THIS " "pointer is a bad idea and not supported!\n"); spl_put(task->vm, newnode); return 0; } /* A dot in the middle is an object reference, lookup head and create tail */ /* Also create head if it is new */ char *obj_tail = strchr(key, '.'); if ( obj_tail ) { char obj_head[obj_tail - key + 1]; memcpy(obj_head, key, obj_tail - key); obj_head[obj_tail - key] = 0; obj_tail++; struct spl_node *o = spl_lookup(task, node, obj_head, flags); if (!o) o = spl_create(task, node, obj_head, spl_get(0), flags); return spl_create(task, o, obj_tail, newnode, flags); } struct spl_node_sub *s = spl_sub_lookup(node, key); if (s) { struct spl_node *target = s->node; if ( (target->flags & SPL_NODE_FLAG_STATIC) && !(flags & SPL_CREATE_NOSTATIC) ) { if (target->ctx) spl_cleanup(task, target->ctx); target->ctx = newnode; } else { spl_cleanup(task, s->node); s->node = newnode; } return target; } /* create local if it is forced by the flags */ if ( flags & SPL_CREATE_LOCAL ) goto create_local; /* create in first object if it is defined in the class path */ if ( node->cls ) { struct spl_node *cn = node; while (cn->ctx_type != SPL_CTX_OBJECT && cn->cls) cn = cn->cls; struct spl_node *tmp = spl_lookup(task, cn, key, flags | SPL_LOOKUP_CLS|SPL_LOOKUP_NOSTATIC); if (tmp) { if (tmp->flags & SPL_NODE_FLAG_STATIC) { if (tmp->ctx) spl_cleanup(task, tmp->ctx); return tmp->ctx = newnode; } return spl_create(task, cn, key, newnode, flags | SPL_CREATE_LOCAL); } } /* create local if no next CTX is available */ if ( !node->ctx ) goto create_local; /* create local if this is a function and SPL_CREATE_FUNCLOCAL is set */ if ( node->ctx_type == SPL_CTX_FUNCTION && (flags & SPL_CREATE_FUNCLOCAL) ) goto create_local; /* create local if this is a function and is not defined in next context */ if ( (node->ctx_type == SPL_CTX_FUNCTION || node->ctx_type == SPL_CTX_OBJECT) && !spl_lookup(task, node->ctx, key, flags) ) goto create_local; /* in all other cases: go to next context */ assert(node->ctx != node); return spl_create(task, node->ctx, key, newnode, flags); create_local: if ( ((flags & (SPL_CREATE_LOCAL|SPL_CREATE_FUNCLOCAL)) == 0) && *key != '?' ) spl_report(SPL_REPORT_RUNTIME, task, "Use (assignment) of undeclared variable '%s'!\n", key); const char *real_key = key; while (*real_key == '?') real_key++; s = calloc(1, sizeof(struct spl_node_sub)); if (node->ctx_type == SPL_CTX_ROOT && task && task->module) s->module = strdup(task->module); s->key = strdup(real_key); if ( real_key[strspn(real_key, "0123456789")] == 0 ) { int this_idx = atoi(real_key); if ( this_idx < 1024*1024*1024 && this_idx >= node->subs_next_idx ) node->subs_next_idx = this_idx+1; } if ( !node->subs_end ) { node->subs_begin = s; node->subs_end = s; } else { if ( (flags & SPL_CREATE_BEGIN) == 0 ) { node->subs_end->next = s; s->last = node->subs_end; node->subs_end = s; } else { node->subs_begin->last = s; s->next = node->subs_begin; node->subs_begin = s; } } if ( node->subs_hash_size ) { unsigned int hash = spl_subs_hash(s->key, node->subs_hash_size); s->hash_next = node->subs_hash[hash]; node->subs_hash[hash] = s; } node->subs_counter++; s->node = newnode; return newnode; } void spl_delete(struct spl_task *task, struct spl_node *node, const char *key) { if ( !node || !key ) return; if ( node->hnode_name ) { if ( task ) spl_hnode_delete(task, node, key); return; } /* A dot in the middle is an object reference, lookup head and delete tail */ char *obj_tail = strchr(key, '.'); if ( obj_tail ) { char obj_head[obj_tail - key + 1]; memcpy(obj_head, key, obj_tail - key); obj_head[obj_tail - key] = 0; obj_tail++; struct spl_node *o = spl_lookup(task, node, obj_head, 0); if (o) spl_delete(task, o, obj_tail); return; } const char *real_key = key; while ( *real_key == '?' ) real_key++; spl_subs_hash_check(node); int hash = node->subs_hash ? spl_subs_hash(real_key, node->subs_hash_size) : 0; struct spl_node_sub *s = node->subs_hash ? node->subs_hash[hash] : node->subs_begin; struct spl_node_sub **l = node->subs_hash ? &node->subs_hash[hash] : 0; while (s) { if (!strcmp(real_key, s->key)) { if ( s->last ) s->last->next = s->next; else node->subs_begin = s->next; if ( s->next ) s->next->last = s->last; else node->subs_end = s->last; if ( l ) *l = s->hash_next; struct spl_node_sub *n = node->subs_hash ? s->hash_next : s->next; spl_put(task ? task->vm : 0, s->node); if (s->module) free(s->module); free(s->key); free(s); node->subs_counter--; s = n; } else { if ( l ) l = &s->hash_next; s = node->subs_hash ? s->hash_next : s->next; } } } int spl_get_int(struct spl_node *node) { if ( !node ) return 0; if ( node->value & SPL_VALUE_INT ) return node->value_int; if ( (node->value & SPL_VALUE_FLOAT) && (node->flags & SPL_NODE_FLAG_IS_FLOAT) ) { node->value_int = node->value_float; node->value |= SPL_VALUE_INT; return node->value_int; } if ( node->value & SPL_VALUE_STRING ) { const char *str = spl_string(node->value_string); int negative = 0; node->value_int = 0; if (str && *str == '-') { negative = 1; str++; } if (str && *str == '+') str++; while (str && *str >= '0' && *str <= '9') { node->value_int = node->value_int*10 + (*str - '0'); str++; } if (negative) node->value_int *= -1; node->value |= SPL_VALUE_INT; return node->value_int; } return 0; } double spl_get_float(struct spl_node *node) { if ( !node ) return 0; if ( node->value & SPL_VALUE_FLOAT ) return node->value_float; if ( (node->value & SPL_VALUE_INT) && (node->flags & SPL_NODE_FLAG_IS_INT) ) { node->value_float = node->value_int; node->value |= SPL_VALUE_FLOAT; return node->value_float; } if ( node->value & SPL_VALUE_STRING ) { const char *str = spl_string(node->value_string); int negative = 0; node->value_float = 0; if (str && *str == '-') { negative = 1; str++; } if (str && *str == '+') str++; while (str && *str >= '0' && *str <= '9') { node->value_float = node->value_float*10 + (*str - '0'); str++; } if (str && *str == '.') { str++; double dez_pos = 10; while (str && *str >= '0' && *str <= '9') { node->value_float += (*str - '0') / dez_pos; dez_pos *= 10; str++; } } if (negative) node->value_float *= -1; node->value |= SPL_VALUE_FLOAT; return node->value_float; } return 0; } char *spl_get_string(struct spl_node *node) { if ( !node ) return ""; if ( node->value & SPL_VALUE_STRING ) return spl_string(node->value_string); if ( (node->value & SPL_VALUE_FLOAT) && (node->flags & SPL_NODE_FLAG_IS_FLOAT) ) { node->value_string = spl_string_printf(0, 0, 0, "%lg", node->value_float); node->value |= SPL_VALUE_STRING; return spl_string(node->value_string); } if ( (node->value & SPL_VALUE_INT) && (node->flags & SPL_NODE_FLAG_IS_INT) ) { node->value_string = spl_string_printf(0, 0, 0, "%d", node->value_int); node->value |= SPL_VALUE_STRING; return spl_string(node->value_string); } return ""; } struct spl_string *spl_get_spl_string(struct spl_node *node) { if ( !node ) return 0; if ( node->value & SPL_VALUE_STRING ) return node->value_string; if ( node->value & SPL_VALUE_FLOAT ) { node->value_string = spl_string_printf(0, 0, 0, "%lg", node->value_float); node->value |= SPL_VALUE_STRING; return node->value_string; } if ( node->value & SPL_VALUE_INT ) { node->value_string = spl_string_printf(0, 0, 0, "%d", node->value_int); node->value |= SPL_VALUE_STRING; return node->value_string; } return 0; } char *spl_get_value(struct spl_node *node) { if ( !node ) return 0; if ( !node->value ) return 0; return spl_get_string(node); } int spl_get_type(struct spl_node *node) { if ( !node ) return SPL_TYPE_NONE; if ( !node->value ) return SPL_TYPE_NONE; if ( node->flags & SPL_NODE_FLAG_IS_FLOAT ) return SPL_TYPE_FLOAT; if ( node->flags & SPL_NODE_FLAG_IS_INT ) return SPL_TYPE_INT; if ( node->ctx_type == SPL_CTX_OBJECT ) return SPL_TYPE_OBJ; if ( node->value & SPL_VALUE_STRING ) { char *string = spl_string(node->value_string); for (int i=0; string[i]; i++) { if (string[i] == '.') { node->flags |= SPL_NODE_FLAG_IS_FLOAT; return SPL_TYPE_FLOAT; } if (string[i] < '0' || string[i] > '9') return SPL_TYPE_NONE; } if (string[0]) { node->flags |= SPL_NODE_FLAG_IS_INT; return SPL_TYPE_INT; } } return SPL_TYPE_NONE; } struct spl_node *spl_set_int(struct spl_node *node, int value) { if ( node ) { spl_string_put(node->value_string); node->value_string = 0; node->value_float = 0; node->value_int = value; node->value = SPL_VALUE_INT; node->flags |= SPL_NODE_FLAG_IS_INT; } return node; } struct spl_node *spl_set_float(struct spl_node *node, double value) { if ( node ) { spl_string_put(node->value_string); node->value_string = 0; node->value_int = 0; node->value_float = value; node->value = SPL_VALUE_FLOAT; node->flags |= SPL_NODE_FLAG_IS_FLOAT; } return node; } struct spl_node *spl_set_string(struct spl_node *node, char *value) { if ( node ) { spl_string_put(node->value_string); node->value_float = 0; node->value_int = 0; node->value_string = spl_string_new(0, 0, 0, value, 0); node->value = value ? SPL_VALUE_STRING : 0; } return node; } struct spl_node *spl_set_spl_string(struct spl_node *node, struct spl_string *value) { if ( node ) { spl_string_put(node->value_string); node->value_float = 0; node->value_int = 0; node->value_string = value; node->value = value ? SPL_VALUE_STRING : 0; } return node; } struct spl_node *spl_tos(struct spl_task *task) { return task->stack ? task->stack->node : 0; } struct spl_node *spl_push(struct spl_task *task, struct spl_node *node) { struct spl_node_stack *n = spl_vm_malloc_node_stack_element(task->vm); n->node = node; n->next = task->stack; task->stack = n; return node; } struct spl_node *spl_pop(struct spl_task *task) { if (!task->stack) { spl_report(SPL_REPORT_RUNTIME, task, "Tried to pop from empty VM Stack!\n"); return 0; } struct spl_node_stack *s = task->stack; struct spl_node *n = s->node; task->stack = s->next; spl_vm_free_node_stack_element(task->vm, s); return n; } struct spl_node *spl_get(struct spl_node *node) { if (!node) { node = calloc(1, sizeof(struct spl_node)); spl_state_counter_malloc_inc(); } node->ref_counter++; return node; } void spl_put(struct spl_vm *vm, struct spl_node *node) { if (!node) return; node->ref_counter--; if ( node->ref_counter < 0 ) { spl_report(SPL_REPORT_RUNTIME, 0, "Got a negative reference counter!\n"); node->ref_counter = 0; } if (!vm) { if ( !node->ref_counter ) spl_report(SPL_REPORT_RUNTIME, 0, "Got final (refcount=0) spl_put() call without vm pointer!\n"); return; } if ( !node->ref_counter && node->hnode_name ) { spl_hnode_put(vm, node); } if ( !node->ref_counter ) { struct spl_node_sub *s = node->subs_begin; while (s) { struct spl_node_sub *n = s->next; spl_put(vm, s->node); if (s->module) free(s->module); free(s->key); free(s); s=n; } if (node->ctx) spl_put(vm, node->ctx); if (node->cls) spl_put(vm, node->cls); if (node->code) spl_code_put(node->code); if (node->value_string) spl_string_put(node->value_string); if ( (node->flags & SPL_NODE_FLAG_GC) != 0 ) { if ( node->gc_left ) node->gc_left->gc_right = node->gc_right; else vm->gc_list = node->gc_right; if ( node->gc_right ) node->gc_right->gc_left = node->gc_left; } spl_state_counter_free_inc(); if (node->subs_hash) free(node->subs_hash); if (node->hnode_name) free(node->hnode_name); if (node->hnode_dump) free(node->hnode_dump); if (node->path) free(node->path); free(node); } else { if ( (node->subs_counter > 0 || node->ctx || node->cls) && (node->flags & SPL_NODE_FLAG_GC) == 0 ) { node->flags |= SPL_NODE_FLAG_GC; if (!vm->gc_list) node->gc_right = 0; else (node->gc_right = vm->gc_list)->gc_left = node; (vm->gc_list = node)->gc_left = 0; } } } struct spl_node *spl_cleanup(struct spl_task *task, struct spl_node *node) { struct spl_node_stack *n = spl_vm_malloc_node_stack_element(task->vm); n->node = node; n->next = task->cleanup; task->cleanup = n; return node; } static struct spl_node *spl_copy_worker(struct spl_vm *vm, struct spl_node *node, struct spl_node *cls, int recurs) { if (node->flags & SPL_NODE_FLAG_STATIC) return spl_get(node); if (node && node->hnode_name) { struct spl_node *copy_data = spl_hnode_copy(vm, node); if (copy_data) return copy_data; } struct spl_node *newnode = spl_get(0); if (!node) return newnode; if ( recurs > 1024 ) { spl_report(SPL_REPORT_RUNTIME, 0, "Trying to copy cyclic object tree!\n"); return newnode; } newnode->flags = node->flags & ~SPL_NODE_FLAG_GC; newnode->ctx_type = node->ctx_type; if ((node->flags & SPL_NODE_FLAG_METHOD) && cls) { if (node->cls) { if (node->ctx) newnode->ctx = spl_get(node->ctx); } else { if (node->ctx && node->ctx->ctx) newnode->ctx = spl_get(node->ctx->ctx); } newnode->cls = spl_get(cls); } else { if (node->ctx) newnode->ctx = spl_get(node->ctx); if (node->cls) newnode->cls = spl_get(node->cls); } if (newnode->code) spl_code_put(newnode->code); newnode->code = node->code ? spl_code_get(node->code) : 0; newnode->code_ip = node->code_ip; newnode->value = node->value; newnode->value_int = node->value_int; newnode->value_float = node->value_float; spl_string_put(newnode->value_string); newnode->value_string = spl_string_get(node->value_string); struct spl_node_sub *s = newnode->subs_begin; while (s) { struct spl_node_sub *n = s->next; spl_put(vm, s->node); if (s->module) free(s->module); free(s->key); free(s); s=n; } if ( newnode->subs_hash ) free(newnode->subs_hash); newnode->subs_next_idx = node->subs_next_idx; newnode->subs_hash = 0; newnode->subs_hash_size = 0; newnode->subs_counter = 0; newnode->subs_begin = 0; newnode->subs_end = 0; s = node->subs_begin; while (s) { struct spl_node_sub *n = calloc(1, sizeof(struct spl_node_sub)); n->key = strdup(s->key); n->node = spl_copy_worker(vm, s->node, newnode, recurs+1); if ( !newnode->subs_end ) { newnode->subs_begin = n; newnode->subs_end = n; } else { newnode->subs_end->next = n; n->last = newnode->subs_end; newnode->subs_end = n; } newnode->subs_counter++; s = s->next; } return newnode; } struct spl_node *spl_copy(struct spl_vm *vm, struct spl_node *node, struct spl_node *cls) { return spl_copy_worker(vm, node, cls, 0); } char *spl_hash_encode(const char *source) { int source_i, target_i; if (*source == 0) return strdup("?="); for (source_i = 0, target_i = 1; source[source_i]; source_i++, target_i++) switch (source[source_i]) { case '0' ... '9': case 'A' ... 'Z': case 'a' ... 'z': case '_': break; default: target_i+=2; } char *target = malloc(target_i+1); target[0] = '?'; for (source_i = 0, target_i = 1; source[source_i]; source_i++, target_i++) switch (source[source_i]) { case '0' ... '9': case 'A' ... 'Z': case 'a' ... 'z': case '_': target[target_i] = source[source_i]; break; default: target[target_i++] = '='; target[target_i++] = (source[source_i] & 0x0f) + 'A'; target[target_i] = ((source[source_i] & 0xf0) >> 4) + 'A'; } target[target_i] = 0; return target; } char *spl_hash_decode(const char *source) { int source_i, target_i; if (*source == '?') source++; if (!strcmp(source, "=")) return strdup(""); for (source_i = target_i = 0; source[source_i]; source_i++, target_i++) if (source[source_i] == '=' && source[source_i+1] && source[source_i+2]) source_i+=2; else if (source[source_i] == '?') target_i--; char *target = malloc(target_i+1); for (source_i = target_i = 0; source[source_i]; source_i++, target_i++) if (source[source_i] == '=' && source[source_i+1] && source[source_i+2]) { target[target_i] = source[++source_i] - 'A'; target[target_i] |= (source[++source_i] - 'A') << 4; } else { if (source[source_i] != '?') target[target_i] = source[source_i]; else target_i--; } target[target_i] = 0; return target; } spl-1.0pre6/examples/0000755000000000000000000000000011300476115013257 5ustar rootrootspl-1.0pre6/examples/example20.expected0000644000000000000000000000177410236117044016610 0ustar rootrootXML: `- E0:friends: `- E0:person: `- A:id: "23" `- A:sex: "m" `- E0:name: `- C0: "fake" `- E0:desc: `- C0: "His desc is so complicated - I <> a CDATA here." `- E1:person: `- A:id: "42" `- A:sex: "f" `- E0:name: `- C0: "kyrah" `- E0:desc: `- C0: "Her description has an HTML
tag." `- E0:br: `- C1: "Just for fun..." --- fake His desc is so complicated - I <<need>> a CDATA here. kyrah Her description has an HTML <br/> tag.

Just for fun...
SPL Debug: Error handling test: SPL Debug: XML Parse error at line 1: mismatched tag spl-1.0pre6/examples/c-api-test3.spla0000644000000000000000000000206510265443562016205 0ustar rootroot# # SPL - The SPL Programming Language # Copyright (C) 2004, 2005 Clifford Wolf # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # c-api-test3.spla: Simple assembler program for testing c-api-test3. # pushc "i" pushc "23" popl :loop pushc "[" push "i" pushc "] hallo world!" cat cat debug push "i" pushc "1" iadd pop "i" push "i" pushc "42" ile if jump :loop halt spl-1.0pre6/examples/example10.spl0000644000000000000000000000265110235723304015600 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example10.spl: Tests for regular expressions, "x ? y : z" operator * and embedded functions */ // SKIP_IF_NO_REGEX var x, y, z, out; x = y = z = "foolish bigfoot"; y =~ s/foo/bar/; z =~ s/foo/bar/g; write(out = "A $x and a $y are eating a $z.\n"); write(".. ${ out =~ /eat/ ? "found" : "didn't find" } string 'eat'.\n"); write(".. ${ out =~ /beat/ ? "found" : "didn't find" } string 'beat'.\n"); var a = ({ if ( out =~ /are/ ) return "found"; return "didn't find"; }); write(".. $a string 'are'.\n"); write(".. $( if ( out =~ /rare/ ) return "found"; return "didn't find"; ) string 'rare'.\n"); spl-1.0pre6/examples/example12.spl0000644000000000000000000000324210242356714015604 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example12.spl: Example for the forach statement */ // SKIP_IF_NO_REGEX var sysctl; var sysctl.abi; var sysctl.abi.defhandler_elf = "0"; var sysctl.abi.defhandler_coff = "117440515"; var sysctl.dev; var sysctl.dev.raid; var sysctl.dev.raid.speed_limit_max = "100000"; var sysctl.dev.raid.speed_limit_min = "100"; var sysctl.dev.cdrom; var sysctl.dev.cdrom.check_media = "0"; var sysctl.dev.cdrom.lock = "1"; var sysctl.dev.cdrom.autoeject = "0"; var sysctl.dev.cdrom.autoclose = "1"; var sysctl.dev.cdrom.info = "CD-ROM information, Id: cdrom.c 3.12 2000/10/18"; function parse_tree(name, tree) { if ( name !~ /\..*\./ ) var tree.foobar = "changed in parse_tree()"; foreach i (tree) { debug "$name.$i = ${defined tree.[i] ? tree.[i] : "NULL"}"; if ( i ~!= "foobar" ) parse_tree("$name.$i", tree.[i]); } } parse_tree("sysctl", sysctl); spl-1.0pre6/examples/example14.spl0000644000000000000000000000234710350526163015610 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example14.spl: Example for templates with special tags */ var articles; var articles[1]; var articles[1].header = "Demo Article 1"; var articles[1].body = "Blablabla"; var articles[2]; var articles[2].header = "Demo Article 2"; var articles[2].body = "Huhaha"; var articles[3]; var articles[3].header = "Demo Article 3"; var articles[3].body = "Another test"; write( #file-as-template example14.spltpl ); spl-1.0pre6/examples/example12.expected0000644000000000000000000000133010242356714016603 0ustar rootrootSPL Debug: sysctl.abi = NULL SPL Debug: sysctl.abi.defhandler_elf = 0 SPL Debug: sysctl.abi.defhandler_coff = 117440515 SPL Debug: sysctl.abi.foobar = changed in parse_tree() SPL Debug: sysctl.dev = NULL SPL Debug: sysctl.dev.raid = NULL SPL Debug: sysctl.dev.raid.speed_limit_max = 100000 SPL Debug: sysctl.dev.raid.speed_limit_min = 100 SPL Debug: sysctl.dev.cdrom = NULL SPL Debug: sysctl.dev.cdrom.check_media = 0 SPL Debug: sysctl.dev.cdrom.lock = 1 SPL Debug: sysctl.dev.cdrom.autoeject = 0 SPL Debug: sysctl.dev.cdrom.autoclose = 1 SPL Debug: sysctl.dev.cdrom.info = CD-ROM information, Id: cdrom.c 3.12 2000/10/18 SPL Debug: sysctl.dev.foobar = changed in parse_tree() SPL Debug: sysctl.foobar = changed in parse_tree() spl-1.0pre6/examples/example51.spl0000644000000000000000000000211610264726077015615 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example51.spl: example for switch statements */ var list = [ 1, 2, 3, 4 ]; while (1) switch { var x = shift list; case x == 1: debug "x is 1"; case x == 2: debug "x is 2"; case x == 3: debug "x is 3"; default: debug "whatever!"; exit; } spl-1.0pre6/examples/example04.expected0000644000000000000000000000106610224224006016576 0ustar rootrootSPL Debug: 10.20.30 SPL Debug: In function: a=10, b=25, c=30, x=, y= SPL Debug: In root ctx: a=10, b=20, c=30, x=, y= SPL Debug: In function: a=11, b=25, c=31, x=1, y= SPL Debug: In root ctx: a=10, b=20, c=31, x=, y= SPL Debug: In function: a=12, b=25, c=33, x=2, y=9 SPL Debug: In root ctx: a=10, b=20, c=33, x=, y= SPL Debug: In function: a=13, b=25, c=36, x=3, y=9 SPL Debug: In root ctx: a=10, b=20, c=36, x=, y= SPL Debug: In function: a=14, b=25, c=40, x=4, y=9 SPL Debug: In root ctx: a=10, b=20, c=40, x=, y= SPL Debug: In function: a=10, b=25, c=40, x=, y= spl-1.0pre6/examples/example16.spl0000644000000000000000000000214210235723304015601 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example16.spl: Example for operator precedence */ write("Test for '?' ':' operator: "); for (var i=0; i<4; i++) write( i < 2 ? i == 0 ? "A" : "B" : i == 2 ? "C" : "D" ); write("\n"); write("Test for basic expressions: "); write("${5+3 == 2*4} ${12-11 == !0}"); write("\n"); spl-1.0pre6/examples/example53.spl0000644000000000000000000000532310267754647015631 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example53.spl: simple XSLT example */ // SKIP_IF_NO_LIBXML2 // SKIP_IF_NO_LIBXSLT load "xml"; var xmldoc = xml_parse(<:> : : : : Clifford Wolf : Distribution Built Kit : : : Felix von Leitner : A small C library : : : Felix von Leitner : A high performance web server : : : Raffi and Clifford : A game for the text console : : : Clifford Wolf : Yet another Scripting Language : : ); var xsldoc = xml_parse(<:> : : : : : : : : : : : : : : : : : : : : : : : : : ); debug xml_xslt_text(xmldoc, xsldoc, ignore: "'Raffi'"); spl-1.0pre6/examples/example18.spl0000644000000000000000000000232410235723304015605 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example18.spl: Simple example for init() */ object A { var name = "A"; method init() { debug "Running init() in $name (A)."; return this; } method foobar() { debug "Running foobar() in $name (A)."; } } object B A { var mysub; var name = "B"; method init() { debug "Running init() in $name (B)."; mysub = new A(); return *A.init(); } } var x = new B(); x.foobar(); spl-1.0pre6/examples/example60.expected0000644000000000000000000000163010347257656016624 0ustar rootrootSPL Debug: [0:0] Hello World! SPL Debug: debug '[0:$i] Hello World!'; => 0 SPL Debug: [1:1] Hello World! SPL Debug: debug '[1:$i] Hello World!'; => 0 SPL Debug: [2:2] Hello World! SPL Debug: debug '[2:$i] Hello World!'; => 0 SPL Debug: [3:3] Hello World! SPL Debug: debug '[3:$i] Hello World!'; => 0 SPL Debug: [4:4] Hello World! SPL Debug: debug '[4:$i] Hello World!'; => 0 SPL Debug: [5:5] Hello World! SPL Debug: debug '[5:$i] Hello World!'; => 0 SPL Debug: [6:6] Hello World! SPL Debug: debug '[6:$i] Hello World!'; => 0 SPL Debug: [7:7] Hello World! SPL Debug: debug '[7:$i] Hello World!'; => 0 SPL Debug: [8:8] Hello World! SPL Debug: debug '[8:$i] Hello World!'; => 0 SPL Debug: [9:9] Hello World! SPL Debug: debug '[9:$i] Hello World!'; => 0 SPL Compiler Error: near line 1, char 8 in eval: syntax error >> debug "Eval with missing ';' at the end. ^ SPL Debug: Eval returned -1. spl-1.0pre6/examples/example55.spl0000644000000000000000000000206710273650114015612 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example55.spl: simple example for parsing a file line by line using a regex */ // SKIP_IF_NO_REGEX load "file"; var file = file_read("example55.spl"); var lines = file =~ /.*?(?=\n)/Ag; foreach i (lines) debug "$i: ${lines[i]}"; spl-1.0pre6/examples/example52.expected0000644000000000000000000000213010271153362016602 0ustar rootrootSPL Debug: ATTRIBUTE 11: _NodeBySplId_(1) SPL Debug: ATTRIBUTE 12: _NodeBySplId_(2) SPL Debug: ATTRIBUTE 13: _NodeBySplId_(3) SPL Debug: ATTRIBUTE 14: _NodeBySplId_(4) SPL Debug: ATTRIBUTE 21: _NodeBySplId_(5) SPL Debug: ATTRIBUTE 22: _NodeBySplId_(6) SPL Debug: ATTRIBUTE 23: _NodeBySplId_(7) SPL Debug: ATTRIBUTE 24: _NodeBySplId_(8) SPL Debug: TEXT2 < VERY 41>: //text2 SPL Debug: TEXT2 : //foo:text2 SPL Debug: TEXT2 < TEST! 43>: //bar:text2 SPL Debug: Content of all attribute nodes: 11 12 13 14 21 22 23 24 SPL Debug: Content of first foo:block element: < THIS 31> < IS 32> < A 33> SPL Debug: Content of all text elements: < THIS 31> < IS 32> < A 33> < VERY 41> < TEST! 43> SPL Debug: XML for 1st element: < THIS 31> < IS 32> < A 33> SPL Debug: XML for all elements: , , , , , spl-1.0pre6/examples/example44.expected0000644000000000000000000000017310253273565016620 0ustar rootrootSPL Debug: Test #1 (should be false): OK SPL Debug: Test #2 (should be true): OK SPL Debug: Test #3 (should be false): OK spl-1.0pre6/examples/example57.spl0000644000000000000000000000251010321772757015621 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example57.spl: example for the optimizer (have a look at the asm code) * * * $ ./splrun -AN examples/example57.spl * # SPL Assembler Dump * :16 PUSHC "The number 12 is smaller than 16." # (1 byte arg) 0 * :18 DEBUG * :19 HALT * # Total size: 38 bytes (4 text, 34 data). * */ if (3+3+3 == 3*3 && 2+2 == 2 ** 2) { debug "The number ${3+4+5} is ${3+4+5 < 8*2 ? "smaller" : "larger"} than ${8*2}."; } else { function foobar() { panic "This is never reached!"; } foobar(); } spl-1.0pre6/examples/example36.expected0000644000000000000000000000013510321726477016620 0ustar rootrootSPL Debug: 60 -> a SPL Debug: 61 -> b SPL Debug: 62 -> c SPL Debug: 60...62 SPL Debug: 11, 3 spl-1.0pre6/examples/example28.expected0000644000000000000000000000007010210662213016577 0ustar rootrootSPL Debug: 0: x=1, y=2, z=3 SPL Debug: 1: x=1, y=4, z=2 spl-1.0pre6/examples/example59.spl0000644000000000000000000000345110335141631015612 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example59.spl: example for 'break' and 'continue' in all kinds of loops */ var data = [ "This is", "a test!", "SKIP", "SKIP", "Yo!", "END", "This is", "never reached" ]; for (var l=0; l<3; l++) { for (var i=0; i ${data[i]}"; } foreach i (data) { if (data[i] ~== "SKIP") continue; if (data[i] ~== "END") break; debug "[$l] foreach: $i -> ${data[i]}"; } foreach[] d (data) { if (d ~== "SKIP") continue; if (d ~== "END") break; debug "[$l] foreach[]: $d"; } var data_copy; data_copy := data; while (elementsof data_copy) { var d = shift data_copy; if (d ~== "SKIP") continue; if (d ~== "END") break; debug "[$l] while: $d"; } data_copy := data; do { var d = shift data_copy; if (d ~== "SKIP") continue; if (d ~== "END") break; debug "[$l] do-while: $d"; } while (elementsof data_copy); } spl-1.0pre6/examples/c-api-test3.c0000644000000000000000000000374611064463357015501 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * c-api-test3.c: More complex test for the assembler interface. Implements * a real assembler which reads text input from a file and creates a * bytecode file from it. */ #include #include #include #include #include #include #include "spl.h" void assemble() { FILE *f = fopen("c-api-test3.spla", "r"); struct spl_asm *as = spl_asm_create(); char line[1024]; int lineno=1; while ( fgets(line, 1024, f) ) { if ( spl_asm_parse_line(as, line) < 0 ) { printf("... in line %d.\n", lineno); exit(1); } lineno++; } if ( spl_asm_resolve_labels(as) < 0 ) exit(1); fclose(f); int fd = open("c-api-test3.splb", O_WRONLY|O_CREAT|O_TRUNC, 0666); spl_asm_write(as, fd); close(fd); spl_asm_destroy(as); } void execute() { struct spl_vm *vm = spl_vm_create(); struct spl_task *task = spl_task_create(vm, 0); struct spl_code *code = spl_code_get(0); code->code_type = SPL_CODE_MAPPED; code->code = spl_mmap_file("c-api-test3.splb", 0); spl_task_setcode(task, code); while ( task->code && !spl_exec(task) ) {} spl_vm_destroy(vm); } int main() { assemble(); execute(); return 0; } spl-1.0pre6/examples/example21.spl0000644000000000000000000000226310235723304015601 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example21.spl: Example for here documents with indenting delimiters */ function here_docs_with_indenting() { write( <>EOT: : 0123 0123 0123 ${10+10} : 0123 0123 ${10+20} : 0123 ${10+30} : ${10+40} EOT ); } here_docs_with_indenting(); spl-1.0pre6/examples/example23.spl0000644000000000000000000000277110235723304015607 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example23.spl: Example for "file" module */ load "file"; var err, text1, text2; text1 = file_read("example23.spl"); err = file_write("example23.output", text1); try (ex) { err = file_write("foobar/example23.output", text1); catch FileEx: debug "As expected: " ~ ex.description; } try (ex) { err = file_read("foobar/example23.output", text1); catch FileEx: debug "As expected: " ~ ex.description; } text2 = file_read("example23.output"); if (text1 ~!= text2) write("Text is different after writing and reading!\n"); err = file_delete("example23.output"); try (ex) { err = file_delete("foobar/example23.output"); catch FileEx: debug "As expected: " ~ ex.description; } spl-1.0pre6/examples/example21.expected0000644000000000000000000000017010200211530016560 0ustar rootroot0123 0123 0123 20 0123 0123 30 0123 40 50 0123 0123 0123 ${10+10} 0123 0123 ${10+20} 0123 ${10+30} ${10+40} spl-1.0pre6/examples/example60.spl0000644000000000000000000000211210347257656015615 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example60.spl: example for the 'eval' statement */ for (var i=0; i<10; i++) { var code = "debug '[$i:\$i] Hello World!';"; debug "$code => ${eval code}"; } var ret = eval <>debug "Eval with missing ';' at the end.."; debug "Eval returned $ret."; spl-1.0pre6/examples/example13.expected0000644000000000000000000000015710241074011016574 0ustar rootrootSPL Debug: Before call: [ 10 20 ] 30 SPL Debug: In modify(): [ 11 21 ] 31 SPL Debug: After call: [ 11 21 ] 30 spl-1.0pre6/examples/example25.spl0000644000000000000000000000176610260204745015615 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example25.spl: Example for quoting/encoding functions */ // SKIP_IF_NO_EXPAT load "encode_xml"; var foobar = "This is a test."; debug "*** ${xml::foobar} ***"; spl-1.0pre6/examples/example05.expected0000644000000000000000000000033210177130134016577 0ustar rootrootSPL Debug: The current apple counter value is: 23 SPL Debug: You have 23 Apples (static=23). SPL Debug: The current banana counter value is: 42 SPL Debug: You have 42 Bananas (static=65). SPL Debug: Static counter: 65 spl-1.0pre6/examples/example62.spl0000644000000000000000000000273610405272362015615 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2006 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example62.spl: simple example for the "time" module */ load "time"; #define timestamp 1136361132 if (time() > timestamp) debug "time() returned a reasonable large value."; var tm1 = time_local(timestamp); var tm1_back = time_mk(tm1); if (timestamp == tm1_back) debug "time_local() and time_mk() seam to work fine."; var tm2 = time_gm(timestamp); debug "timestamp = $timestamp"; // some systems don't have this function // debug "time_mkgm(tm2) = ${time_mkgm(tm2)}"; // some systems don't have .zone and .gmtoff delete tm2.zone; delete tm2.gmtoff; foreach field (tm2) debug "tm2.$field = ${tm2[field]}"; debug time_fmt("%a, %d %b %Y %H:%M:%S", tm2); spl-1.0pre6/examples/example27.spl0000644000000000000000000000177410235723304015615 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example27.spl: Simple test for hash efficiency */ var x, y = 0; for (var i = 0; i<10240; i++) x.[i] = (y += 7); for (var i = 0; i<10240; i++) y += x.[i] % 111; debug y; spl-1.0pre6/examples/example61.expected0000644000000000000000000000041410347257656016624 0ustar rootrootSPL Debug: 255 255 255 SPL Debug: 255 255 255 SPL Debug: 255 255 255 SPL Debug: :::30|00031|32:::|:0033|34 SPL Debug: :::1e|0001F|40:::|:0021|100010 SPL Debug: ::-30|-0031|-32::|:-033|-34 SPL Debug: 2.702700|2.70|:2.70|2.70:|02.70 SPL Debug: Hello:::::::|:::::::World spl-1.0pre6/examples/example64.spl0000644000000000000000000000244510421441634015612 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2006 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example64.spl: simple example for $[, $], $@ and $# as well as //S */ // SKIP_IF_NO_REGEX var data = [ hello: "world", "this" => "is", a: "test" ]; foreach i (data) debug "A: ${$[} $] <$i> $# ${$@[$#]}"; foreach[] i (data) debug "B: ${$[} $] <$i> $# ${$@[$#]}"; var output; foreach[] i (data) { if (not $[) output ~= " "; output ~= "$# $i"; } debug "C: <$output>"; foreach[] line ("This is a split test : Blablabla : Holadiro!" =~ /\s*:\s*/Sg) debug "D: [$#] $line"; spl-1.0pre6/examples/example53.expected0000644000000000000000000000131710267754647016633 0ustar rootrootSPL Debug: ROCK Linux Distribution Built Kit SPL Yet another Scripting Language dietlibc A small C library gatling A high performance web server Clifford Wolf Felix von Leitner Raffi and Clifford spl-1.0pre6/examples/example29.spl0000644000000000000000000000207010235723304015605 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example29.spl: Regulax expressions $N test */ // SKIP_IF_NO_REGEX var text = "This is a simple test"; text =~ /\s(\S+).*\s(.*)/; debug "This [${$2}] [${$1}] simple"; debug "This [$2] [$1] simple"; $0 =~ s/\sis/That was/; debug $0; spl-1.0pre6/examples/example45.expected0000644000000000000000000000007310253517431016611 0ustar rootrootSPL Debug: Result #1: 10 / 5 SPL Debug: Result #2: 42 / 42 spl-1.0pre6/examples/example66.spl0000644000000000000000000000401310430724177015613 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2006 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example66.spl: example for , and tags */ // SKIP_IF_NO_REGEX function splif_greater(%args) { return args.a > args.b; } function splcall_substitue(textfunc, %args) { return textfunc() =~ s/$args.from/$args.to/Rg; } function splcall_myloop(textfunc, %args) { var mytextfunc, myctx, text; mytextfunc := textfunc; myctx.[*] = mytextfunc.[*]; mytextfunc.[*] = myctx; for (var i = args.from; i<=args.to; i++) { myctx["counter"] = i; text ~= mytextfunc(); } return text; } function splcall_add(%args) { return args.a + args.b; } write(<:> : Barfoo : Foobar : 42 is greater then 4*2. : 42 is not greater than 4*2. : 4+2 is greater then 42. : 4+2 is not greater than 42. : This is a test! : The answer is . : $i: Current counter: $counter ); spl-1.0pre6/examples/example37.expected0000644000000000000000000000073410232653117016615 0ustar rootrootSPL Debug: Original: 0->10 1->5 2->33 3->100 4->2 5->70 6->80 7->52 SPL Debug: Int Up: 4->2 1->5 0->10 2->33 7->52 5->70 6->80 3->100 SPL Debug: Text Up: 0->10 3->100 4->2 2->33 1->5 7->52 5->70 6->80 SPL Debug: Keys Down: 7->52 6->80 5->70 4->2 3->100 2->33 1->5 0->10 SPL Debug: Int Down: 3->100 6->80 5->70 7->52 2->33 0->10 1->5 4->2 SPL Debug: Text Down: 6->80 5->70 7->52 1->5 2->33 4->2 3->100 0->10 SPL Debug: Reindexed: 0->80 1->70 2->52 3->5 4->33 5->2 6->100 7->10 spl-1.0pre6/examples/example29.expected0000644000000000000000000000015010210640623016600 0ustar rootrootSPL Debug: This [test] [is] simple SPL Debug: This [test] [is] simple SPL Debug: That was a simple test spl-1.0pre6/examples/example30.spl0000644000000000000000000000220010241074011015557 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example30.spl: Simple example for ':=' vs. '=' operator */ object Value { var val; } var a = new Value(); var b = new Value(); var r = a; a.val = " 23"; b.val = " 42"; debug "$a.val, $b.val, $r.val"; r.val = 123; debug "$a.val, $b.val, $r.val"; r := b; r.val = 142; debug "$a.val, $b.val, $r.val"; spl-1.0pre6/examples/example32.spl0000644000000000000000000000175210235723304015605 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example32.spl: Simple example for anonymous functions */ function demo(val, func) { func(val); } demo("Hello World!", function(v) { debug "[[ $v ]]"; }); spl-1.0pre6/examples/example30.expected0000644000000000000000000000011310241074011016563 0ustar rootrootSPL Debug: 23, 42, 23 SPL Debug: 123, 42, 123 SPL Debug: 123, 42, 142 spl-1.0pre6/examples/example34.spl0000644000000000000000000000413110235723304015601 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example34.spl: Simple example for exceptions */ object Ex { var description = "This is an example exception object.\n" ~ "Nice to have descriptions on exceptions.. ;-)"; method tellme() { return "I am Ex. (${this})"; } method init() { return this; } } object Ex1 Ex { method tellme() { return "I am Ex1. (${this})"; } } object Ex2 Ex { method tellme() { return "I am Ex2. (${this})"; } } function throw_exception_sub(ex) { debug "Now throwing the exception $ex."; throw ex; debug "*** NEVER REACHED ***"; } function throw_exception(ex) { if (1) { var dummy = 1; debug "Going to throw exception $ex."; throw_exception_sub(ex); debug "*** NEVER REACHED ***"; } } try(x) { if (1) { var foobar = 1; throw_exception(new Ex1()); } catch Ex1: debug "Got exception Ex1: ${x.tellme()}\n${x.backtrace}"; catch Ex2: debug "Got exception Ex2: ${x.tellme()}\n${x.backtrace}"; } try(x) { if (1) { var foobar = 1; throw_exception(new Ex2()); } catch Ex: debug "Got exception Ex: ${x.tellme()}"; } try(x) { try (y) { if (1) { var foobar = 1; throw_exception(new Ex2()); } catch Ex1: debug "Got exception Ex1: ${y.tellme()}"; } catch Ex2: debug "Got exception Ex2: ${x.tellme()}"; } throw_exception(new Ex()); debug "*** NEVER REACHED ***"; spl-1.0pre6/examples/example22.expected0000644000000000000000000000011010200662162016566 0ustar rootrootdemo1: [0][1][2][3][4][5][6][7] demo2: Now i is 23. demo2: Now i is 42. spl-1.0pre6/examples/example14.expected0000644000000000000000000000027010175722455016613 0ustar rootroot

Demo for iterators in templates

Demo Article 1

Blablabla


Demo Article 2

Huhaha


Demo Article 3

Another test


spl-1.0pre6/examples/example36.spl0000644000000000000000000000210310321726477015612 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example36.spl: Example for next, prev, lengthof and elementsof instructions */ var x = [ 60 => 'a', 'b', 'c' ]; foreach i (x) debug "$i -> ${x[i]}"; debug "${prev x, 61}...${next x, 61}"; debug "${lengthof "Hello World"}, ${elementsof x}"; spl-1.0pre6/examples/example06.expected0000644000000000000000000000024310150426042016576 0ustar rootrootSPL Debug: Calling demo(x, y) ... SPL Debug: demo(10, 20) => 10 is smaller than 20 SPL Debug: demo(15, 15) => 15 equals 15 SPL Debug: demo(20, 10) => 20 = 10 + 10 spl-1.0pre6/examples/example38.spl0000644000000000000000000000566710241110000015600 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example38.spl: Some more advanced object tests */ function test1() { function getobj() { var strange_var = 1; object Foobar { method strange() { return "${ strange_var++ } ${ this } ${ strange }"; } } return Foobar; } var class1 = getobj(); var obj1 = new class1(); object Class2 class1 { } var obj2 = new Class2(); debug obj1.strange(); debug obj2.strange(); } function test2() { object Foo { method foo() { debug "Foo: ${this} -> foo()"; } method bar() { return "Bar: ${this} -> bar()"; } method init() { debug "Init: ${this}"; return this; } } object Bar Foo { var count = 12; method bar() { count = count + 111; debug "${ *Foo.bar() } --> ${ count }"; } } var f = new Foo(); var b = new Bar(); b.foo(); b.bar(); f.foo(); debug f.bar(); var x = new Bar(); x.bar(); var x = new Bar(); x.bar(); } function test3() { object Bla { var title = "a demo."; } object Foo { var y = 23; var xxx; method init() { y = 42; xxx[0] = new Bla(); xxx[0].title = "This is"; return this; } } var x = new Foo(); debug x.xxx[0].title; debug Bla.title; } function test4() { var foo; var bar; function regbar(blup) { bar = blup; } object Demo { var bla = 0; method getbla() { return bla; } method init(x) { bla = x; foo = getbla; regbar(getbla); return this; } } var foobar = new Demo(42); debug foobar.getbla(); debug foo(); debug bar(); } function test5() { object Value { var val; } var a = new Value(); var b = new Value(); function get_base_obj() { var x = a; object Foo { method set_a(val) { x.val = val; } } return Foo; } function get_derived_obj(base) { var x = b; object Foo base { method set_b(val) { x.val = val; } } return Foo; } var o = new (get_derived_obj(get_base_obj()))(); o.set_a(23); o.set_b(42); debug "a=$a.val"; debug "b=$b.val"; } debug "---- Test #1 ----"; test1(); debug "---- Test #2 ----"; test2(); debug "---- Test #3 ----"; test3(); debug "---- Test #4 ----"; test4(); debug "---- Test #5 ----"; test5(); spl-1.0pre6/examples/example62.expected0000644000000000000000000000061710405272362016614 0ustar rootrootSPL Debug: time() returned a reasonable large value. SPL Debug: time_local() and time_mk() seam to work fine. SPL Debug: timestamp = 1136361132 SPL Debug: tm2.sec = 12 SPL Debug: tm2.min = 52 SPL Debug: tm2.hour = 7 SPL Debug: tm2.mday = 4 SPL Debug: tm2.mon = 0 SPL Debug: tm2.year = 106 SPL Debug: tm2.wday = 3 SPL Debug: tm2.yday = 3 SPL Debug: tm2.isdst = 0 SPL Debug: Wed, 04 Jan 2006 07:52:12 spl-1.0pre6/examples/example54.expected0000644000000000000000000000072310270135050016603 0ustar rootrootSPL Debug: this record is in the past! ... this record is in the future! foo clifford's birthyear is 1980 bar ... ... BEFTOPMIDBOTAFT spl-1.0pre6/examples/example46.expected0000644000000000000000000000173510254570043016617 0ustar rootrootSPL Debug: Simple sin/cos example program: **************************************** * **************************************** * **************************************** * *************************************** * *************************************** ** *************************************** ** ************************************** *** ************************************* *** ************************************ **** *********************************** ***** ********************************** ****** ********************************* ******* ******************************* ******** ****************************** ********** **************************** ************ ************************* ************** *********************** **************** ******************** ******************* *************** ********************** ********* ************************** ******************************** ****************************************** spl-1.0pre6/examples/example38.expected0000644000000000000000000000141010367655262016622 0ustar rootrootSPL Debug: ---- Test #1 ---- SPL Debug: 1 [ Foobar ] [SPL Codepointer: byte 54 in 'example38.spl'] SPL Debug: 2 [ Foobar | Class2 ] [SPL Codepointer: byte 54 in 'example38.spl'] SPL Debug: ---- Test #2 ---- SPL Debug: Init: [ Foo ] SPL Debug: Init: [ Foo | Bar ] SPL Debug: Foo: [ Foo | Bar ] -> foo() SPL Debug: Bar: [ Foo | Bar ] -> bar() --> 123 SPL Debug: Foo: [ Foo ] -> foo() SPL Debug: Bar: [ Foo ] -> bar() SPL Debug: Init: [ Foo | Bar ] SPL Debug: Bar: [ Foo | Bar ] -> bar() --> 123 SPL Debug: Init: [ Foo | Bar ] SPL Debug: Bar: [ Foo | Bar ] -> bar() --> 123 SPL Debug: ---- Test #3 ---- SPL Debug: This is SPL Debug: a demo. SPL Debug: ---- Test #4 ---- SPL Debug: 42 SPL Debug: 42 SPL Debug: 42 SPL Debug: ---- Test #5 ---- SPL Debug: a=23 SPL Debug: b=42 spl-1.0pre6/examples/c-api-test4.c0000644000000000000000000000267211064463357015477 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * c-api-test4.c: Simple test for the compiler interface. */ #include #include "spl.h" int main() { struct spl_vm *vm = spl_vm_create(); struct spl_asm *as = spl_asm_create(); spl_compiler(as, "var x = 23;\n" "var y = 42;\n" "var z = y - x;\n" "debug \"x=$x y=$y z=$z\";\n", "", 0, 0 ); spl_asm_add(as, SPL_OP_HALT, 0); spl_asm_print(as, 0); fflush(stdout); struct spl_code *code = spl_asm_dump(as); spl_asm_destroy(as); struct spl_task *task = spl_task_create(vm, 0); spl_task_setcode(task, code); while ( task->code && !spl_exec(task) ) {} spl_vm_destroy(vm); return 0; } spl-1.0pre6/examples/c-api-test1.expected0000644000000000000000000000002710265443562017041 0ustar rootrootSPL Debug: hello world spl-1.0pre6/examples/example02.spl0000644000000000000000000000214410235723304015576 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example02.spl: Simple test for IF statements */ var x; x = -1; if ( x ) debug "[A] x is true ($x)"; else debug "[A] x is false ($x)"; x = x+1; if ( x ) debug "[B] x is true ($x)"; else debug "[B] x is false ($x)"; x = x+1; if ( x ) debug "[C] x is true ($x)"; debug "DONE"; spl-1.0pre6/examples/example04.spl0000644000000000000000000000311010235723304015572 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example04.spl: Simple test for scoping and functions, and also for inline * assembler and the obscure '>>>' operator (string until end of line). */ var a = 10; var b = 20; var c = 30; var x, y; function demo(x, y) { var a = a + x, b = 25; c = c + x; debug "In function: a=$a, b=$b, c=$c, x=$x, y=$y"; } asm >>> push a >>> push b >>> push c >>> dotcat >>> dotcat >>> debug ; demo(); debug "In root ctx: a=$a, b=$b, c=$c, x=$x, y=$y"; demo(1); debug "In root ctx: a=$a, b=$b, c=$c, x=$x, y=$y"; demo(2, 9); debug "In root ctx: a=$a, b=$b, c=$c, x=$x, y=$y"; demo(3, 9, 3); debug "In root ctx: a=$a, b=$b, c=$c, x=$x, y=$y"; demo(4, 9, 3, 4); debug "In root ctx: a=$a, b=$b, c=$c, x=$x, y=$y"; var foobar; foobar.[42] = demo; foobar.[6*6+6](); spl-1.0pre6/examples/example41.spl0000644000000000000000000000200010242356714015575 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example41.spl: just another SPL hacker */ for(var d,i=<>just,j=function(){d~=i~(defined(i=next[*],i)?" ":" ");},just,another,SPL,hacker;defined i||({debug d;return 0;});j()); spl-1.0pre6/examples/example06.spl0000644000000000000000000000232010235723304015576 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example06.spl: Simple test for return values and early returns */ function demo(x, y) { if ( x < y ) return "$x is smaller than $y"; var diff = x - y; if ( diff == 0 ) return "$x equals $y"; return "$x = $y + $diff"; } debug "Calling demo(x, y) ..."; debug "demo(10, 20) => " ~ demo(10, 20); debug "demo(15, 15) => " ~ demo(15, 15); debug "demo(20, 10) => " ~ demo(20, 10); spl-1.0pre6/examples/example43.spl0000644000000000000000000000177110242647240015612 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example42.spl: various array declaration syntax types */ function dumpit(a) { foreach i (a) debug "${i}->${a[i]}"; } var a1 = [ "a" => "x", b: "y", "z" ]; dumpit(a1); spl-1.0pre6/examples/example31.expected0000644000000000000000000000016310223507636016607 0ustar rootrootSPL Debug: 1/5 reached SPL Debug: 2/5 reached SPL Debug: 3/5 reached SPL Debug: 4/5 reached SPL Debug: 5/5 reached spl-1.0pre6/examples/example08.spl0000644000000000000000000000164310241103460015577 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example08.spl: Example for encoding pragma */ #encoding latin_1 debug "Hällö Wörld"; spl-1.0pre6/examples/example23.expected0000644000000000000000000000043210232535003016574 0ustar rootrootSPL Debug: As expected: Can't write file 'foobar/example23.output': No such file or directory SPL Debug: As expected: Can't read file 'foobar/example23.output': No such file or directory SPL Debug: As expected: Can't delete file 'foobar/example23.output': No such file or directory spl-1.0pre6/examples/example45.spl0000644000000000000000000000244710253517431015615 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example45.spl: simple example for object operators */ object Vector2D { var x, y; method operator_add(a, b) { return new Vector2D(a.x + b.x, a.y + b.y); } method init(_x, _y) { x = _x; y = _y; return this; } } var v1 = new Vector2D(5, 7); var v2 = new Vector2D(2, 2); var v3 = new Vector2D(3, -4); var result = v1 (+) v2 (+) v3; debug "Result #1: ${result.x} / ${result.y}"; result (+)= new Vector2D(32, 37); debug "Result #2: ${result.x} / ${result.y}"; spl-1.0pre6/examples/example15.expected0000644000000000000000000000007210150745075016610 0ustar rootrootsubstituted $not_substituted substituted $not_substituted spl-1.0pre6/examples/example07.expected0000644000000000000000000000132410150426042016600 0ustar rootrootSPL Debug: prime.['abc'] is not defined. SPL Debug: prime.['123'] is defined. SPL Debug: prime.[1] => 0 SPL Debug: prime.[2] => 1 SPL Debug: prime.[3] => 1 SPL Debug: prime.[4] => 0 SPL Debug: prime.[5] => 1 SPL Debug: prime.[6] => 0 SPL Debug: prime.[7] => 1 SPL Debug: prime.[8] => 0 SPL Debug: prime.[9] => 0 SPL Debug: prime.[10] => 0 SPL Debug: prime.[11] => 1 SPL Debug: prime.[12] => 0 SPL Debug: prime.[13] => 1 SPL Debug: prime.[14] => 0 SPL Debug: prime.[15] => 0 SPL Debug: prime.[16] => 0 SPL Debug: prime.[17] => 1 SPL Debug: prime.[18] => 0 SPL Debug: prime.[19] => 1 You tried 'prime.[42] = 1'. Please do not define your own prime numbers! You called method 'delete' in prime namespace. Primes are read-only! spl-1.0pre6/examples/example47.spl0000644000000000000000000000207510255033211015603 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example47.spl: simple unicode / utf example */ // SKIP_IF_NO_REGEX var txt = chr(165) ~ chr(8364) ~ chr(36) ~ " - UTF-8 seams to be working.."; debug txt; txt =~ /.(?P.)/I; debug "BTW: The unicode char for $euro is ${ord(euro)}."; spl-1.0pre6/examples/example63.expected0000644000000000000000000000002710415222561016605 0ustar rootrootSPL Debug: hello world spl-1.0pre6/examples/example55.expected0000644000000000000000000000277610273650334016630 0ustar rootrootSPL Debug: 0: /* SPL Debug: 1: * SPL - The SPL Programming Language SPL Debug: 2: * Copyright (C) 2004, 2005 Clifford Wolf SPL Debug: 3: * SPL Debug: 4: * This program is free software; you can redistribute it and/or modify SPL Debug: 5: * it under the terms of the GNU General Public License as published by SPL Debug: 6: * the Free Software Foundation; either version 2 of the License, or SPL Debug: 7: * (at your option) any later version. SPL Debug: 8: * SPL Debug: 9: * This program is distributed in the hope that it will be useful, SPL Debug: 10: * but WITHOUT ANY WARRANTY; without even the implied warranty of SPL Debug: 11: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the SPL Debug: 12: * GNU General Public License for more details. SPL Debug: 13: * SPL Debug: 14: * You should have received a copy of the GNU General Public License SPL Debug: 15: * along with this program; if not, write to the Free Software SPL Debug: 16: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA SPL Debug: 17: * SPL Debug: 18: * example55.spl: simple example for parsing a file line by line using a regex SPL Debug: 19: */ SPL Debug: 20: SPL Debug: 21: // SKIP_IF_NO_REGEX SPL Debug: 22: SPL Debug: 23: load "file"; SPL Debug: 24: SPL Debug: 25: var file = file_read("example55.spl"); SPL Debug: 26: var lines = file =~ /.*?(?=\n)/Ag; SPL Debug: 27: SPL Debug: 28: foreach i (lines) SPL Debug: 29: debug "$i: ${lines[i]}"; SPL Debug: 30: spl-1.0pre6/examples/example49.spl0000644000000000000000000000213410262752241015612 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example49.spl: example for '{' ... '}' vs. '{[' ... ']}' */ var msg = ""; function x() { msg ~= "World"; } /* append "Hello " */ { function x() { msg ~= "Hello "; } x(); } /* append "World" */ x(); /* append "!" */ {[ function x() { msg ~= "!"; } ]} x(); debug msg; spl-1.0pre6/examples/example47.expected0000644000000000000000000000014210254570043016607 0ustar rootrootSPL Debug: ¥€$ - UTF-8 seams to be working.. SPL Debug: BTW: The unicode char for € is 8364. spl-1.0pre6/examples/example39.expected0000644000000000000000000000060110447766423016625 0ustar rootrootSPL Debug: Match #0: [f] barlish (foolish) SPL Debug: Match #1: [b] bigbart (foot) SPL Debug: Ever seen a barlish bigfoot? SPL Debug: Ever beeing eaten by a barlish bigfoot? SPL Debug: foobar == foobar == foobar SPL Debug: foobar == foobar == foobar SPL Debug: aybyxay SPL Debug: Hello World SPL Debug: Hello World SPL Debug: Foo is now defined here too. SPL Debug: fax SPL Debug: foo spl-1.0pre6/examples/c-api-test2.expected0000644000000000000000000000003010265443562017034 0ustar rootrootSPL Debug: hello world! spl-1.0pre6/examples/example11.spl0000644000000000000000000000213010350526163015573 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example11.spl: Example for compiler meta-directives */ // SKIP_IF_NO_REGEX #file-as-code example11.code var data = #file-as-const example11.spl; data =~ s/.*?( .*?SPL.*?>).*/$1/s; write(data ~ "\n"); var language="SPL"; write(#file-as-template example11.spltpl); spl-1.0pre6/examples/example13.spl0000644000000000000000000000230710241657676015620 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example13.spl: Example for call-by-value for scalar and call-by-ref for complex variables */ var dummy = [ foo: 10, bar: 20 ]; var foobar = 30; function modify(x, y) { x.foo++; x.bar++; y++; debug "In modify(): [ $x.foo $x.bar ] $y"; } debug "Before call: [ $dummy.foo $dummy.bar ] $foobar"; modify(dummy, foobar); debug "After call: [ $dummy.foo $dummy.bar ] $foobar"; spl-1.0pre6/examples/example20.xml0000644000000000000000000000051310200322223015561 0ustar rootroot fake > a CDATA here.]]> kyrah Her description has an HTML <br/> tag.
Just for fun...
spl-1.0pre6/examples/example50.spl0000644000000000000000000000236610263435475015621 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example50.spl: example for PHP-like templates and foreach[] */ debug ; if (declared list) panic "The 'list' var should be local to the template!"; spl-1.0pre6/examples/example15.spl0000644000000000000000000000200610235723304015577 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example15.spl: Example for various styles of here documents */ var a="substituted"; write( <<< $a ~ >>> $not_substituted ); write(<>EOT $not_substituted EOT); spl-1.0pre6/examples/example52.spl0000644000000000000000000000432410270135050015577 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example52.spl: example for the real XML module */ // SKIP_IF_NO_LIBXML2 load "xml"; var xml_file = <> < THIS 31> < IS 32> < A 33> < VERY 41> <SIMPLE 42> < TEST! 43> ; var doc = xml_parse(xml_file); foreach[] i (doc["//@*"].nodes) debug "ATTRIBUTE ${doc[i]}: $i"; foreach[] i (doc["//*[local-name()='text1']"]) debug "TEXT1 ${doc[i].data}: $i"; foreach[] i (["//text2", "//foo:text2", "//bar:text2"]) debug "TEXT2 ${doc[i]}: $i"; debug "Content of all attribute nodes:\n${ doc["//@*"].data.[" "] }"; debug "Content of first foo:block element:\n${ doc["//foo:block"] }"; debug "Content of all text elements:\n${ doc["//*[local-name()='text1' or local-name()='text2']"].data.["\n"] }"; debug "XML for 1st element:\n${ doc["//foo:block"].xml }"; debug "XML for all elements:\n${ doc["//record"].xml.[", "] }"; spl-1.0pre6/examples/example40.expected0000644000000000000000000000047610340317675016620 0ustar rootrootSPL Debug: 42 SPL Debug: Variable 'foobar' not found. SPL Debug: I am 'foobar' from object A. SPL Debug: Foobar from object 'A'. SPL Debug: Barfoo from function test3(). SPL Debug: Foobar from object 'B'. SPL Debug: Barfoo from function test3(). SPL Debug: Foobar from object 'B'. SPL Debug: Barfoo from root context. spl-1.0pre6/examples/example32.expected0000644000000000000000000000003610224220762016600 0ustar rootrootSPL Debug: [[ Hello World! ]] spl-1.0pre6/examples/example17.spl0000644000000000000000000000233010235723304015601 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example17.spl: Simple example for task API */ load "task"; var wakeup_c = 0; function print_loop(ch) { for (var i=0; i<30; i++) write(ch); // the "++" operation is atomic! if ( ++wakeup_c == 2 ) task_continue("main"); } task_create("Task A", "print_loop('A');"); task_create("Task B", "print_loop('B');"); task_continue("Task A"); task_continue("Task B"); task_pause(); write("\n"); spl-1.0pre6/examples/example54.spl0000644000000000000000000000411010301100355015565 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example54.spl: simple example for modifying XML objects */ // SKIP_IF_NO_LIBXML2 // SKIP_IF_NO_REGEX load "xml"; var xmldoc = xml_parse(<:> : : : ... : ... : ... : ... : ... : ... : : ); foreach[] i (xmldoc["//@year"].nodes) xmldoc[i] =~ s/2004/2005/; xmldoc["//*[string(@year) = '1980']"].xml = "foo clifford's birthyear is 1980 bar"; xmldoc["//*[string(@year) = '2060']"].innerxml = "this record is in the future!"; xmldoc["//*[string(@year) = '1960']"].innerxml = xmldoc["//*[string(@year) = '2060']"].innerxml =~ s/future/past/R; delete xmldoc["//delme"]; xmldoc["//container"].add_xml_top = "MID"; xmldoc["//container"].add_xml_bottom = "BOT"; xmldoc["//container"].add_xml_top = "TOP"; xmldoc["//container"].add_xml_before = "BEF"; xmldoc["//container"].add_xml_after = "AFT"; debug xml_dump(xmldoc); spl-1.0pre6/examples/example24.expected0000644000000000000000000000016010201133507016572 0ustar rootrootThis is a test for embedded files (templates). .. another feature which should be used carefully! END-OF-SCRIPT spl-1.0pre6/examples/example16.expected0000644000000000000000000000010010151355115016572 0ustar rootrootTest for '?' ':' operator: ABCD Test for basic expressions: 1 1 spl-1.0pre6/examples/example19.spl0000644000000000000000000000363610235723304015615 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example19.spl: Simple example 'import' statement. */ object A { var name = "A"; static static_counter_a = 0; var var_counter_a = 0; method init() { debug "Running init() in $name (A)."; static_counter_a = 23; var_counter_a = 23; return this; } } object B { var name = "B"; static static_counter_b = 0; var var_counter_b = 0; method increment_b() { var_counter_b++; } method init() { debug "Running init() in $name (B)."; static_counter_b = 42; var_counter_b = 42; return this; } } object X A { import B; var name = "X"; method init() { *A.init(); *B.init(); return this; } } var myx = new X(); debug "${A.static_counter_a} ${X.static_counter_a} ${myx.static_counter_a} " ~ "${A.var_counter_a} ${X.var_counter_a} ${myx.var_counter_a}"; debug "${B.static_counter_b} ${X.static_counter_b} ${myx.static_counter_b} " ~ "${B.var_counter_b} ${X.var_counter_b} ${myx.var_counter_b}"; myx.increment_b(); debug "${B.var_counter_b} ${X.var_counter_b} ${myx.var_counter_b}"; X.increment_b(); debug "${B.var_counter_b} ${X.var_counter_b} ${myx.var_counter_b}"; spl-1.0pre6/examples/example56.spl0000644000000000000000000000314210321461404015602 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example56.spl: example for e// regexes and the 'E' regex modifier. */ // SKIP_IF_NO_REGEX var text = <:> :This is an example for e// regexes and the 'E' regex modifier. :Some ASCII Codes: A = #A, B = #B, C = #C, D = #D ; var counter = 0; var list = text =~ /#(.)/AINEg; if (list) { var newtext = $-; foreach[] $$ (list) newtext ~= "${ord($1)} (${++counter})" ~ $+; write(newtext); } write("---\n"); write(text =~ e/#(.)/Rg "${ord($1)} (${++counter})"); write("---\n"); text =~ e/#(.)/g "${ord($1)} (${++counter})"; write(text); write("---\n"); text = "123 #AA# 456 #XX# 789"; debug text =~ e/@(.).@/Rg "<${ord($1)}>"; debug text =~ e/#(.).#/Rg "<${ord($1)}>"; debug text =~ e/@(.).@/g "<${ord($1)}>"; debug text; debug text =~ e/#(.).#/g "<${ord($1)}>"; debug text; spl-1.0pre6/examples/example08.expected0000644000000000000000000000003210241103460016571 0ustar rootrootSPL Debug: Hällö Wörld spl-1.0pre6/examples/example58.spl0000644000000000000000000000274510340317675015627 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example58.spl: example for the pre-processor and implied ~ operator */ #define FUNC_LIST #define REGISTER(f) \ #define FUNC_LIST FUNC_LIST FOREACH_FUNC() function funcA(value) { debug "Function A: $value * 2 = ${value*2}"; } REGISTER(funcA) function funcB(value) { debug "Function B: " "$value / 2 = ${value/2}"; } REGISTER(funcB) function funcC(value) { debug 'Function '"C: $value ** 2 = " ~ (value**2); } REGISTER(funcC) #define CALL_ALL_FUNC(val) \ #define FOREACH_FUNC(f) \\ debug "Output of ${#f}($val):"; \\ f(val); \ FUNC_LIST \ #undef FOREACH_FUNC CALL_ALL_FUNC(23) CALL_ALL_FUNC(42) #define mysqrt(x) ((x)*(x)) debug mysqrt(mysqrt(2)); spl-1.0pre6/examples/example64.expected0000644000000000000000000000052310416727625016623 0ustar rootrootSPL Debug: A: 1 0 hello world SPL Debug: A: 0 0 this is SPL Debug: A: 0 1 a test SPL Debug: B: 1 0 hello world SPL Debug: B: 0 0 this is SPL Debug: B: 0 1 a test SPL Debug: C: SPL Debug: D: [0] This is a split test SPL Debug: D: [1] Blablabla SPL Debug: D: [2] Holadiro! spl-1.0pre6/examples/example56.expected0000644000000000000000000000105510321461404016606 0ustar rootrootThis is an example for e// regexes and the 'E' regex modifier. Some ASCII Codes: A = 65 (1), B = 66 (2), C = 67 (3), D = 68 (4) --- This is an example for e// regexes and the 'E' regex modifier. Some ASCII Codes: A = 65 (5), B = 66 (6), C = 67 (7), D = 68 (8) --- This is an example for e// regexes and the 'E' regex modifier. Some ASCII Codes: A = 65 (9), B = 66 (10), C = 67 (11), D = 68 (12) --- SPL Debug: 123 #AA# 456 #XX# 789 SPL Debug: 123 <65> 456 <88> 789 SPL Debug: 0 SPL Debug: 123 #AA# 456 #XX# 789 SPL Debug: 2 SPL Debug: 123 <65> 456 <88> 789 spl-1.0pre6/examples/example48.expected0000644000000000000000000000121110256005736016612 0ustar rootrootSPL Debug: Called demo(Test #1): SPL Debug: foo -> This is a test SPL Debug: bar -> This is another test SPL Debug: Called demo(Test #2): SPL Debug: foo -> This is a test SPL Debug: bar -> This is another test SPL Debug: Called demo(Test #3): SPL Debug: foo1 -> This is a test SPL Debug: bar1 -> This is another test SPL Debug: foo2 -> This is a test SPL Debug: bar2 -> This is another test SPL Debug: Called demo(Test #4): SPL Debug: 1 -> X1 Records: SPL Debug: foo1 -> This is gonna be used SPL Debug: bar1 -> This is another test SPL Debug: 2 -> X2 Records: SPL Debug: foo2 -> This is a test SPL Debug: bar2 -> This is another test spl-1.0pre6/examples/c-api-test1.c0000644000000000000000000000452311064463357015471 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * c-api-test1.c: Simple test for the plain bytecode interpreter. */ #ifndef USEWIN32API # include # include #else # define htonl(x) \ ((((unsigned int)(x) & 0xff000000) >> 24) | \ (((unsigned int)(x) & 0x00ff0000) >> 8) | \ (((unsigned int)(x) & 0x0000ff00) << 8) | \ (((unsigned int)(x) & 0x000000ff) << 24)) #endif #include #include "spl.h" unsigned char demoprog[] = SPL_SIGNATURE "\000" "\x24\xff\xff\xff\xff" // 0 - 4: pushc "hello" "\x24\xff\xff\xff\xff" // 5 - 9: pushc " " "\x24\xff\xff\xff\xff" // 10 - 14: pushc "world" "\xa2\xa2\xf8" // 15 - 17: cat, cat, debug "\x24\xff\xff\xff\xff" // 18 - 22: pushc "42" "\xf2" // 23 - 23: rlret "hello\x00" // 24 - 29: "hello" " \x00" // 30 - 31: " " "world\x00" // 32 - 37: "world" "42"; // 38 - 40: "42" int main() { *((int32_t*)(demoprog+ 1+sizeof(SPL_SIGNATURE))) = htonl(24 - 5); *((int32_t*)(demoprog+ 6+sizeof(SPL_SIGNATURE))) = htonl(30 - 10); *((int32_t*)(demoprog+11+sizeof(SPL_SIGNATURE))) = htonl(32 - 15); *((int32_t*)(demoprog+19+sizeof(SPL_SIGNATURE))) = htonl(38 - 23); if ( sizeof(demoprog) != 41+sizeof(SPL_SIGNATURE) ) printf("WARNING: demoprog[] is %d bytes long.\n", (int)sizeof(demoprog)); struct spl_vm *vm = spl_vm_create(); struct spl_task *task = spl_task_create(vm, 0); struct spl_code *code = spl_code_get(0); code->code_type = SPL_CODE_STATIC; code->code = demoprog; spl_task_setcode(task, code); while ( spl_exec(task) != 42 ) {} spl_vm_destroy(vm); return 0; } spl-1.0pre6/examples/example14.spltpl0000644000000000000000000000024010350526163016316 0ustar rootroot

Demo for iterators in templates

${articles.[a].header}

${articles.[a].body}


spl-1.0pre6/examples/c-api-test3.expected0000644000000000000000000000110410265443562017040 0ustar rootrootSPL Debug: [23] hallo world! SPL Debug: [24] hallo world! SPL Debug: [25] hallo world! SPL Debug: [26] hallo world! SPL Debug: [27] hallo world! SPL Debug: [28] hallo world! SPL Debug: [29] hallo world! SPL Debug: [30] hallo world! SPL Debug: [31] hallo world! SPL Debug: [32] hallo world! SPL Debug: [33] hallo world! SPL Debug: [34] hallo world! SPL Debug: [35] hallo world! SPL Debug: [36] hallo world! SPL Debug: [37] hallo world! SPL Debug: [38] hallo world! SPL Debug: [39] hallo world! SPL Debug: [40] hallo world! SPL Debug: [41] hallo world! SPL Debug: [42] hallo world! spl-1.0pre6/examples/example20.spl0000644000000000000000000000301310310023554015564 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example20.spl: Simple example for the "format_xml" module. */ // SKIP_IF_NO_EXPAT // SKIP_IF_NO_REGEX load "format_xml"; var xmlfile = #file-as-const example20.xml; var xmltree = format_xml_parse(xmlfile); function xmldump(node, reclv) { if ( node ~!= "" ) { var txt = node; txt =~ s/\n/ /g; write(' "$txt"'); } write("\n"); var i, j; foreach i (node) { for (j=0; j\n'); write(format_xml_dump(xmltree)); debug "Error handling test:"; try (e) { format_xml_parse("
"); catch FormatXmlEx: debug e.description; } spl-1.0pre6/examples/example01.expected0000644000000000000000000000007610150426042016575 0ustar rootrootSPL Debug: hello world! SPL Debug: What's the question to 42? spl-1.0pre6/examples/example22.spl0000644000000000000000000000237410335141631015603 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example22.spl: Example for gotos and labels */ function demo1() { var i = 0; write("demo1: "); continue_label: write("[${i++}]"); if ( i > 7 ) goto break_out; goto continue_label; break_out: write("\n"); } function demo2() { var i; for (i=0; i<42; i++) { if (i == 23) goto break_out; continue_label:; } if (i > 42) return; break_out: write("demo2: Now i is $i.\n"); goto continue_label; } demo1(); demo2(); spl-1.0pre6/examples/example24.spl0000644000000000000000000000207410235723304015604 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example24.spl: Example for embedded files */ write( #file-as-const *demo.txt ); #embedded-file demo.txt EOF This is a test for embedded files (templates). .. another feature which should be used carefully! EOF write("END-OF-SCRIPT\n"); spl-1.0pre6/examples/example61.spl0000644000000000000000000000272710350557066015622 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example61.spl: example for the 'hex', 'oct', 'bin' and 'fmt' builtins */ // SKIP_IF_NO_REGEX debug hex("0xfF") ~ " " ~ hex("0XFf") ~ " " ~ hex("FF"); debug oct("0o377") ~ " " ~ oct("0O377") ~ " " ~ oct("377"); debug bin("0b11111111") ~ " " ~ bin("0B11111111") ~ " " ~ bin("11111111"); var f = 10 ./ 3.7; debug fmt("%5d|%05d|%-5d|%5.4d|%d", 30, 31, 32, 33, 34) =~ s/ /:/Rg; debug fmt("%5x|%05X|%-5o|%5.4x|%b", 30, 31, 32, 33, 34) =~ s/ /:/Rg; debug fmt("%5d|%05d|%-5d|%5.4d|%d", -30, -31, -32, -33, -34) =~ s/ /:/Rg; debug fmt("%f|%.2f|%5.2f|%-5.2f|%05.2f", f, f, f, f, f) =~ s/ /:/Rg; debug fmt("%-*s|%*s", 12, "Hello", 12, "World") =~ s/ /:/Rg; spl-1.0pre6/examples/example41.expected0000644000000000000000000000004310240076203016573 0ustar rootrootSPL Debug: just another SPL hacker spl-1.0pre6/examples/example26.spl0000644000000000000000000000206310241657676015623 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example26.spl: Simple example for arrays */ var x = [ 2 => "This", 5 => "is", 7 => "a", 8 => "test!" ]; var y; var z; foreach i (x) { push y, x.[i]; debug "X: ${x.[i]}"; } while ( defined (z = shift y) ) debug "Y: $z"; spl-1.0pre6/examples/example33.expected0000644000000000000000000000015110224230405016572 0ustar rootrootSPL Debug: [0] -> A: 1, B: 2, C: 3 SPL Debug: [1] -> A: 1, B: 2, C: 3 SPL Debug: [2] -> A: 1, B: 2, C: 3 spl-1.0pre6/examples/example63.spl0000644000000000000000000000201510415222561015601 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2006 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example63.spl: simple example for temporary lvalues */ function foo() { return [ a: "hello", b: "world" ]; } function bar() { return [ x: "a", y: "b" ]; } debug foo().a ~ " " ~ foo()[bar().y]; spl-1.0pre6/examples/example28.spl0000644000000000000000000000250110243336414015604 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example28.spl: Simple test for SQLite bindings */ // SKIP_IF_NO_SQLITE load "sql"; load "sql_sqlite"; load "file"; var db = sql_connect("sqlite", "demodb.sqlite"); sql(db, "CREATE TABLE foobar ( x, y, z );"); var val = [ "0, 2, 3", "1, 2, 3", "1, 4, 2", "2, 4, 2" ]; foreach i (val) sql(db, "INSERT INTO foobar values (${val.[i]});"); var r = sql(db, "SELECT * FROM foobar WHERE x = 1"); foreach i (r) debug "$i: x=${r.[i].x}, y=${r.[i].y}, z=${r.[i].z}"; file_delete("demodb.sqlite"); spl-1.0pre6/examples/example25.expected0000644000000000000000000000006610202156722016605 0ustar rootrootSPL Debug: *** This is a <b>test</b>. *** spl-1.0pre6/examples/example17.expected0000644000000000000000000000007510175447100016610 0ustar rootrootABABABABABABABABABABABABABABABABABABABABABABABABABABABABABAB spl-1.0pre6/examples/example65.spl0000644000000000000000000000351010430724177015613 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2006 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example65.spl: simple example for the localization operator (_) */ // SKIP_IF_NO_REGEX function _(format, domain, @args) { format =~ e/[A-Za-z]/g ({ var char = $0; if (char =~ /[a-z]/) return chr(ord(char)+ord('A')-ord('a')); else return chr(ord(char)+ord('a')-ord('A')); }); format =~ e/\{([0-9]*)\}/g ($1 ~== "" ? "{" : args[$1]); if (defined domain) format = "$domain: $format"; return "[$format]"; } debug _"Hello World! The answer is ${21 * 2}."; debug _<:> : This is a localized template. : The sum of ${5} and ${3} is ${5+3}. : ; debug _<:> : It is possible to ${_"cascade"} localization texts: : ${_"This is ${_"a"} simple ${_"test"}."} : Isn't that ${_"wonderful"}? ; debug <:> : --begin-- : It is also possible to localize only parts of teplates. : The sum of ${5} and ${3} is ${5+3}. : --end-- ; debug _foobar_"Note that the '{' charachter is escaped using '{}'."; spl-1.0pre6/examples/example09.expected0000644000000000000000000000050710150426042016604 0ustar rootrootA "\n" ist converted to a newline and a "\$" to a $. A '\n' ist converted to a newline and a '\$' to a $. A "\n" ist converted to a newline and a "\$" to a $. A '\n' ist converted to a newline and a '\$' to a $. Embedded variables in strings: hello world! Embedded expressions is strings: 11 Even complex ones: 6 ... 17 ... 15 spl-1.0pre6/examples/example67.spl0000644000000000000000000000320610430156271015611 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2006 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example67.spl: example for dynamic typing and casting operators */ var x = 3.33; var y = 2; debug fmt("x*y == %5.3f %5.3f %5.3f", x*y, x.*y, x#*y); debug fmt("x/y == %5.3f %5.3f %5.3f", x/y, x./y, x#/y); x = #(x); debug "--- casting x to int ---"; debug fmt("x*y == %5.3f %5.3f %5.3f", x*y, x.*y, x#*y); debug fmt("x/y == %5.3f %5.3f %5.3f", x/y, x./y, x#/y); x = .(x); debug "-- casting x to float --"; debug fmt("x*y == %5.3f %5.3f %5.3f", x*y, x.*y, x#*y); debug fmt("x/y == %5.3f %5.3f %5.3f", x/y, x./y, x#/y); object Vector { var x,y; method get_text() { return "<$x/$y>"; } method operator_add(a, b) { return new Vector(a.x + b.x, a.y + b.y); } method init(_x, _y) { x=_x; y=_y; return this; } } var a = new Vector(23, 42), b = new Vector(37, -12); debug "${a.get_text()} ${b.get_text()} ${(a + b).get_text()}"; spl-1.0pre6/examples/example65.expected0000644000000000000000000000074210430724177016622 0ustar rootrootSPL Debug: [hELLO wORLD! tHE ANSWER IS 42.] SPL Debug: [tHIS IS A LOCALIZED TEMPLATE. tHE SUM OF 5 AND 3 IS 8. ] SPL Debug: [iT IS POSSIBLE TO [CASCADE] LOCALIZATION TEXTS: [tHIS IS [A] SIMPLE [TEST].] iSN'T THAT [WONDERFUL]? ] SPL Debug: --begin-- [iT IS ALSO POSSIBLE TO LOCALIZE ONLY PARTS OF TEPLATES.] [2ndline: tHE SUM OF 5 AND 3 IS 8.] --end-- SPL Debug: [foobar: nOTE THAT THE '{' CHARACHTER IS ESCAPED USING '{}'.] spl-1.0pre6/examples/example57.expected0000644000000000000000000000005510321736224016613 0ustar rootrootSPL Debug: The number 12 is smaller than 16. spl-1.0pre6/examples/example49.expected0000644000000000000000000000003010262752241016606 0ustar rootrootSPL Debug: Hello World! spl-1.0pre6/examples/c-api-test4.expected0000644000000000000000000000237010430156271017037 0ustar rootroot# SPL Assembler Dump :16 PUSHC "x" # (4 byte arg) 0 :21 PUSHC "23" # (4 byte arg) 2 :26 POPL :27 CHECKP :28 PUSHC "y" # (4 byte arg) 5 :33 PUSHC "42" # (4 byte arg) 7 :38 POPL :39 CHECKP :40 PUSHC "z" # (4 byte arg) 10 :45 PUSHC "y" # (4 byte arg) 5 :50 GETVAL :51 PUSHC "x" # (4 byte arg) 0 :56 GETVAL :57 SUB :58 POPL :59 CHECKP :60 PUSHC "x=" # (4 byte arg) 12 :65 PUSHC "x" # (4 byte arg) 0 :70 GETVAL :71 CAT :72 PUSHC " y=" # (4 byte arg) 15 :77 CAT :78 PUSHC "y" # (4 byte arg) 5 :83 GETVAL :84 CAT :85 PUSHC " z=" # (4 byte arg) 19 :90 CAT :91 PUSHC "z" # (4 byte arg) 10 :96 GETVAL :97 CAT :98 PUSHC "" # (4 byte arg) 1 :103 CAT :104 DEBUG :105 HALT # Total size: 113 bytes (90 text, 23 data). SPL Debug: x=23 y=42 z=19 spl-1.0pre6/examples/example31.spl0000644000000000000000000000215310235723304015600 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example31.spl: Simple example optimized && and || operations */ function f(t) { debug t; return 1; } if (0 && f("not reached")) f("not reached"); if (1 && f("1/5 reached")) f("2/5 reached"); if (0 || f("3/5 reached")) f("4/5 reached"); if (1 || f("not reached")) f("5/5 reached"); spl-1.0pre6/examples/example10.expected0000644000000000000000000000026010150425557016602 0ustar rootrootA foolish bigfoot and a barlish bigfoot are eating a barlish bigbart. .. found string 'eat'. .. didn't find string 'beat'. .. found string 'are'. .. didn't find string 'rare'. spl-1.0pre6/examples/example02.expected0000644000000000000000000000015110150426042016570 0ustar rootrootSPL Debug: [A] x is true (-1) SPL Debug: [B] x is false (0) SPL Debug: [C] x is true (1) SPL Debug: DONE spl-1.0pre6/examples/example33.spl0000644000000000000000000000217410241657676015624 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example33.spl: Simple example for vaargs like argument passing */ function bar(a, @y) { debug "[1] -> A: $a, B: ${y[0]}, C: ${y[1]}"; debug "[2] -> A: $a, B: ${ shift y }, C: ${ shift y }"; } function foo(a, b, c) { debug "[0] -> A: $a, B: $b, C: $c"; bar(a, b, c); } var x = [2, 3]; foo(1, @x); spl-1.0pre6/examples/example50.expected0000644000000000000000000000021010263200460016566 0ustar rootrootSPL Debug: Testing tags.. SPL Debug: Hello World! === this === is === a === test Bye, bye, world! spl-1.0pre6/examples/example35.spl0000644000000000000000000000203410235723304015602 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example35.spl: Simple example for debug, warning and error instructions */ function demo() { debug "This is a debug text."; warning "This is a warning text."; panic "This is an error text."; } demo(); spl-1.0pre6/examples/example42.expected0000644000000000000000000000244110242640132016600 0ustar rootrootSPL Debug: - 42: a1:42 a2:42 b1:42 b2:42 c1:42 c2:42 A1:42 A2:42 B1:42 B2:42 C1:42 C2:42 SPL Debug: a 43: a1:43 a2:43 b1:43 b2:43 c1:43 c2:43 A1:43 A2:43 B1:43 B2:43 C1:43 C2:43 SPL Debug: b 44: a1:44 a2:44 b1:44 b2:44 c1:44 c2:44 A1:44 A2:44 B1:44 B2:44 C1:44 C2:44 SPL Debug: c 45: a1:45 a2:45 b1:45 b2:45 c1:45 c2:45 A1:45 A2:45 B1:45 B2:45 C1:45 C2:45 SPL Debug: A 46: a1:46 a2:46 b1:46 b2:46 c1:46 c2:46 A1:46 A2:46 B1:46 B2:46 C1:46 C2:46 SPL Debug: B 47: a1:47 a2:47 b1:47 b2:47 c1:47 c2:47 A1:47 A2:47 B1:47 B2:47 C1:47 C2:47 SPL Debug: C 48: a1:48 a2:48 b1:48 b2:48 c1:48 c2:48 A1:48 A2:48 B1:48 B2:48 C1:48 C2:48 SPL Debug: a 47: a1:47 a2:47 b1:47 b2:47 c1:47 c2:47 A1:47 A2:47 B1:47 B2:47 C1:47 C2:47 SPL Debug: b 46: a1:46 a2:46 b1:46 b2:46 c1:46 c2:46 A1:46 A2:46 B1:46 B2:46 C1:46 C2:46 SPL Debug: c 45: a1:45 a2:45 b1:45 b2:45 c1:45 c2:45 A1:45 A2:45 B1:45 B2:45 C1:45 C2:45 SPL Debug: A 44: a1:44 a2:44 b1:44 b2:44 c1:44 c2:44 A1:44 A2:44 B1:44 B2:44 C1:44 C2:44 SPL Debug: B 43: a1:43 a2:43 b1:43 b2:43 c1:43 c2:43 A1:43 A2:43 B1:43 B2:43 C1:43 C2:43 SPL Debug: C 42: a1:42 a2:42 b1:42 b2:42 c1:42 c2:42 A1:42 A2:42 B1:42 B2:42 C1:42 C2:42 spl-1.0pre6/examples/example37.spl0000644000000000000000000000273410241657676015632 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example37.spl: Example for array module */ load "array"; function dump(d, a) { var t = d ~ ":"; foreach i (a) t ~= " $i->${a[i]}"; debug t; } var a = [ 10, 5, 33, 100, 2, 70, 80, 52 ]; dump("Original", a); array_sort_by_values(a, function(a,b) { return a > b; }); dump("Int Up", a); array_sort_by_values(a, function(a,b) { return a ~> b; }); dump("Text Up", a); array_sort_by_keys(a, function(a,b) { return a < b; }); dump("Keys Down", a); array_sort_by_values(a, function(a,b) { return a < b; }); dump("Int Down", a); array_sort_by_values(a, function(a,b) { return a ~< b; }); dump("Text Down", a); array_reindex(a); dump("Reindexed", a); spl-1.0pre6/examples/example34.expected0000644000000000000000000000203210232535003016574 0ustar rootrootSPL Debug: Going to throw exception [ Ex | Ex1 ]. SPL Debug: Now throwing the exception [ Ex | Ex1 ]. SPL Debug: Got exception Ex1: I am Ex1. ([ Ex | Ex1 ]) in: ???? (byte NUM in code block 'example34.spl') called by: ???? (byte NUM in code block 'example34.spl') called by: ROOT (byte NUM in code block 'example34.spl') SPL Debug: Going to throw exception [ Ex | Ex2 ]. SPL Debug: Now throwing the exception [ Ex | Ex2 ]. SPL Debug: Got exception Ex: I am Ex2. ([ Ex | Ex2 ]) SPL Debug: Going to throw exception [ Ex | Ex2 ]. SPL Debug: Now throwing the exception [ Ex | Ex2 ]. SPL Debug: Got exception Ex2: I am Ex2. ([ Ex | Ex2 ]) SPL Debug: Going to throw exception [ Ex ]. SPL Debug: Now throwing the exception [ Ex ]. SPL Runtime Error: Thrown uncaught exception: [ Ex ] >> This is an example exception object. >> Nice to have descriptions on exceptions.. ;-) in: ???? (byte NUM in code block 'example34.spl') called by: ???? (byte NUM in code block 'example34.spl') called by: ROOT (byte NUM in code block 'example34.spl') spl-1.0pre6/examples/example26.expected0000644000000000000000000000022010207651363016604 0ustar rootrootSPL Debug: X: This SPL Debug: X: is SPL Debug: X: a SPL Debug: X: test! SPL Debug: Y: This SPL Debug: Y: is SPL Debug: Y: a SPL Debug: Y: test! spl-1.0pre6/examples/example39.spl0000644000000000000000000000371210447766423015630 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example39.spl: Some more advanced regex tests */ // SKIP_IF_NO_REGEX function test1() { var x = "foolish bigfoot"; var r = x =~ /(?P(?P\S)\S*)\s*/APLg; foreach i (r) { var $$; r[i].word =~ s/foo(.*)/bar$1/; debug "Match #$i: [${r[i].firstchar}] ${r[i].word} ($0)"; } var text1 = "Ever seen a ${r[0].word} $1?"; var text2 = text1 =~ s/seen/beeing eaten by/R; debug text1; debug text2; } function test2() { var x ="foobar"; x =~ /(?Pf.+)/; debug "$1 == $<1> == $"; var a = $1; var b = $<1>; var c = $; debug "$a == $b == $c"; } function test3() { var x = "axbxxax"; x =~ s/(?x)/y/g; debug x; } function test4() { var text = "Hello World"; if (text =~ /(?P\S+)\s+(?P\S+)/) { import $$; debug "$foo $bar"; } if (declared foo) panic "This is never reached"; if (text =~ /(?P\S+)\s+(?P\S+)/I) { debug "$foo $bar"; } if (declared foo) debug "Foo is now defined here too."; } function test5() { function x() { "faxbar" =~ /f../; debug $0; } "foobar" =~ /f../; x(); debug $0; } test1(); test2(); test3(); test4(); test5(); spl-1.0pre6/examples/example18.expected0000644000000000000000000000022210176664121016610 0ustar rootrootSPL Debug: Running init() in B (B). SPL Debug: Running init() in A (A). SPL Debug: Running init() in B (A). SPL Debug: Running foobar() in B (A). spl-1.0pre6/examples/example66.expected0000644000000000000000000000034110430724177016616 0ustar rootrootFoobar 42 is greater then 4*2. 4+2 is not greater than 42. This is a demo! The answer is 42. A: Current counter: 7 A: Current counter: 8 A: Current counter: 9 B: Current counter: 7 B: Current counter: 8 B: Current counter: 9 spl-1.0pre6/examples/example58.expected0000644000000000000000000000064711300472330016614 0ustar rootrootSPL Debug: Output of funcA(23): SPL Debug: Function A: 23 * 2 = 46 SPL Debug: Output of funcB(23): SPL Debug: Function B: 23 / 2 = 11.5 SPL Debug: Output of funcC(23): SPL Debug: Function C: 23 ** 2 = 529 SPL Debug: Output of funcA(42): SPL Debug: Function A: 42 * 2 = 84 SPL Debug: Output of funcB(42): SPL Debug: Function B: 42 / 2 = 21 SPL Debug: Output of funcC(42): SPL Debug: Function C: 42 ** 2 = 1764 SPL Debug: 16 spl-1.0pre6/examples/c-api-test2.c0000644000000000000000000000266511064463357015477 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * c-api-test2.c: Simple test for the assembler interface. */ #include "spl.h" int main() { struct spl_vm *vm = spl_vm_create(); struct spl_asm *as = spl_asm_create(); spl_asm_add(as, SPL_OP_PUSHC, "hello "); spl_asm_add(as, SPL_OP_PUSHC, "world!"); spl_asm_add(as, SPL_OP_CAT, 0); spl_asm_add(as, SPL_OP_DEBUG, 0); spl_asm_add(as, SPL_OP_PUSHC, "42"); spl_asm_add(as, SPL_OP_RLRET, 0); struct spl_code *code = spl_asm_dump(as); spl_asm_destroy(as); struct spl_task *task = spl_task_create(vm, 0); spl_task_setcode(task, code); while ( spl_exec(task) != 42 ) {} spl_vm_destroy(vm); return 0; } spl-1.0pre6/examples/example01.spl0000644000000000000000000000204010235723304015570 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example01.spl: Simple test for variables and math */ var h = "hello"; var w = "world!"; var a = 2; var b = 3; var c = 5; var d = 7; debug "$h $w"; debug "What's the question to " ~ (a * b + c * d + 1) ~ "?"; spl-1.0pre6/examples/example03.spl0000644000000000000000000000210310235723304015572 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example03.spl: Simple test for WHILE and FOR loops */ var i = 0; while (i < 10) { debug "[ $i ] while test"; i = i + 1; } // note that this 'i' is not the global 'i' for (var i=20; i>=15; i--) debug "[ $i ] for test"; debug "[ $i ] DONE."; spl-1.0pre6/examples/example40.spl0000644000000000000000000000325210434660641015606 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example40.spl: Walking thru the contexts examples */ var barfoo = "Barfoo from root context."; function test1() { function create_foobar(value) { var [*].foobar = value; } create_foobar(42); debug foobar; } function test2() { object A { var foobar = "I am 'foobar' from object A."; } object B { method print_foobar() { if (declared foobar) debug foobar; else debug "Variable 'foobar' not found."; } } B.print_foobar(); B.[*] = A; B.print_foobar(); } function test3() { var barfoo = "Barfoo from function test3()."; object A { var foobar = "Foobar from object 'A'."; } object B { var foobar = "Foobar from object 'B'."; } object C A { method test() { debug foobar; debug barfoo; } } (new C()).test(); C[+] = B; (new C()).test(); C[*] = [/]; (new C()).test(); } test1(); test2(); test3(); spl-1.0pre6/examples/example11.expected0000644000000000000000000000054410235723304016603 0ustar rootrootJust another SPL code file. #import-as-code is like a C #include. This is all compile-time. So don't build loops! ;-) * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf This is a template file for SPL. It is like a quoted string which allows $ substitutions. Do not underestimate the power of SPL! spl-1.0pre6/examples/example05.spl0000644000000000000000000000335010235723304015601 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example05.spl: Simple test for objects */ object generic_counter { var item = 0, counter = 0; static static_counter = 0; method increment() { counter++; static_counter++; } method show() { debug "You have $counter $item (static=$static_counter)."; } method init() { return this; } } object apple_counter generic_counter { var item = "Apples"; method show() { debug "The current apple counter value is: " ~ counter; *generic_counter.show(); } } object banana_counter generic_counter { var item = "Bananas"; method show() { debug "The current banana counter value is: " ~ counter; *generic_counter.show(); } } var my_apples = new apple_counter(); var my_bananas = new banana_counter(); for (var i=0; i<23; i++) my_apples.increment(); my_apples.show(); for (var i=0; i<42; i++) my_bananas.increment(); my_bananas.show(); debug "Static counter: ${generic_counter.static_counter}"; spl-1.0pre6/examples/example42.spl0000644000000000000000000000321410242640132015574 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example42.spl: The hardcore test for 'static' object members */ object Value { var value; method init(v) { value = v; return this; } } object A { static v1 = new Value(42); static v2 = 42; } object B A { } object C { import A; } var a = new A(); var b = new B(); var c = new C(); var list = [ a:a, b:b, c:c, A:A, B:B, C:C ]; function print_all(obj, val) { var out = "$obj $val:"; foreach i (list) { var v1 = list[i].v1.value, v2 = list[i].v2; out ~= (v1 == val ? " " : " !") ~ "${i}1:$v1"; out ~= (v2 == val ? " " : " !") ~ "${i}2:$v2"; } debug out; } var current = 42; print_all("-", 42); foreach i (list) { current++; list[i].v1.value++; list[i].v2++; print_all(i, current); } foreach i (list) { current--; list[i].v1 = new Value(current); list[i].v2 = current; print_all(i, current); } spl-1.0pre6/examples/example03.expected0000644000000000000000000000072210150426042016575 0ustar rootrootSPL Debug: [ 0 ] while test SPL Debug: [ 1 ] while test SPL Debug: [ 2 ] while test SPL Debug: [ 3 ] while test SPL Debug: [ 4 ] while test SPL Debug: [ 5 ] while test SPL Debug: [ 6 ] while test SPL Debug: [ 7 ] while test SPL Debug: [ 8 ] while test SPL Debug: [ 9 ] while test SPL Debug: [ 20 ] for test SPL Debug: [ 19 ] for test SPL Debug: [ 18 ] for test SPL Debug: [ 17 ] for test SPL Debug: [ 16 ] for test SPL Debug: [ 15 ] for test SPL Debug: [ 10 ] DONE. spl-1.0pre6/examples/example07.spl0000644000000000000000000000220310235723304015577 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example07.spl: Simple test for hosted nodes */ load "prime"; debug "prime.['abc'] is${ defined prime.['abc'] ? "" : " not" } defined."; debug "prime.['123'] is${ defined prime.['123'] ? "" : " not" } defined."; for (var i=1; i<20; i++) debug "prime.[$i] => ${prime.[i]}"; prime.[42]=1; delete prime.[23]; spl-1.0pre6/examples/example44.spl0000644000000000000000000000216410253273565015617 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example44.spl: example for pointer comparison operators */ var a, b = 42; var num = 1; function checkit(x, y, z) { debug "Test #${num++} (should be ${z ? " true" : "false"}): " ~ ((x ^== y) == z ? "OK" : "Error"); } checkit(a, b, 0); a = b; checkit(a, b, 1); a += 0; checkit(a, b, 0); spl-1.0pre6/examples/example51.expected0000644000000000000000000000011310264726077016613 0ustar rootrootSPL Debug: x is 1 SPL Debug: x is 2 SPL Debug: x is 3 SPL Debug: whatever! spl-1.0pre6/examples/example09.spl0000644000000000000000000000264110235723304015607 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example09.spl: Tests for extended string parsing */ $write("A \"\\n\" ist converted to a newline and a \"\\\$\" to a \$.\n"); $write('A \'\\n\' ist converted to a newline and a \'\\\$\' to a \$.\n'); write('A "\\n" ist converted to a newline and a "\\\$" to a \$.\n'); write("A '\\n' ist converted to a newline and a '\\\$' to a \$.\n"); var hlo="hello", wrld="world"; write("Embedded variables in strings: $hlo $wrld!\n"); var a = 2, b = 3, c = 5; write("Embedded expressions is strings: ${ a * b + c }\n"); write("Even complex ones: ${ (a * b) ~ " ... ${ a + b * c } ... " ~ (b * c) }\n"); spl-1.0pre6/examples/example46.spl0000644000000000000000000000247610334147312015615 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example46.spl: simple example for math functions */ var map; var output; #define pi 3.14159265 /* this is floating point stuff. so the results may differ slightly on * different architectures... */ for (var i=0; i.<=pi./2; i.+=.01) { var x = round(cos(i) .* 40); var y = round(sin(i) .* 20); map[x][y] = " "; } for (var y=0; y<=21; y++) { for (var x=0; x<=41; x++) output ~= declared map[x][y] ? map[x][y] : "*"; output ~= "\n"; } debug "Simple sin/cos example program:\n" ~ output; spl-1.0pre6/examples/example43.expected0000644000000000000000000000006010242647240016603 0ustar rootrootSPL Debug: a->x SPL Debug: b->y SPL Debug: 0->z spl-1.0pre6/examples/example35.expected0000644000000000000000000000054010232535003016577 0ustar rootrootSPL Debug: This is a debug text. SPL Runtime Warning: This is a warning text. in: ???? (byte NUM in code block 'example35.spl') called by: ROOT (byte NUM in code block 'example35.spl') SPL Runtime Error: This is an error text. in: ???? (byte NUM in code block 'example35.spl') called by: ROOT (byte NUM in code block 'example35.spl') spl-1.0pre6/examples/example48.spl0000644000000000000000000000263410256005736015621 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * example48.spl: example for named parameters */ function demo(name, %args) { debug "Called demo($name):"; foreach i (args) debug "\t$i\t-> ${args[i]}"; } demo("Test #1", foo: "This is a test", bar: "This is another test"); demo(foo: "This is a test", "Test #2", bar: "This is another test"); var x1 = [ foo1: "This is a test", bar1: "This is another test" ]; var x2 = [ foo2: "This is a test", bar2: "This is another test" ]; demo(%x1, "Test #3", %x2 ); demo("Test #4", 1: "X1 Records:", foo1: "This is gonna be used", %x1, bar1: "This is gonna be ignored", 2: "X2 Records:", %x2); spl-1.0pre6/examples/example27.expected0000644000000000000000000000002210210073432016572 0ustar rootrootSPL Debug: 634739 spl-1.0pre6/examples/example19.expected0000644000000000000000000000024210233427025016605 0ustar rootrootSPL Debug: Running init() in X (A). SPL Debug: Running init() in X (B). SPL Debug: 23 23 23 0 0 23 SPL Debug: 42 42 42 0 0 42 SPL Debug: 0 0 43 SPL Debug: 0 1 43 spl-1.0pre6/examples/example11.code0000644000000000000000000000022310147476570015722 0ustar rootroot write("Just another SPL code file. #import-as-code is like a C #include.\n"); write("This is all compile-time. So don't build loops! ;-)\n\n"); spl-1.0pre6/examples/example67.expected0000644000000000000000000000050411300472330016604 0ustar rootrootSPL Debug: x*y == 6.660 6.660 6.000 SPL Debug: x/y == 1.665 1.665 1.000 SPL Debug: --- casting x to int --- SPL Debug: x*y == 6.000 6.000 6.000 SPL Debug: x/y == 1.500 1.500 1.000 SPL Debug: -- casting x to float -- SPL Debug: x*y == 6.000 6.000 6.000 SPL Debug: x/y == 1.500 1.500 1.000 SPL Debug: <23/42> <37/-12> <60/30> spl-1.0pre6/examples/example59.expected0000644000000000000000000000264310335141631016617 0ustar rootrootSPL Debug: [0] for: 0 -> This is SPL Debug: [0] for: 1 -> a test! SPL Debug: [0] for: 4 -> Yo! SPL Debug: [0] foreach: 0 -> This is SPL Debug: [0] foreach: 1 -> a test! SPL Debug: [0] foreach: 4 -> Yo! SPL Debug: [0] foreach[]: This is SPL Debug: [0] foreach[]: a test! SPL Debug: [0] foreach[]: Yo! SPL Debug: [0] while: This is SPL Debug: [0] while: a test! SPL Debug: [0] while: Yo! SPL Debug: [0] do-while: This is SPL Debug: [0] do-while: a test! SPL Debug: [0] do-while: Yo! SPL Debug: [1] for: 0 -> This is SPL Debug: [1] for: 1 -> a test! SPL Debug: [1] for: 4 -> Yo! SPL Debug: [1] foreach: 0 -> This is SPL Debug: [1] foreach: 1 -> a test! SPL Debug: [1] foreach: 4 -> Yo! SPL Debug: [1] foreach[]: This is SPL Debug: [1] foreach[]: a test! SPL Debug: [1] foreach[]: Yo! SPL Debug: [1] while: This is SPL Debug: [1] while: a test! SPL Debug: [1] while: Yo! SPL Debug: [1] do-while: This is SPL Debug: [1] do-while: a test! SPL Debug: [1] do-while: Yo! SPL Debug: [2] for: 0 -> This is SPL Debug: [2] for: 1 -> a test! SPL Debug: [2] for: 4 -> Yo! SPL Debug: [2] foreach: 0 -> This is SPL Debug: [2] foreach: 1 -> a test! SPL Debug: [2] foreach: 4 -> Yo! SPL Debug: [2] foreach[]: This is SPL Debug: [2] foreach[]: a test! SPL Debug: [2] foreach[]: Yo! SPL Debug: [2] while: This is SPL Debug: [2] while: a test! SPL Debug: [2] while: Yo! SPL Debug: [2] do-while: This is SPL Debug: [2] do-while: a test! SPL Debug: [2] do-while: Yo! spl-1.0pre6/examples/example11.spltpl0000644000000000000000000000040110350526163016312 0ustar rootroot This is a template file for $language. It is like a quoted string which allows $$ substitutions. Do not underestimate the power of ${ "S" ~ ({ if ( "apples" ~== "bananas" ) return "S"; if ( "apples" ~!= "bananas" ) return "P"; return "L"; }) ~ "L" }! spl-1.0pre6/restore.c0000644000000000000000000002401511064463357013305 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * restore.c: Restore the SPL VM state from a dump file * * WARNING: We assume that it is save to cast pointers to long and vice versa here. * This is true for all (most) UNIX systems, but the C standard doesn't require * long to have the same storage size as a pointer. */ #include #include #include #ifdef ENABLE_PTHREAD_SUPPORT #include #endif #include "spl.h" enum KEY_IDS { KEY_NONE, KEY_IDS, KEY_NODE, KEY_CODE, KEY_TASK, KEY_VM, KEY_CLS, KEY_CTX, KEY_CTX_TYPE, KEY_SHAONE, KEY_CODE_IP, KEY_CODE_TYPE, KEY_NIDX, KEY_SUBS, KEY_VALUE, KEY_HOSTED, KEY_HDATA, KEY_STACK, KEY_ROOT, KEY_SIZE, KEY_FLAGS, KEY_ID, KEY_MOD, KEY_NAME, KEY_END }; static struct { char *string; int id; } keywords[] = { { "IDS", KEY_IDS }, { "NODE", KEY_NODE }, { "CODE", KEY_CODE }, { "TASK", KEY_TASK }, { "VM", KEY_VM }, { "CLS", KEY_CLS }, { "CTX", KEY_CTX }, { "CTX_TYPE", KEY_CTX_TYPE }, { "SHAONE", KEY_SHAONE }, { "CODE_IP", KEY_CODE_IP }, { "CODE_TYPE", KEY_CODE_TYPE }, { "NIDX", KEY_NIDX }, { "SUBS", KEY_SUBS }, { "VALUE", KEY_VALUE }, { "HOSTED", KEY_HOSTED }, { "HDATA", KEY_HDATA }, { "STACK", KEY_STACK }, { "ROOT", KEY_ROOT }, { "SIZE", KEY_SIZE }, { "FLAGS", KEY_FLAGS }, { "ID", KEY_ID }, { "MOD", KEY_MOD }, { "NAME", KEY_NAME }, { "END", KEY_END }, { 0, 0 } }; static FILE *restore_file; static struct spl_code **code_map; static struct spl_node **node_map; #ifdef ENABLE_PTHREAD_SUPPORT static pthread_mutex_t restore_lck = PTHREAD_MUTEX_INITIALIZER; #endif static int read_key() { char buffer[10]; int ch = fgetc(restore_file); if (ch == EOF) return 0; if (ch != '\n' && ch != ' ') ungetc(ch, restore_file); for (int i=0; i<9; i++) { ch = fgetc(restore_file); if ( ch == EOF ) return 0; if ( (ch < 'A' || ch > 'Z') && ch != '_' ) { ungetc(ch, restore_file); buffer[i] = 0; break; } else buffer[i] = ch; } buffer[9] = 0; for (int i=0; keywords[i].string; i++) if (!strcmp(buffer, keywords[i].string)) return keywords[i].id; return -1; } static long read_num() { int ch = fgetc(restore_file); int value = 0; if (ch != '=' && ch != ',') return 0; while (1) { ch = fgetc(restore_file); if (ch == EOF) return 0; if ( ch < '0' || ch > '9' ) { ungetc(ch, restore_file); return value; } value = value*10 + (ch - '0'); } } static char *read_string() { int ch = fgetc(restore_file); if (ch == '(') ungetc(ch, restore_file); else if (ch != '=') { ungetc(ch, restore_file); return 0; } ch = fgetc(restore_file); if (ch != '(') return 0; char *value = malloc(1024); int size = 0, roof = 1024; while (1) { ch = fgetc(restore_file); if (ch == EOF) return 0; if ( ch == ')' ) { value[size] = 0; return realloc(value, size+1); } if ( ch == '%' ) { int c1 = fgetc(restore_file); int c2 = fgetc(restore_file); if ( c1 == EOF || c2 == EOF ) return 0; ch = 0; ch |= ( c1 >= '0' && c1 <= '9' ? c1 - '0' : (c1 - 'A') + 10 ) << 4; ch |= ( c2 >= '0' && c2 <= '9' ? c2 - '0' : (c2 - 'A') + 10 ); } value[size++] = ch; if ( size > roof-10 ) { roof += 1024; value = realloc(value, roof); } } } static unsigned char *read_data(void) { int ch = fgetc(restore_file); if (ch != '=') return 0; ch = fgetc(restore_file); if (ch != '{') return 0; char *value = malloc(1024); int size = 0, roof = 1024; while (1) { int c1 = fgetc(restore_file); if (c1 == EOF) return 0; if ( c1 == '}' ) { return realloc(value, size); } int c2 = fgetc(restore_file); if (c2 == EOF) return 0; ch = 0; ch |= ( c1 >= '0' && c1 <= '9' ? c1 - '0' : (c1 - 'A') + 10 ) << 4; ch |= ( c2 >= '0' && c2 <= '9' ? c2 - '0' : (c2 - 'A') + 10 ); value[size++] = ch; if ( size > roof-10 ) { roof += 1024; value = realloc(value, roof); } } } #define EXPECT(x) \ ({ if (!(x)) { spl_report(SPL_REPORT_RESTORE, vm, "Syntax error in tuple %d (%s:%d).\n", \ lineno, __FILE__, __LINE__); vm=0; goto restore_finished; } }) struct spl_vm *spl_restore_ascii(struct spl_vm *vm, FILE *file) { struct spl_task **last_taskp = &vm->task_list; int map_size = 0; int lineno = 0; #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_lock(&restore_lck); #endif restore_file = file; spl_put(vm, vm->root); vm->root = 0; while (1) { int key = read_key(); long id = read_num(); lineno++; switch ( key ) { case KEY_IDS: { EXPECT(read_key() == KEY_VALUE); map_size = read_num(); EXPECT(read_key() == KEY_VALUE); char *sig = read_string(); int sigcheck = !strcmp(sig, SPL_SIGNATURE); free(sig); EXPECT(sigcheck); code_map = calloc(map_size, sizeof(struct spl_code*)); node_map = calloc(map_size, sizeof(struct spl_node*)); break; } case KEY_CODE: { struct spl_code *code = calloc(1, sizeof(struct spl_code)); spl_state_counter_malloc_inc(); code_map[id] = code; EXPECT(read_key() == KEY_CODE_TYPE); code->code_type = read_num(); code->code_type = SPL_CODE_MALLOCED; EXPECT(read_key() == KEY_SIZE); code->size = read_num(); EXPECT(read_key() == KEY_ID); code->id = read_string(); EXPECT(read_key() == KEY_SHAONE); code->sha1 = read_string(); EXPECT(read_key() == KEY_CODE); code->code = read_data(); if (!code->code && code->sha1 && vm->codecache_dir) { int fn_size = strlen(vm->codecache_dir) + 100; char fn[fn_size]; snprintf(fn, fn_size, "%s/%s.splb", vm->codecache_dir, code->sha1); code->code_type = SPL_CODE_MAPPED; code->code = spl_mmap_file(fn, &code->size); EXPECT(code->code != NULL); } break; } case KEY_NODE: { struct spl_node *node = calloc(1, sizeof(struct spl_node)); spl_state_counter_malloc_inc(); node_map[id] = node; EXPECT(read_key() == KEY_FLAGS); node->flags = read_num() & ~SPL_NODE_FLAG_GC; EXPECT(read_key() == KEY_CLS); node->cls = (struct spl_node*)read_num(); EXPECT(read_key() == KEY_CTX); node->ctx = (struct spl_node*)read_num(); EXPECT(read_key() == KEY_CTX_TYPE); node->ctx_type = read_num(); EXPECT(read_key() == KEY_CODE); node->code = code_map[read_num()]; if ( node->code ) node->code->ref_counter++; EXPECT(read_key() == KEY_CODE_IP); node->code_ip = read_num(); EXPECT(read_key() == KEY_NIDX); node->subs_next_idx = read_num(); EXPECT(read_key() == KEY_SUBS); while ( (id = read_num()) ) { struct spl_node_sub *s = calloc(1, sizeof(struct spl_node_sub)); char ch = fgetc(restore_file); if ( ch == ':' ) s->module = read_string(); else ungetc(ch, restore_file); s->node = (struct spl_node*)id; s->key = read_string(); node->subs_counter++; if ( !node->subs_end ) { node->subs_begin = s; node->subs_end = s; } else { node->subs_end->next = s; s->last = node->subs_end; node->subs_end = s; } } EXPECT(read_key() == KEY_VALUE); char *str = read_string(); if (str) { node->value_string = spl_string_new(0, 0, 0, str, 0); node->value = SPL_VALUE_STRING; } EXPECT(read_key() == KEY_HOSTED); node->hnode_name = read_string(); EXPECT(read_key() == KEY_HDATA); node->hnode_dump = read_string(); break; } case KEY_TASK: { struct spl_task *task = calloc(1, sizeof(struct spl_task)); spl_state_counter_malloc_inc(); EXPECT(read_key() == KEY_CTX); task->ctx = node_map[read_num()]; if ( task->ctx ) task->ctx->ref_counter++; EXPECT(read_key() == KEY_CODE); task->code = code_map[read_num()]; if ( task->code ) task->code->ref_counter++; EXPECT(read_key() == KEY_CODE_IP); task->code_ip = read_num(); EXPECT(read_key() == KEY_FLAGS); task->flags = read_num(); EXPECT(read_key() == KEY_ID); task->id = read_string(); EXPECT(read_key() == KEY_STACK); struct spl_node_stack **l = &task->stack; while ( (id = read_num()) ) { struct spl_node_stack *s = spl_vm_malloc_node_stack_element(vm); s->next = 0; s->node = node_map[id]; if ( s->node ) s->node->ref_counter++; *l = s; l = &s->next; } task->vm = vm; *last_taskp = task; last_taskp = &task->next; break; } case KEY_VM: { EXPECT(read_key() == KEY_ROOT); vm->root = node_map[read_num()]; if ( vm->root ) vm->root->ref_counter++; free(code_map); goto reference_nodes; } default: EXPECT(0); } } reference_nodes: for (int i=0; ictx = node_map[(long)node_map[i]->ctx]; if ( node_map[i]->ctx ) node_map[i]->ctx->ref_counter++; node_map[i]->cls = node_map[(long)node_map[i]->cls]; if ( node_map[i]->cls ) node_map[i]->cls->ref_counter++; struct spl_node_sub *s = node_map[i]->subs_begin; while (s) { s->node = node_map[(long)s->node]; if ( s->node ) s->node->ref_counter++; s = s->next; } } free(node_map); while (1) { int key = read_key(); read_num(); lineno++; switch ( key ) { case KEY_MOD: { EXPECT(read_key() == KEY_NAME); char *name = read_string(); EXPECT(name != 0); fflush(stdout); spl_module_load(vm, name, 1); free(name); break; } case KEY_END: goto restore_finished; default: EXPECT(0); } } restore_finished: #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_unlock(&restore_lck); #endif return vm; } spl-1.0pre6/splrun.c0000644000000000000000000003424411064463357013152 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * splrun.c: SPL command line interpreter */ #include #include #include #include #include #include #include #include #include #ifndef USEWIN32API # include #endif #include "spl.h" #include "compat.h" static int got_sigint = 0; void help(int ret) { printf("\n"); switch ( ret ) { case 2: printf("Input-Output Error!\n\n"); break; } printf("Usage: splrun [Options] [-d dump-file] [{-x|-m} bytecode-file] \\\n"); printf(" { spl-source-file | -q spl-source | -a assembler-file | \\\n"); printf(" -c bytecode-file | {-R|-r} restore-file }\n\n"); printf(" -N Do not execute the program - just compile or restore\n"); printf(" -A Show assembler dump (only available when compiling)\n"); printf(" -O Show assembler dump before the optimizer modified it\n"); printf(" -D Enable Debug output for all instructions executed\n"); printf(" -C Run reference-counter checks after each instruction\n"); printf(" -S Print machine state to standard error on exit\n"); printf(" -SS Print machine state to standard error after each instruction\n"); printf(" -T Create a tree-dump to standard error on exit\n"); printf(" -F Show global SPL malloc/free counters on exit\n"); printf(" -P Run the parser in debug mode (set spl_yydebug to 1)\n"); printf(" -X Print a dummy C source file for xgettext (don't mix with -o)\n"); printf(" -t Print data segment (when -A is also passed)\n"); printf(" -e Compile with debug symbols\n"); printf(" -E Run command line debugger\n\n"); printf(" -o Do not run the optimizer\n\n"); printf(" -p name[=val] Set parameter to be passed to program\n\n"); printf(" -K directory Set the code cache directory\n\n"); printf(" -M path Set the module search path\n\n"); printf(" -b addr Set breakpoint\n"); printf(" -s Stepping one instruction forward\n\n"); printf(" -ss Step one instruction, dump, reload, ..\n"); printf(" (needs \"-d file\" option)\n\n"); printf(" -B file Collect benchmark data and write to file\n\n"); printf(" -q source Execute a literally passed script\n\n"); printf(" -a file Assemble and execute\n"); printf(" -c file Execute Pre-compiled binary\n"); printf(" -x file Write compiled binary to file\n\n"); printf(" -m file Write a executeable SPL binary file\n\n"); printf(" -d file Dump state to file on exit\n"); printf(" -r file Restore state from dumped file\n"); printf(" -R file Restore from and dump to this file\n\n"); exit(ret); } static void sigint_handler(int dummy UNUSED); static void sigint_handler(int dummy UNUSED) { got_sigint = 1; return; } struct parameter { char *name, *value; struct parameter *next; }; int main(int argc, char **argv) { int opt_dump_asm = 0; int opt_dump_asm_preopt = 0; int opt_no_optimizer = 0; int opt_no_exec = 0; int opt_debugger = 0; int opt_debug_exec = 0; int opt_debug_sym = 0; int opt_dump_state = 0; int opt_stepping = 0; FILE *opt_dump_file = 0; FILE *opt_restore_file = 0; FILE *opt_bench_file = 0; int *bench_data_usec = 0; int *bench_data_iter = 0; float bench_total_usec = 0; struct spl_code *bench_data_code = 0; char *opt_gen_bytecode = 0; int opt_gen_bytecode_exec = 0; int opt_read_assembler = 0; int opt_read_bytecode = 0; int opt_show_malloc_counter = 0; int opt_print_data_seg = 0; int opt_treedump = 0; int opt_rccheck = 0; int opt, rc = 0, ret = 1; int breakpoint = -1; char *module_path = 0; char *codecache_dir = 0; char *cmdline_script = 0; struct parameter *para_list = 0; signal(SIGINT, sigint_handler); while ( (opt = getopt(argc, argv, "+p:q:AtODCNSeEod:r:R:b:sB:PXacm:x:K:M:TF")) != -1 ) { switch ( opt ) { case 'p': { struct parameter *p = malloc(sizeof(struct parameter)); p->name = strdup(optarg); p->value = strchr(p->name, '='); if (p->value) { *p->value = 0; p->value++; } p->next = para_list; para_list = p; break; } case 'q': { cmdline_script = optarg; break; } case 'A': opt_dump_asm = 1; break; case 't': opt_print_data_seg = 1; break; case 'O': opt_dump_asm_preopt = 1; break; case 'D': opt_debug_exec = 1; break; case 'C': opt_rccheck = 1; break; case 'N': opt_no_exec = 1; break; case 'S': opt_dump_state++; break; case 'e': opt_debug_sym = 1; break; case 'E': opt_debugger = 1; break; case 'o': opt_no_optimizer = 1; break; case 'd': opt_dump_file = fopen(optarg, "w+"); break; case 'R': opt_dump_file = opt_restore_file = fopen(optarg, "r+"); break; case 'r': opt_restore_file = fopen(optarg, "r"); break; case 'b': breakpoint = atoi(optarg); break; case 's': opt_stepping++; break; case 'B': opt_bench_file = fopen(optarg, "w+"); break; case 'P': spl_yydebug = 1; break; case 'X': spl_dump_translatable_strings = 1; break; case 'a': opt_read_assembler = 1; break; case 'c': opt_read_bytecode = 1; break; case 'm': opt_gen_bytecode_exec = 1; case 'x': opt_gen_bytecode = optarg; break; case 'K': codecache_dir = optarg; break; case 'M': module_path = optarg; break; case 'T': opt_treedump = 1; break; case 'F': opt_show_malloc_counter = 1; break; default: help(1); } } if ( !opt_restore_file && !cmdline_script && optind+1 > argc ) help(1); if ( opt_restore_file && optind != argc ) help(1); if ( opt_stepping > 1 && !opt_dump_file ) help(1); if ( spl_dump_translatable_strings && opt_no_optimizer ) help(1); struct spl_vm *vm; struct spl_task *task; dump_reload_loop: vm = spl_vm_create(); if (module_path) vm->path = strdup(module_path); else my_asprintf(&vm->path, ".:./spl_modules:%s", spl_system_modules_dir()); vm->codecache_dir = codecache_dir ? strdup(codecache_dir) : 0; vm->runloop = spl_simple_runloop; if ( opt_restore_file ) { if (!spl_restore_ascii(vm, opt_restore_file)) return 1; task = vm->task_list; if ( opt_dump_file == opt_restore_file ) { rewind(opt_dump_file); #ifndef USEWIN32API /* hmm.. there is no ftruncate on mingw32 ? * ok - so we simply do not truncate the file, * the restore function will ignore everything * after the END token anyways. */ ftruncate(fileno(opt_dump_file), 0); #endif } else fclose(opt_restore_file); } else if ( opt_read_bytecode ) { struct spl_code *code = spl_code_get(0); code->code_type = SPL_CODE_MAPPED; code->code = spl_mmap_file(argv[optind++], &code->size); if (!code->code) help(2); if (*code->code == '#') { spl_state_counter_free_inc(); code->code_type = SPL_CODE_STATIC; while (*code->code != SPL_OP_SIG && code->size > 0) { code->code++; code->size--; } } task = spl_task_create(vm, "main"); spl_task_setcode(task, code); } else { task = spl_task_create(vm, "main"); struct spl_asm *as = spl_asm_create(); as->vm = vm; if ( opt_read_assembler ) { FILE *f = fopen(argv[optind++], "r"); char line[1024]; int lineno=1; if (!f) help(2); while ( fgets(line, 1024, f) ) { if ( spl_asm_parse_line(as, line) < 0 ) { printf("... in line %d.\n", lineno); exit(1); } lineno++; } if ( spl_asm_resolve_labels(as) < 0 ) exit(1); fclose(f); } else { char *spl_source = cmdline_script ?: spl_malloc_file(argv[optind++], 0); if ( !spl_source) { spl_asm_destroy(as); spl_vm_destroy(vm); help(2); } if ( spl_compiler(as, spl_source, cmdline_script ? "command line" : argv[optind-1], spl_malloc_file, opt_debug_sym) ) return 1; spl_asm_add(as, SPL_OP_HALT, 0); if (!cmdline_script) free(spl_source); } if (opt_dump_asm_preopt) spl_asm_print(as, opt_print_data_seg); if ( !opt_no_optimizer ) spl_optimizer(as); if (opt_dump_asm) spl_asm_print(as, opt_print_data_seg); if (opt_gen_bytecode) { int fd = open(opt_gen_bytecode, O_WRONLY|O_TRUNC|O_CREAT|MY_O_BINARY, opt_gen_bytecode_exec ? 0777 : 0666); if ( fd < 0) { printf("Can't write bytecode file!\n"); return 1; } if ( opt_gen_bytecode_exec ) { char *magic_cookie = "#!/bin/sh\nexec splrun -c \"$0\" \"$@\"; exit $?\n"; write(fd, magic_cookie, strlen(magic_cookie)); } spl_asm_write(as, fd); spl_asm_destroy(as); spl_vm_destroy(vm); return 0; } spl_task_setcode(task, spl_asm_dump(as)); task->code->id = strdup(cmdline_script ? "command line" : argv[optind-1]); spl_asm_destroy(as); } if (opt_bench_file && task->code) { bench_data_code = spl_code_get(task->code); bench_data_usec = calloc(bench_data_code->size, sizeof(int)); bench_data_iter = calloc(bench_data_code->size, sizeof(int)); } task->debug = opt_debug_exec; if ( optind < argc ) { int spl_argc = 0; struct spl_node *spl_argv = spl_get(0); spl_create(task, vm->root, "argv", spl_argv, SPL_CREATE_LOCAL); while (optind < argc) { spl_create(task, spl_argv, 0, SPL_NEW_STRING_DUP(argv[optind++]), SPL_CREATE_LOCAL); spl_argc++; } spl_create(task, vm->root, "argc", SPL_NEW_INT(spl_argc), SPL_CREATE_LOCAL); } if ( para_list ) { struct spl_node *pn = spl_get(0); spl_create(task, vm->root, "parameters", pn, SPL_CREATE_LOCAL); while (para_list) { struct parameter *p = para_list; spl_create(task, pn, p->name, p->value ? SPL_NEW_STRING_DUP(p->value) : spl_get(0), SPL_CREATE_LOCAL); para_list = p->next; free(p->name); free(p); } } if ( !opt_no_exec ) { #ifndef USEWIN32API struct rusage ru_before, ru_after; int ru_code_ip = 0; #endif spl_builtin_register_all(vm); while ( 1 ) { spl_gc_maybe(vm); task = spl_schedule(task); if ( !task ) break; if ( task->code_ip == breakpoint ) { fprintf(stderr, "** breakpoint reached **\n"); break; } #ifndef USEWIN32API if (bench_data_code) { getrusage(RUSAGE_SELF, &ru_before); ru_code_ip = task->code_ip; } #endif if ( !task->code || (rc = (opt_debugger ? spl_debug_exec : spl_exec)(task)) || got_sigint || opt_stepping) break; #ifndef USEWIN32API if (bench_data_code && ru_code_ip) { getrusage(RUSAGE_SELF, &ru_after); int ip = bench_data_code == task->code ? ru_code_ip : 0; int usec = (ru_after.ru_utime.tv_usec - ru_before.ru_utime.tv_usec) + ((ru_after.ru_utime.tv_sec - ru_before.ru_utime.tv_sec) * 1000000); bench_total_usec+=usec; bench_data_usec[ip]+=usec; bench_data_iter[ip]++; } #endif if ( opt_rccheck ) spl_rccheck(vm); if ( opt_dump_state > 1 ) { spl_dump_ascii(vm, stderr); fprintf(stderr, "----\n"); } } } if ( opt_dump_file ) { if (spl_dump_ascii(vm, opt_dump_file)) { fprintf(stderr, "Unable to dump VM state (see dumpfile for details).\n"); } else { if ( opt_stepping > 1 && task && task->code ) { rewind(opt_dump_file); opt_restore_file = opt_dump_file; spl_vm_destroy(vm); goto dump_reload_loop; } } fclose(opt_dump_file); } if ( opt_dump_state ) spl_dump_ascii(vm, stderr); if ( opt_treedump ) spl_treedump(vm, stderr, SPL_TREEDUMP_FLAG_ALL, 0); if ( ret != 1 ) fprintf(stderr, "Program returned %d.\n", ret); if (opt_debugger) spl_debug_reset(); if (bench_data_code) { char *sep = "-------+-------------------------------------+" "--------------------------------\n"; char title[100]; snprintf(title, 100, "%-6s | %4s %7s %9s %12s |\n", "", "%TM", "ITER", "USEC", "US/IT"); fprintf(opt_bench_file, "%s%s", title, sep); for (int i=0; isize; i++) { if (!bench_data_iter[i]) continue; fprintf(opt_bench_file, ":%-5d | %4.1f %7d %9d %12.2f | %-*s", i, (float)bench_data_usec[i]*100 / bench_total_usec, bench_data_iter[i], bench_data_usec[i], (float)bench_data_usec[i]/bench_data_iter[i], bench_data_code->code[i] < 0x60 ? 10 : 0, spl_asm_op2txt(bench_data_code->code[i])); if (bench_data_code->code[i] < 0x60) { int arg, arg_size = 4 - (bench_data_code->code[i] & 3); arg = spl_bytes_to_int(arg_size, bench_data_code->code+i+1); if (bench_data_code->code[i] < 0x20) fprintf(opt_bench_file, ":%d", arg+i+arg_size+1); else { fputc('"', opt_bench_file); for (int j = 0; (bench_data_code->code+i+arg+arg_size+1)[j]; j++) switch ((bench_data_code->code+i+arg+arg_size+1)[j]) { case '\\': fprintf(opt_bench_file, "\\\\"); break; case '\"': fprintf(opt_bench_file, "\\\""); break; case '\n': fprintf(opt_bench_file, "\\n"); break; case '\r': fprintf(opt_bench_file, "\\r"); break; case '\t': fprintf(opt_bench_file, "\\t"); break; default: fputc((bench_data_code->code+i+arg+arg_size+1)[j], opt_bench_file); } fputc('"', opt_bench_file); } } fprintf(opt_bench_file, "\n"); } fprintf(opt_bench_file, "%s", sep); spl_code_put(bench_data_code); free(bench_data_usec); free(bench_data_iter); bench_data_code = 0; } if (opt_bench_file) fclose(opt_bench_file); spl_vm_destroy(vm); if ( spl_state_counter_malloc_get() != spl_state_counter_free_get() ) fprintf(stderr, "Malloc/free counter unbalanced: %d / %d\n", spl_state_counter_malloc_get(), spl_state_counter_free_get()); else if ( opt_show_malloc_counter ) fprintf(stderr, "Malloc/free counter: %d / %d\n", spl_state_counter_malloc_get(), spl_state_counter_free_get()); return rc; } spl-1.0pre6/exec.c0000644000000000000000000013507511300472330012537 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * exec.c: The interpreter runloop. All the opcodes are defined here. */ #include #include #include #include #include #include #include #include "spl.h" #include "compat.h" #ifdef ENABLE_REGEX_SUPPORT # include #endif /* wrapper to catch empty-stack conditions */ #define spl_pop(t) \ ({ \ struct spl_node *inner_n = spl_pop(t); \ if ( !inner_n ) retval = -1; \ inner_n; \ }) int spl_exec(struct spl_task *task) { unsigned char op, orig_op; int orig_ip; int32_t arg = 0; char *txtarg = 0; int retval = 0; if ( !task ) return -1; if ( task->flags & SPL_TASK_FLAG_BUSY ) { spl_report(SPL_REPORT_RUNTIME, task, "This task is already busy! (spl_exec() recursion?)\n"); return -1; } if ( !task->code ) { spl_report(SPL_REPORT_RUNTIME, task, "No code to execute! (Got already HALT instruction?)\n"); return -1; } task->flags |= SPL_TASK_FLAG_BUSY; task->vm->ic++; orig_ip = task->code_ip; orig_op = op = task->code->code[task->code_ip++]; if ( orig_ip == 0 && op != SPL_OP_SIG ) { spl_report(SPL_REPORT_RUNTIME, task, "Bytecode has no signature!\n"); spl_task_setcode(task, 0); retval = -1; goto finish; } if ( op < 0x60 ) { int arg_size = 4 - (op & 3); op = op & ~3; arg = spl_bytes_to_int(arg_size, task->code->code + task->code_ip); task->code_ip += arg_size; if ( op >= 0x20 ) txtarg = (char*)(task->code->code + task->code_ip + arg); } if ( task->debug ) { if ( op < 0x20 ) spl_report(SPL_REPORT_RUNTIME|SPL_REPORT_DEBUG, task, " :%d\t%s\t:%d (%d, %d bytes)\n", orig_ip, spl_asm_op2txt(op), (int)(task->code_ip + arg), (int)arg, 4 - (orig_op & 3)); else if ( op < 0x60 ) spl_report(SPL_REPORT_RUNTIME|SPL_REPORT_DEBUG, task, " :%d\t%s\t\"%s\" (%d, %d bytes)\n", orig_ip, spl_asm_op2txt(op), txtarg, (int)arg, 4 - (orig_op & 3)); else spl_report(SPL_REPORT_RUNTIME|SPL_REPORT_DEBUG, task, " :%d\t%s\n", orig_ip, spl_asm_op2txt(op)); } switch ( op ) { /* opcodes with addr argument */ case SPL_OP_JUMP: { task->code_ip += arg; task->debug_str = 0; goto finish; } case SPL_OP_REGF: case SPL_OP_REGM: { struct spl_node *f = spl_get(0); f->ctx = spl_get(task->ctx); f->code = spl_code_get(task->code); f->code_ip = task->code_ip; if ( op == SPL_OP_REGF ) f->flags |= SPL_NODE_FLAG_FUNCTION; if ( op == SPL_OP_REGM ) f->flags |= SPL_NODE_FLAG_METHOD; char *s; if (task->debug_str) my_asprintf(&s, "[SPL Codepointer: %s]", task->debug_str); else my_asprintf(&s, "[SPL Codepointer: byte %d in '%s']", f->code_ip, f->code && f->code->id ? f->code->id : ""); spl_set_string(f, s); spl_push(task, f); task->code_ip += arg; task->debug_str = 0; goto finish; } case SPL_OP_ENTER: { struct spl_node *r = spl_get(0); r->ctx = task->ctx; task->ctx = spl_get(0); if (task->debug_str) spl_set_string(r, strdup(task->debug_str)); r->code = spl_code_get(task->code); r->code_ip = task->code_ip + arg; r->flags |= SPL_NODE_FLAG_RETINFO; spl_push(task, r); task->ctx->ctx_type = SPL_CTX_FUNCTION; task->ctx->ctx = spl_get(r->ctx); goto finish; } case SPL_OP_GOTO: { int newip = task->code_ip + arg; int c_create = 0, c_destroy = 0; int c_min = 0, c_cur = 0; if ( newip > task->code_ip ) { /* Jumping down */ for (int i = task->code_ip; i < newip; i++) { int inner_op = task->code->code[i]; if ( inner_op < 0x60 ) i += 4 - (inner_op & 3); if ( inner_op == SPL_OP_BEGIN ) c_cur++; if ( inner_op == SPL_OP_END ) c_cur--; if ( c_cur < c_min ) c_min = c_cur; } c_create = c_cur - c_min; c_destroy = -c_min; } if ( newip < task->code_ip ) { /* Jumping up */ for (int i = newip; i < task->code_ip; i++) { int inner_op = task->code->code[i]; if ( inner_op < 0x60 ) i += 4 - (inner_op & 3); if ( inner_op == SPL_OP_BEGIN ) c_cur++; if ( inner_op == SPL_OP_END ) c_cur--; if ( c_cur < c_min ) c_min = c_cur; } c_create = -c_min; c_destroy = c_cur - c_min; } for (int i = 0; i < c_destroy; i++) { spl_cleanup(task, task->ctx); task->ctx = spl_get(task->ctx->ctx); } for (int i = 0; i < c_create; i++) { struct spl_node *c = spl_get(0); c->ctx_type = SPL_CTX_LOCAL; c->ctx = task->ctx; task->ctx=c; } task->code_ip = newip; goto finish; } case SPL_OP_CATCH: { char *n = spl_get_string(spl_cleanup(task, spl_pop(task))); char cn[strlen(n) + 100]; struct spl_node *e = spl_lookup(task, task->ctx, n, 0); if (!e) { spl_report(SPL_REPORT_RUNTIME, task, "Trying to catch '%s', but no such object is declared!\n", n); retval = -1; goto finish; } snprintf(cn, strlen(n) + 100, "#catch_%s", n); struct spl_node *c = spl_get(0); c->flags |= SPL_NODE_FLAG_CATCH; c->code = spl_code_get(task->code); c->code_ip = task->code_ip + arg; c->ctx = spl_get(e); spl_create(task, task->ctx, cn, c, SPL_CREATE_LOCAL); goto finish; } /* opcodes with string argument */ case SPL_OP_PUSH: { struct spl_node *src = spl_lookup(task, task->ctx, txtarg, 0); spl_push(task, spl_get(src)); goto finish; } case SPL_OP_PUSHC: { spl_push(task, SPL_NEW_SPL_STRING(spl_string_new(SPL_STRING_STATIC, 0, 0, txtarg, task->code))); goto finish; } case SPL_OP_POP: { spl_create(task, task->ctx, txtarg, spl_pop(task), 0); goto finish; } case SPL_OP_PUSHA: { struct spl_node *c = spl_pop(task); struct spl_node *src = spl_lookup(task, task->ctx, txtarg, 0); spl_push(task, spl_get(src)); spl_set_int(c, spl_get_int(c) + 1); c->flags |= SPL_NODE_FLAG_ARGC; spl_push(task, c); goto finish; } case SPL_OP_PUSHAC: { struct spl_node *c = spl_pop(task); spl_push(task, SPL_NEW_SPL_STRING(spl_string_new(SPL_STRING_STATIC, 0, 0, txtarg, task->code))); spl_set_int(c, spl_get_int(c) + 1); c->flags |= SPL_NODE_FLAG_ARGC; spl_push(task, c); goto finish; } case SPL_OP_POPA: { struct spl_node *retinfo = spl_pop(task); struct spl_node *c = spl_pop(task); if ( spl_get_int(c) > 0 ) { struct spl_node *v = spl_pop(task); spl_create(task, task->ctx, txtarg, v, SPL_CREATE_LOCAL); spl_set_int(c, spl_get_int(c) - 1); } else { spl_create(task, task->ctx, txtarg, spl_get(0), SPL_CREATE_LOCAL); } c->flags |= SPL_NODE_FLAG_ARGC; spl_push(task, c); spl_push(task, retinfo); goto finish; } case SPL_OP_CLIB: { if ( spl_clib_call(task, txtarg) < 0 ) { spl_report(SPL_REPORT_RUNTIME, task, "Can't find CLIB handler for '%s'!\n", txtarg); retval = -1; } else { if (task->stack && task->stack->node && (task->stack->node->flags & SPL_NODE_FLAG_CLEXCEPT)) { task->stack->node->flags &= ~SPL_NODE_FLAG_CLEXCEPT; goto throw_entry_point; } } goto finish; } case SPL_OP_DCALL: { char txtarg_dyn[strlen(txtarg)+2]; sprintf(txtarg_dyn, "?%s", txtarg); struct spl_node *f = spl_lookup(task, task->ctx, txtarg_dyn, 0); if ( f && f->code && (f->flags & (SPL_NODE_FLAG_FUNCTION|SPL_NODE_FLAG_METHOD)) ) { struct spl_node *r = spl_get(0); r->ctx = task->ctx; task->ctx = spl_get(0); if (task->debug_str) spl_set_string(r, strdup(task->debug_str)); r->code = task->code; task->code = spl_code_get(f->code); r->code_ip = task->code_ip; task->code_ip = f->code_ip; task->debug_str = 0; r->flags |= SPL_NODE_FLAG_RETINFO; spl_push(task, r); task->ctx->ctx_type = SPL_CTX_FUNCTION; task->ctx->ctx = f->ctx ? spl_get(f->ctx) : 0; task->ctx->cls = f->cls ? spl_get(f->cls) : 0; goto finish; } if ( spl_clib_call(task, txtarg) < 0 ) { spl_report(SPL_REPORT_RUNTIME, task, "Can't find function or CLIB handler for '%s'!\n", txtarg); retval = -1; } else { if (task->stack && task->stack->node && (task->stack->node->flags & SPL_NODE_FLAG_CLEXCEPT)) { task->stack->node->flags &= ~SPL_NODE_FLAG_CLEXCEPT; goto throw_entry_point; } } goto finish; } case SPL_OP_DBGSYM: { task->debug_str = txtarg; goto finish; } op_objop: case SPL_OP_OBJOP: { struct spl_node *n2 = SPL_POP_CLEANUP(task); struct spl_node *n1 = SPL_POP_CLEANUP(task); if ( !n1 || n1->ctx_type != SPL_CTX_OBJECT) { spl_report(SPL_REPORT_RUNTIME, task, "Opcode OBJOP executed with a non-object argument!\n"); retval = -1; goto finish; } char method_name[strlen(txtarg) + 20]; snprintf(method_name, strlen(txtarg) + 20, "operator_%s", txtarg); struct spl_node *method = spl_lookup(task, n1, method_name, SPL_LOOKUP_NOCTX); if ( !method ) { spl_report(SPL_REPORT_RUNTIME, task, "Opcode OBJOP executed for non-defined method '%s'!\n", method_name); retval = -1; goto finish; } spl_push(task, spl_get(n2)); spl_push(task, spl_get(n1)); spl_push(task, SPL_NEW_INT(2)); spl_push(task, spl_get(method)); goto op_call; } /* opcodes without arguments */ case SPL_OP_ZERO: { spl_push(task, SPL_NEW_INT(0)); goto finish; } case SPL_OP_ONE: { spl_push(task, SPL_NEW_INT(1)); goto finish; } case SPL_OP_UNDEF: { spl_push(task, spl_get(0)); goto finish; } #ifdef ENABLE_REGEX_SUPPORT case SPL_OP_REMATCH: case SPL_OP_RESUBST: { unsigned char *text = 0; unsigned int text_len = 0; char *id = 0, *match, *subst = 0, *mod; struct spl_node *result_node = spl_get(0); struct spl_node *last_result_for_context = 0; struct spl_node *last_result2_for_context = 0; struct spl_string *origtext = 0; struct spl_node *textnode = 0; mod = spl_get_string(SPL_POP_CLEANUP(task)); if (op == SPL_OP_RESUBST) subst = spl_get_string(SPL_POP_CLEANUP(task)); struct spl_string *match_string = spl_get_spl_string(SPL_POP_CLEANUP(task)); match = spl_string(match_string); int options = 0; int do_global = 0; const char *error; int erroffset, rc; int ovector[300]; int rematched = 0; pcre *re = 0; pcre_extra *pe = 0; int with_context = 0; int use_last_match = 0; int pass_array = 0; int pass_by_number = 0; int pass_by_name = 0; int pass_subst = 0; int do_import = 0; int do_split = 0; int startoffset = 0; int newtext_pos = 0; int newtext_len = 0; int newtext_avail = 0; char *newtext = 0; for (int i=0; mod[i]; i++) switch (mod[i]) { case 'i': options |= PCRE_CASELESS; break; case 's': options |= PCRE_DOTALL; break; case 'x': options |= PCRE_EXTENDED; break; case 'm': options |= PCRE_MULTILINE; break; case 'g': do_global = 1; break; case 'E': with_context = 1; break; case 'L': use_last_match = 1; break; case 'A': pass_array = 1; break; case 'N': pass_by_number = 1; break; case 'P': pass_by_name = 1; break; case 'R': pass_subst = 1; break; case 'I': do_import = 1; break; case 'S': do_split = 1; break; default: spl_report(SPL_REPORT_RUNTIME, task, "Regex Engine: Unrecognised regex modifier: '%c'!\n", mod[i]); retval = -1; goto re_finish; } if (do_split && (pass_by_number || pass_by_name || pass_array)) { spl_report(SPL_REPORT_RUNTIME, task, "Regex Engine: Unrecognised regex modifier combination: '%s'!\n", mod); retval = -1; goto re_finish; } if (op == SPL_OP_RESUBST) { if (pass_subst) { textnode = spl_pop(task); } else { id = spl_get_string(SPL_POP_CLEANUP(task)); textnode = spl_get(spl_lookup(task, task->ctx, id, 0)); } origtext = spl_get_spl_string(textnode); } else { origtext = spl_get_spl_string(SPL_POP_CLEANUP(task)); } text = (unsigned char *)spl_string(origtext); text_len = origtext ? origtext->total_len : 0; if (match_string && (match_string->flags & SPL_STRING_UTF8)) options |= PCRE_UTF8; if (origtext && (origtext->flags & SPL_STRING_UTF8)) options |= PCRE_UTF8; if (pass_subst && textnode) spl_set_spl_string(result_node, spl_string_get(spl_get_spl_string(textnode))); re = pcre_compile(match, options, &error, &erroffset, 0); if ( re == 0 ) { spl_report(SPL_REPORT_RUNTIME, task, "Regex Engine: %s at char %d!\n", error, erroffset); retval = -1; goto re_finish; } pe = pcre_study(re, 0, &error); if ( error ) { spl_report(SPL_REPORT_RUNTIME, task, "Regex Engine: %s!\n", error); retval = -1; goto re_finish; } regex_redo: if (do_global == 2) { rc = pcre_exec(re, pe, (char*)text, text_len, startoffset, PCRE_NOTEMPTY|PCRE_ANCHORED, ovector, 300); if ( rc == PCRE_ERROR_NOMATCH && text[startoffset]) { // skip one utf-8 encoded unicode character int extra_startoffset = 1; while ( text[startoffset+extra_startoffset] > 0x7f && text[startoffset+extra_startoffset] < 0xc0 ) extra_startoffset++; rc = pcre_exec(re, pe, (char*)text, text_len, startoffset+extra_startoffset, 0, ovector, 300); } } else { rc = pcre_exec(re, pe, (char*)text, text_len, startoffset, 0, ovector, 300); } if ( rc == 0 ) rc = 100; if ( rc == PCRE_ERROR_NOMATCH ) goto re_finish; if ( rc < 0 ) { spl_report(SPL_REPORT_RUNTIME, task, "Regex Engine: PCRE Error %d (see 'man pcreapi')!\n", rc); retval = -1; goto re_finish; } struct spl_node *result = spl_get(0); result->flags |= SPL_NODE_FLAG_RERES; struct spl_node *result_node2 = 0; if (pass_by_number || pass_by_name) { if (pass_array) { result_node2 = spl_get(0); spl_create(task, result_node, 0, result_node2, SPL_CREATE_LOCAL); } else result_node2 = result_node; } for (int i = 0; i < rc; i++) { int offset = ovector[2*i]; int length = ovector[2*i+1] - ovector[2*i]; char key[32]; snprintf(key, 32, "%d", i); struct spl_node *valnode = offset < 0 ? spl_get(0) : SPL_NEW_SPL_STRING(spl_string_split(origtext, offset, length)); if (pass_by_number) spl_create(task, result_node2, key, spl_get(valnode), SPL_CREATE_LOCAL); if (i == 0 && pass_array && !pass_by_number && !pass_by_name) spl_create(task, result_node, 0, spl_get(valnode), SPL_CREATE_LOCAL); spl_create(task, result, key, valnode, SPL_CREATE_LOCAL); } if (do_split || with_context) { if (do_split) spl_create(task, result_node, 0, ovector[0] < 0 ? spl_get(0) : SPL_NEW_SPL_STRING(spl_string_split(origtext, startoffset, ovector[0] - startoffset)), SPL_CREATE_LOCAL); if (with_context) { struct spl_node *valnode = ovector[0] < 0 ? spl_get(0) : SPL_NEW_SPL_STRING(spl_string_split(origtext, startoffset, ovector[0] - startoffset)); // HENC "+" => "=LC" if (last_result_for_context) spl_create(task, last_result_for_context, "=LC", spl_get(valnode), SPL_CREATE_LOCAL); if (last_result2_for_context) spl_create(task, last_result2_for_context, "=LC", spl_get(valnode), SPL_CREATE_LOCAL); // HENC "-" => "=NC" spl_create(task, result, "=NC", valnode, SPL_CREATE_LOCAL); if (result_node2) spl_create(task, result_node2, "=NC", spl_get(valnode), SPL_CREATE_LOCAL); last_result_for_context = result; last_result2_for_context = result_node2; } } int nametab_count; int nametab_entsize; const unsigned char *nametab; if ( !pcre_fullinfo(re, 0, PCRE_INFO_NAMECOUNT, &nametab_count) && !pcre_fullinfo(re, 0, PCRE_INFO_NAMEENTRYSIZE, &nametab_entsize) && !pcre_fullinfo(re, 0, PCRE_INFO_NAMETABLE, &nametab) ) for (int i=0; i < nametab_count; i++) { unsigned int captnum = nametab[i*nametab_entsize] << 8 | nametab[i*nametab_entsize+1]; const char *name = (const char*)nametab + (i*nametab_entsize+2); int offset = ovector[2*captnum]; int length = ovector[2*captnum+1] - ovector[2*captnum]; struct spl_node *valnode = offset < 0 ? spl_get(0) : SPL_NEW_SPL_STRING(spl_string_split(origtext, offset, length)); if (pass_by_name) spl_create(task, result_node2, name, spl_get(valnode), SPL_CREATE_LOCAL); if (do_import) spl_create(task, task->ctx, name, spl_get(valnode), SPL_CREATE_LOCAL); spl_create(task, result, name, valnode, SPL_CREATE_LOCAL); } if (rematched == 0 || use_last_match == 1) spl_create(task, task->ctx, "#reres", result, SPL_CREATE_FUNCLOCAL); else { spl_put(task->vm, result); last_result_for_context = 0; } rematched++; if (op == SPL_OP_RESUBST) { newtext_len = newtext_pos + (ovector[0] - startoffset) + (text_len - ovector[1]); for (int i=0; subst[i]; i++) { if (subst[i] == '\\' && subst[i+1]) { newtext_len++; i++; } else if (subst[i] == '$' && isdigit((unsigned char)subst[i+1])) { int captnum = subst[i+1] - '0'; if (captnum >= rc) { rematched = 0; spl_report(SPL_REPORT_RUNTIME, task, "Regex Engine: Substitution refers to not existing capture $<%d>!\n", captnum); retval = -1; goto re_finish; } newtext_len += ovector[2*captnum+1] - ovector[2*captnum]; i++; } else if (subst[i] == '$' && subst[i+1] == '<') { int namelen = strcspn(subst+2, ">"); char *endptr, *name = my_strndupa(subst+2, namelen); if (subst[i+namelen+2] != '>') { rematched = 0; spl_report(SPL_REPORT_RUNTIME, task, "Regex Engine: Missing '>' in '$< .. >' in regex substitution!\n"); retval = -1; goto re_finish; } int captnum = strtol(name, &endptr, 10); if (*endptr) captnum = pcre_get_stringnumber(re, name); if (captnum >= rc || captnum < 0) { rematched = 0; spl_report(SPL_REPORT_RUNTIME, task, "Regex Engine: Substitution refers to not existing capture $<%s>!\n", name); retval = -1; goto re_finish; } newtext_len += ovector[2*captnum+1] - ovector[2*captnum]; i += namelen+2; } else newtext_len++; } if (newtext_len+1 > newtext_avail) { newtext_avail = newtext_len + 1000; newtext = realloc(newtext, newtext_avail); } if (startoffset < ovector[0]) { int length = ovector[0] - startoffset; memcpy(newtext + newtext_pos, text + startoffset, length); newtext_pos += length; } for (int i=0; subst[i]; i++) { if (subst[i] == '\\' && subst[i+1]) { switch (subst[i+1]) { case 'a': newtext[newtext_pos++] = '\a'; break; case 'b': newtext[newtext_pos++] = '\b'; break; case 't': newtext[newtext_pos++] = '\t'; break; case 'n': newtext[newtext_pos++] = '\n'; break; case 'v': newtext[newtext_pos++] = '\v'; break; case 'f': newtext[newtext_pos++] = '\f'; break; case 'r': newtext[newtext_pos++] = '\r'; break; default: newtext[newtext_pos++] = subst[i+1]; } i++; } else if (subst[i] == '$' && isdigit((unsigned char)subst[i+1])) { int captnum = subst[i+1] - '0'; int length = ovector[2*captnum+1] - ovector[2*captnum]; memcpy(newtext+newtext_pos, text + ovector[2*captnum], length); newtext_pos += length; i++; } else if (subst[i] == '$' && subst[i+1] == '<') { int namelen = strcspn(subst+2, ">"); char *endptr, *name = my_strndupa(subst+2, namelen); int captnum = strtol(name, &endptr, 10); if (*endptr) captnum = pcre_get_stringnumber(re, name); int length = ovector[2*captnum+1] - ovector[2*captnum]; memcpy(newtext+newtext_pos, text + ovector[2*captnum], length); newtext_pos += length; i += namelen+2; } else newtext[newtext_pos++] = subst[i]; } strcpy(newtext + newtext_pos, (char*)text + ovector[1]); } startoffset = ovector[1]; if (do_global && text[startoffset]) { do_global = 2; goto regex_redo; } re_finish: if (last_result_for_context || last_result2_for_context) { struct spl_node *valnode = SPL_NEW_SPL_STRING(spl_string_split(origtext, startoffset, (origtext ? origtext->total_len : 0) - startoffset)); // HENC "+" => "=LC" if (last_result_for_context) spl_create(task, last_result_for_context, "=LC", spl_get(valnode), SPL_CREATE_LOCAL); if (last_result2_for_context) spl_create(task, last_result2_for_context, "=LC", spl_get(valnode), SPL_CREATE_LOCAL); spl_put(task->vm, valnode); } if (do_split) spl_create(task, result_node, 0, SPL_NEW_SPL_STRING(spl_string_split(origtext, startoffset, (origtext ? origtext->total_len : 0) - startoffset)), SPL_CREATE_LOCAL); if (textnode) spl_put(task->vm, textnode); if (newtext) { newtext = realloc(newtext, newtext_len+1); if (pass_subst && textnode) spl_set_spl_string(result_node, spl_string_new(0, 0, 0, newtext, 0)); else spl_create(task, task->ctx, id, SPL_NEW_STRING(newtext), 0); } if ( !(pass_subst && textnode) ) spl_set_int(result_node, rematched); spl_push(task, result_node); pcre_free(re); pcre_free(pe); goto finish; } #else case SPL_OP_REMATCH: case SPL_OP_RESUBST: { spl_report(SPL_REPORT_RUNTIME, task, "Regex support not enabled.\n"); retval = -1; goto finish; } #endif case SPL_OP_DEFINED: { struct spl_node *n = SPL_POP_CLEANUP(task); spl_push(task, SPL_NEW_INT( n->value != 0 ? 1 : 0 )); goto finish; } case SPL_OP_DECLARED: { char *name = spl_get_string(SPL_POP_CLEANUP(task)); char *name_dot = strrchr(name, '.'); char name_dyn[strlen(name)+2]; if (name_dot) sprintf(name_dyn, "%.*s.?%s", (int)(name_dot-name), name, name_dot+1); else sprintf(name_dyn, "?%s", name); struct spl_node *n = spl_lookup(task, task->ctx, name_dyn, 0); spl_push(task, SPL_NEW_INT( n != 0 ? 1 : 0 )); goto finish; } case SPL_OP_NEXT: case SPL_OP_PREV: { struct spl_node *key_node = SPL_POP_CLEANUP(task); struct spl_node *node = SPL_POP_CLEANUP(task); struct spl_node_sub *s; if (node->hnode_name) { char *key = key_node->value ? spl_hash_encode(spl_get_string(key_node)) : 0; struct spl_node *result = (op == SPL_OP_NEXT ? spl_hnode_next : spl_hnode_prev) (task, node, key); free(key); if (result) { spl_push(task, result); goto finish; } } if (!key_node->value) { next_prev_first: s = op == SPL_OP_NEXT ? node->subs_begin : node->subs_end; spl_push(task, s ? SPL_NEW_STRING(spl_hash_decode(s->key)) : spl_get(0)); goto finish; } char *key = spl_hash_encode(spl_get_string(key_node)); s = spl_sub_lookup(node, key); if (!s) { free(key); goto next_prev_first; } s = op == SPL_OP_NEXT ? s->next : s->last; if (s) { spl_push(task, SPL_NEW_STRING(spl_hash_decode(s->key))); free(key); goto finish; } spl_push(task, spl_get(0)); free(key); goto finish; } case SPL_OP_HGETA: { struct spl_node *h = spl_pop(task); struct spl_node *retinfo = spl_pop(task); struct spl_node *c = spl_pop(task); struct spl_node_sub *s = c->subs_end; while (s) { spl_create(task, h, s->key, spl_get(s->node), SPL_CREATE_LOCAL); s = s->last; } c->flags |= SPL_NODE_FLAG_ARGC; spl_push(task, c); spl_push(task, retinfo); spl_put(task->vm, h); goto finish; } case SPL_OP_HSETA: { struct spl_node *h = spl_pop(task); struct spl_node *c = spl_pop(task); struct spl_node_sub *s = h->subs_end; while (s) { spl_create(task, c, s->key, spl_get(s->node), SPL_CREATE_LOCAL); s = s->last; } c->flags |= SPL_NODE_FLAG_ARGC; spl_push(task, c); spl_put(task->vm, h); goto finish; } op_call: case SPL_OP_CALL: { struct spl_node *f = SPL_POP_CLEANUP(task); if ( !f->ctx || !f->code || !(f->flags & (SPL_NODE_FLAG_FUNCTION|SPL_NODE_FLAG_METHOD)) ) { spl_report(SPL_REPORT_RUNTIME, task, "Tried to execute CALL on a variable with no function/method reference!\n"); retval = -1; goto finish; } struct spl_node *r = spl_get(0); r->ctx = task->ctx; task->ctx = spl_get(0); if (task->debug_str) spl_set_string(r, strdup(task->debug_str)); r->code = task->code; task->code = spl_code_get(f->code); r->code_ip = task->code_ip; task->code_ip = f->code_ip; task->debug_str = 0; r->flags |= SPL_NODE_FLAG_RETINFO; spl_push(task, r); task->ctx->ctx_type = SPL_CTX_FUNCTION; task->ctx->ctx = f->ctx ? spl_get(f->ctx) : 0; task->ctx->cls = f->cls ? spl_get(f->cls) : 0; goto finish; } case SPL_OP_BEGIN: { struct spl_node *c = spl_get(0); c->ctx_type = SPL_CTX_LOCAL; c->ctx = task->ctx; task->ctx=c; goto finish; } case SPL_OP_END: { spl_cleanup(task, task->ctx); task->ctx = spl_get(task->ctx->ctx); goto finish; } case SPL_OP_RETURN: { struct spl_node *results = spl_pop(task); struct spl_node *retinfo = spl_pop(task); while ( retinfo && (retinfo->flags & SPL_NODE_FLAG_RETINFO) == 0 ) { spl_put(task->vm, retinfo); retinfo = spl_pop(task); } struct spl_code *old_code = task->code; struct spl_node *old_ctx = task->ctx; if ( !retinfo || !retinfo->ctx || !retinfo->code ) { spl_report(SPL_REPORT_RUNTIME, task, "Tried to execute RETURN with no return information!\n"); retval = -1; goto finish; } task->ctx = spl_get(retinfo->ctx); spl_put(task->vm, old_ctx); task->code = spl_code_get(retinfo->code); spl_code_put(old_code); task->code_ip = retinfo->code_ip; task->debug_str = 0; spl_put(task->vm, retinfo); spl_push(task, results); goto finish; } case SPL_OP_OBJECT: { struct spl_node *b = spl_pop(task); struct spl_node *n = spl_pop(task); struct spl_node *src = 0; if (b->value) { src = spl_lookup(task, task->ctx, spl_get_string(b), 0); if ( !src ) { spl_report(SPL_REPORT_RUNTIME, task, "Parent object '%s' not found!\n", spl_get_string(b)); retval = -1; goto finish; } } if ( src && (src->flags & SPL_NODE_FLAG_CLASS) == 0 ) { spl_report(SPL_REPORT_RUNTIME, task, "Parent object '%s' isn't really an object!\n", spl_get_string(b)); retval = -1; goto finish; } struct spl_node *c = spl_get(0); if ( src ) { c->cls = spl_get(src); spl_set_spl_string(c, spl_string_new(SPL_STRING_STATIC, spl_get_spl_string(src), spl_get_spl_string(n), " | ", 0)); } else spl_set_spl_string(c, spl_string_get(spl_get_spl_string(n))); // struct spl_node *r = spl_get(0); // r->flags |= SPL_NODE_FLAG_REF; // r->ctx = c; spl_create(task, task->ctx, spl_get_string(n), c, SPL_CREATE_LOCAL); spl_put(task->vm, c->ctx); c->flags |= SPL_NODE_FLAG_CLASS; c->ctx_type = SPL_CTX_OBJECT; c->ctx = task->ctx; task->ctx = spl_get(c); spl_put(task->vm, b); spl_put(task->vm, n); goto finish; } case SPL_OP_ENDOBJ: { struct spl_node *old_ctx = task->ctx; task->ctx = spl_get(task->ctx->ctx); spl_put(task->vm, old_ctx); goto finish; } case SPL_OP_IMPORT: { struct spl_node *src = SPL_POP_CLEANUP(task); struct spl_node_sub *s = src->subs_begin; while (s) { int name_is_ok = s->key[0] != 0; for (int i=0; name_is_ok && s->key[i]; i++) { if (s->key[i] == '_') continue; if (s->key[i] >= 'a' && s->key[i] <= 'z') continue; if (s->key[i] >= 'A' && s->key[i] <= 'Z') continue; if (s->key[i] >= '0' && s->key[i] <= '9' && i > 0) continue; name_is_ok = 0; } if (name_is_ok) { struct spl_node *n; if (s->node->flags & SPL_NODE_FLAG_METHOD) n = spl_copy(task->vm, s->node, task->ctx); else n = spl_get(s->node); spl_create(task, task->ctx, s->key, n, SPL_CREATE_LOCAL|SPL_CREATE_NOSTATIC); } s = s->next; } goto finish; } case SPL_OP_LIFTCALL: { struct spl_node_stack *sp = task->stack, *lp = 0; int c = sp ? spl_get_int(sp->node)+1 : 0; while (c && sp) c--, sp = (lp=sp)->next; if (sp && lp && !c) { lp->next = sp->next; sp->next = task->stack; task->stack = sp; } goto finish; } case SPL_OP_IF: { if ( spl_get_int(SPL_POP_CLEANUP(task)) == 0 ) { if ( task->code->code[task->code_ip] < 0x60 ) task->code_ip += 5 - (task->code->code[task->code_ip] & 3); else task->code_ip++; } goto finish; } case SPL_OP_UNLESS: { if ( spl_get_int(SPL_POP_CLEANUP(task)) != 0 ) { if ( task->code->code[task->code_ip] < 0x60 ) task->code_ip += 5 - (task->code->code[task->code_ip] & 3); else task->code_ip++; } goto finish; } case SPL_OP_TRY: { struct spl_node *n = spl_pop(task); if (task->ctx) { task->ctx->flags |= SPL_NODE_FLAG_TRY; spl_create(task, task->ctx, "#try", n, SPL_CREATE_LOCAL); } else spl_put(task->vm, n); goto finish; } case SPL_OP_THROW: { throw_entry_point:; struct spl_node *e = spl_pop(task); e->flags |= SPL_NODE_FLAG_EXCEPTION; char *backtrace = spl_backtrace_string(task); retry_catch_lookup: while ( task->ctx && (task->ctx->ctx_type == SPL_CTX_FUNCTION || task->ctx->ctx_type == SPL_CTX_LOCAL) ) { struct spl_node *this_ctx = task->ctx; if ( this_ctx->flags & SPL_NODE_FLAG_TRY ) { struct spl_node_sub *s = this_ctx->subs_begin; while (s) { if ((s->node->flags & SPL_NODE_FLAG_CATCH) == 0) goto next_throw_sub; struct spl_node *el = e; while (el && el != s->node->ctx) el = el->cls ? el->cls : el->ctx; if (el != s->node->ctx) goto next_throw_sub; task->code_ip = s->node->code_ip; spl_code_put(task->code); task->code = spl_code_get(s->node->code); task->debug_str = 0; struct spl_node *bt = SPL_NEW_STRING(backtrace); spl_create(task, e, "backtrace", bt, SPL_CREATE_LOCAL); struct spl_node *t = spl_lookup(task, this_ctx, "#try", SPL_LOOKUP_NOCTX); if (t) spl_create(task, this_ctx, spl_get_string(t), e, SPL_CREATE_LOCAL); else spl_put(task->vm, e); goto finish; next_throw_sub: s = s->next; } } task->ctx = spl_get(this_ctx->ctx); spl_put(task->vm, this_ctx); } struct spl_node *retinfo = task->stack ? spl_pop(task) : 0; while ( retinfo && (retinfo->flags & SPL_NODE_FLAG_RETINFO) == 0 ) { spl_put(task->vm, retinfo); retinfo = task->stack ? spl_pop(task) : 0; } if ( !retinfo || !retinfo->ctx || !retinfo->code ) { spl_code_put(task->code); task->code_ip = 0; task->code = 0; struct spl_node *dn = spl_lookup(task, e, "description", SPL_LOOKUP_NOCTX|SPL_LOOKUP_TEST); char *ds = dn ? spl_get_string(dn) : ""; int ds_len = 10; for (int i=0; ds[i]; i++) ds_len += ds[i] == '\n' ? 10 : 1; char dsc[ds_len]; strcpy(dsc, *ds ? ">> " : ""); for (int i=0, j=3; ds[i]; i++) if (ds[i] == '\n') { if (ds[i+1] == 0) break; strcpy(dsc+j, "\n>> "); j += 4; } else { dsc[j++] = ds[i]; dsc[j] = 0; } spl_report(SPL_REPORT_RUNTIME, task, "Thrown uncaught exception: %s\n%s%s%s", spl_get_string(e), dsc, *dsc ? "\n" : "", backtrace); spl_put(task->vm, e); free(backtrace); retval = -1; goto finish; } struct spl_code *old_code = task->code; struct spl_node *old_ctx = task->ctx; task->debug_str = 0; task->ctx = spl_get(retinfo->ctx); spl_put(task->vm, old_ctx); task->code = spl_code_get(retinfo->code); spl_code_put(old_code); task->code_ip = retinfo->code_ip; spl_put(task->vm, retinfo); goto retry_catch_lookup; } case SPL_OP_NEW: { struct spl_node *src = SPL_POP_CLEANUP(task); struct spl_node *n = spl_get(0); struct spl_string *ts = spl_string_new(SPL_STRING_STATIC, 0, spl_get_spl_string(src), "[ ", 0); spl_set_spl_string(n, spl_string_new(SPL_STRING_STATIC, ts, 0, " ]", 0)); spl_string_put(ts); if ( !src || (src->flags & SPL_NODE_FLAG_CLASS) == 0 ) { spl_report(SPL_REPORT_RUNTIME, task, "Opcode NEW executed with a non-object argument!\n"); spl_put(task->vm, n); retval = -1; goto finish; } n->cls = spl_get(src); if (src->ctx) n->ctx = spl_get(src->ctx); n->ctx_type = SPL_CTX_OBJECT; struct spl_node *in = spl_lookup(task, n, "?init", SPL_LOOKUP_NOCTX); if ( in ) { spl_cleanup(task, n); spl_push(task, spl_get(in)); goto op_call; } spl_report(SPL_REPORT_RUNTIME, task, "Opcode NEW executed with an object argument without constructor!\n"); spl_put(task->vm, n); retval = -1; goto finish; } case SPL_OP_SETCTX: { struct spl_node *n = spl_pop(task); spl_put(task->vm, n->ctx); spl_put(task->vm, n->cls); n->ctx = spl_get(task->ctx); n->cls = 0; spl_push(task, n); goto finish; } case SPL_OP_GETVAL: { struct spl_node *id = spl_cleanup(task, spl_pop(task)); struct spl_node *src = spl_lookup(task, task->ctx, spl_get_string(id), 0); spl_push(task, spl_get(src)); goto finish; } case SPL_OP_COPY: { struct spl_node *n = spl_pop(task); spl_push(task, spl_get(n)); spl_push(task, n); goto finish; } case SPL_OP_DROP: { spl_put(task->vm, spl_pop(task)); goto finish; } case SPL_OP_POPI: case SPL_OP_POPIC: case SPL_OP_POPL: case SPL_OP_POPLC: case SPL_OP_POPS: case SPL_OP_POPSC: case SPL_OP_POPU: case SPL_OP_POPUC: { struct spl_node *val = spl_pop(task); struct spl_node *id = spl_pop(task); if ( op == SPL_OP_POPU || op == SPL_OP_POPUC ) { struct spl_node *val_old = val; val = spl_copy(task->vm, val_old, 0); spl_put(task->vm, val_old); } if ( op == SPL_OP_POPIC || op == SPL_OP_POPLC || op == SPL_OP_POPSC || op == SPL_OP_POPUC ) spl_push(task, spl_get(val)); if ( op == SPL_OP_POPS || op == SPL_OP_POPSC ) { struct spl_node *static_pointer = spl_get(0); static_pointer->flags |= SPL_NODE_FLAG_STATIC; static_pointer->ctx = val; val = static_pointer; } spl_create(task, task->ctx, spl_get_string(id), val, op == SPL_OP_POPL || op == SPL_OP_POPLC || op == SPL_OP_POPS || op == SPL_OP_POPSC ? SPL_CREATE_LOCAL : 0); spl_put(task->vm, id); goto finish; } case SPL_OP_PUSHAV: { struct spl_node *v = spl_pop(task); struct spl_node *c = spl_pop(task); spl_set_int(c, spl_get_int(c) + 1); c->flags |= SPL_NODE_FLAG_ARGC; spl_push(task, v); spl_push(task, c); goto finish; } case SPL_OP_CLEARA: { struct spl_node *retinfo = spl_pop(task); struct spl_node *c = spl_pop(task); while ( spl_get_int(c) > 0 ) { spl_set_int(c, spl_get_int(c) - 1); spl_put(task->vm, spl_pop(task)); } spl_put(task->vm, c); spl_push(task, retinfo); goto finish; } case SPL_OP_APUSHA: { struct spl_node *a = spl_cleanup(task, spl_pop(task)); struct spl_node *c = spl_pop(task); struct spl_node_sub *s = a ? a->subs_end : 0; while (s) { spl_set_int(c, spl_get_int(c) + 1); spl_push(task, spl_get(s->node)); s = s->last; } c->flags |= SPL_NODE_FLAG_ARGC; spl_push(task, c); goto finish; } case SPL_OP_APOPA: { const char *n = spl_get_string(spl_cleanup(task, spl_pop(task))); struct spl_node *retinfo = spl_pop(task); struct spl_node *c = spl_pop(task); struct spl_node *a = spl_get(0); while ( spl_get_int(c) > 0 ) { spl_set_int(c, spl_get_int(c) - 1); struct spl_node *v = spl_pop(task); spl_create(task, a, NULL, v, SPL_CREATE_LOCAL); } c->flags |= SPL_NODE_FLAG_ARGC; spl_push(task, c); spl_push(task, retinfo); spl_create(task, task->ctx, n, a, SPL_CREATE_LOCAL); goto finish; } case SPL_OP_HENC: { char *source = spl_get_string(spl_cleanup(task, spl_pop(task))); spl_push(task, SPL_NEW_STRING(spl_hash_encode(source))); goto finish; } case SPL_OP_HDEC: { char *source = spl_get_string(spl_cleanup(task, spl_pop(task))); spl_push(task, SPL_NEW_STRING(spl_hash_decode(source))); goto finish; } case SPL_OP_CAT: case SPL_OP_DOTCAT: { struct spl_string *n2 = spl_get_spl_string(SPL_POP_CLEANUP(task)); struct spl_string *n1 = spl_get_spl_string(SPL_POP_CLEANUP(task)); struct spl_string *result = spl_string_new(op == SPL_OP_DOTCAT ? SPL_STRING_DOTCAT : 0, n1, n2, 0, 0); spl_push(task, SPL_NEW_SPL_STRING(result)); goto finish; } case SPL_OP_APOP: case SPL_OP_ASHIFT: { struct spl_node *a = SPL_POP_CLEANUP(task); if ( !a ) { spl_push(task, spl_get(0)); goto finish; } struct spl_node_sub *s = (op == SPL_OP_APOP) ? a->subs_end : a->subs_begin; if ( !s ) { spl_push(task, spl_get(0)); goto finish; } spl_push(task, s->node); if ( s->last ) s->last->next = s->next; else a->subs_begin = s->next; if ( s->next ) s->next->last = s->last; else a->subs_end = s->last; a->subs_counter--; if ( a->subs_hash ) { int hash = spl_subs_hash(s->key, a->subs_hash_size); struct spl_node_sub **l = &a->subs_hash[hash]; while (*l) { if (*l == s) { *l = s->hash_next; break; } l = &((*l)->hash_next); } } if (s->module) free(s->module); free(s->key); free(s); goto finish; } case SPL_OP_APUSH: case SPL_OP_AUNSHIFT: { struct spl_node *v = spl_pop(task); char *aid = spl_get_string(SPL_POP_CLEANUP(task)); struct spl_node *a = spl_lookup(task, task->ctx, aid, 0); if (!a) a = spl_create(task, task->ctx, aid, spl_get(0), 0); spl_push(task, SPL_NEW_INT(a->subs_next_idx)); spl_create(task, a, NULL, v, SPL_CREATE_LOCAL | (op == SPL_OP_AUNSHIFT ? SPL_CREATE_BEGIN : 0)); goto finish; } case SPL_OP_APUSHREF: case SPL_OP_APUSHREFID: { struct spl_node *v = spl_pop(task); char *key = 0; if (op == SPL_OP_APUSHREFID) key = spl_get_string(SPL_POP_CLEANUP(task)); struct spl_node *a = spl_pop(task); spl_create(task, a, key, v, SPL_CREATE_LOCAL); spl_push(task, a); goto finish; } case SPL_OP_LENGTH: { struct spl_string *string = spl_get_spl_string(SPL_POP_CLEANUP(task)); if (!string) spl_push(task, SPL_NEW_INT(0)); else if (string->flags & SPL_STRING_UTF8) { unsigned char *str = (unsigned char *)spl_string(string); int len = 0; for (int i=0; str[i]; i++) if (str[i] <= 0x7F || str[i] >= 0xC0) len++; spl_push(task, SPL_NEW_INT(len)); } else spl_push(task, SPL_NEW_INT(string->total_len)); goto finish; } case SPL_OP_ELEMENTS: { struct spl_node *n = SPL_POP_CLEANUP(task); spl_push(task, SPL_NEW_INT(n->subs_counter)); goto finish; } case SPL_OP_LAND: { int n2 = spl_get_int(SPL_POP_CLEANUP(task)); int n1 = spl_get_int(SPL_POP_CLEANUP(task)); spl_push(task, SPL_NEW_INT(n1 && n2 ? 1 : 0)); goto finish; } case SPL_OP_LOR: { int n2 = spl_get_int(SPL_POP_CLEANUP(task)); int n1 = spl_get_int(SPL_POP_CLEANUP(task)); spl_push(task, SPL_NEW_INT(n1 || n2 ? 1 : 0)); goto finish; } case SPL_OP_LNOT: { spl_push(task, SPL_NEW_INT( ! spl_get_int(SPL_POP_CLEANUP(task)) )); goto finish; } case SPL_OP_NEG: { struct spl_node *n1 = SPL_POP_CLEANUP(task); char *new, *old = spl_get_string(n1); switch ( old[0] ) { case '-': new = strdup(old+1); break; case '+': my_asprintf(&new, "-%s", old+1); break; default: my_asprintf(&new, "-%s", old); break; } struct spl_node *n2 = SPL_NEW_STRING(new); n2->flags |= n1->flags & (SPL_NODE_FLAG_IS_INT | SPL_NODE_FLAG_IS_FLOAT); spl_push(task, n2); goto finish; } case SPL_OP_EQ ... SPL_OP_POW: { struct spl_node *n1 = 0; struct spl_node *n2 = 0; if (task->stack) { n2 = task->stack->node; if (task->stack->next) n1 = task->stack->next->node; } if (!n1) { SPL_POP_CLEANUP(task); SPL_POP_CLEANUP(task); goto finish; } int n1_type = spl_get_type(n1); int n2_type = spl_get_type(n2); if (n1_type == SPL_TYPE_OBJ || n2_type == SPL_TYPE_OBJ) { switch ( op ) { case SPL_OP_EQ: txtarg="eq"; break; case SPL_OP_NE: txtarg="ne"; break; case SPL_OP_LT: txtarg="lt"; break; case SPL_OP_GT: txtarg="gt"; break; case SPL_OP_LE: txtarg="le"; break; case SPL_OP_GE: txtarg="ge"; break; case SPL_OP_ADD: txtarg="add"; break; case SPL_OP_SUB: txtarg="sub"; break; case SPL_OP_MUL: txtarg="mul"; break; case SPL_OP_DIV: txtarg="div"; break; case SPL_OP_MOD: txtarg="mod"; break; case SPL_OP_POW: txtarg="pow"; break; default: spl_report(SPL_REPORT_RUNTIME, task, "Unimplemented opcode 0x%02x\n", op); retval = -1; goto finish; } goto op_objop; } if (n1_type == SPL_TYPE_FLOAT || n2_type == SPL_TYPE_FLOAT || op == SPL_OP_DIV) { op += SPL_OP_FEQ - SPL_OP_EQ; goto op_floatop; } op += SPL_OP_IEQ - SPL_OP_EQ; goto op_intop; } op_intop: case SPL_OP_IEQ ... SPL_OP_IPOW: { int n2 = spl_get_int(SPL_POP_CLEANUP(task)); int n1 = spl_get_int(SPL_POP_CLEANUP(task)); if ( (op == SPL_OP_IDIV || op == SPL_OP_IMOD) && !n2 ) { spl_report(SPL_REPORT_RUNTIME, task, "Integer division by zero!\n"); retval = -1; goto finish; } switch ( op ) { case SPL_OP_IEQ: spl_push(task, SPL_NEW_INT( n1 == n2 ? 1 : 0 )); break; case SPL_OP_INE: spl_push(task, SPL_NEW_INT( n1 != n2 ? 1 : 0 )); break; case SPL_OP_ILT: spl_push(task, SPL_NEW_INT( n1 < n2 ? 1 : 0 )); break; case SPL_OP_IGT: spl_push(task, SPL_NEW_INT( n1 > n2 ? 1 : 0 )); break; case SPL_OP_ILE: spl_push(task, SPL_NEW_INT( n1 <= n2 ? 1 : 0 )); break; case SPL_OP_IGE: spl_push(task, SPL_NEW_INT( n1 >= n2 ? 1 : 0 )); break; case SPL_OP_IADD: spl_push(task, SPL_NEW_INT( n1 + n2 )); break; case SPL_OP_ISUB: spl_push(task, SPL_NEW_INT( n1 - n2 )); break; case SPL_OP_IMUL: spl_push(task, SPL_NEW_INT( n1 * n2 )); break; case SPL_OP_IDIV: spl_push(task, SPL_NEW_INT( n1 / n2 )); break; case SPL_OP_IMOD: spl_push(task, SPL_NEW_INT( n1 % n2 )); break; case SPL_OP_IPOW: spl_push(task, SPL_NEW_INT( (int)pow(n1, n2) )); break; default: spl_report(SPL_REPORT_RUNTIME, task, "Unimplemented integer opcode 0x%02x\n", op); retval = -1; } goto finish; } op_floatop: case SPL_OP_FEQ ... SPL_OP_FPOW: { double n2 = spl_get_float(SPL_POP_CLEANUP(task)); double n1 = spl_get_float(SPL_POP_CLEANUP(task)); if ( (op == SPL_OP_FDIV || op == SPL_OP_FMOD) && !n2 ) { spl_report(SPL_REPORT_RUNTIME, task, "Floating-point division by zero!\n"); retval = -1; goto finish; } switch ( op ) { case SPL_OP_FEQ: spl_push(task, SPL_NEW_FLOAT( n1 == n2 ? 1 : 0 )); break; case SPL_OP_FNE: spl_push(task, SPL_NEW_FLOAT( n1 != n2 ? 1 : 0 )); break; case SPL_OP_FLT: spl_push(task, SPL_NEW_FLOAT( n1 < n2 ? 1 : 0 )); break; case SPL_OP_FGT: spl_push(task, SPL_NEW_FLOAT( n1 > n2 ? 1 : 0 )); break; case SPL_OP_FLE: spl_push(task, SPL_NEW_FLOAT( n1 <= n2 ? 1 : 0 )); break; case SPL_OP_FGE: spl_push(task, SPL_NEW_FLOAT( n1 >= n2 ? 1 : 0 )); break; case SPL_OP_FADD: spl_push(task, SPL_NEW_FLOAT( n1 + n2 )); break; case SPL_OP_FSUB: spl_push(task, SPL_NEW_FLOAT( n1 - n2 )); break; case SPL_OP_FMUL: spl_push(task, SPL_NEW_FLOAT( n1 * n2 )); break; case SPL_OP_FDIV: spl_push(task, SPL_NEW_FLOAT( n1 / n2 )); break; case SPL_OP_FMOD: spl_push(task, SPL_NEW_FLOAT( fmod(n1, n2) )); break; case SPL_OP_FPOW: spl_push(task, SPL_NEW_FLOAT( pow(n1, n2) )); break; default: spl_report(SPL_REPORT_RUNTIME, task, "Unimplemented floating-point opcode 0x%02x\n", op); retval = -1; } goto finish; } case SPL_OP_SEQ ... SPL_OP_SGE: { char *n2 = spl_get_string(SPL_POP_CLEANUP(task)); char *n1 = spl_get_string(SPL_POP_CLEANUP(task)); int r = strcmp(n1, n2); switch ( op ) { case SPL_OP_SEQ: spl_push(task, SPL_NEW_INT( r == 0 )); break; case SPL_OP_SNE: spl_push(task, SPL_NEW_INT( r != 0 )); break; case SPL_OP_SLT: spl_push(task, SPL_NEW_INT( r < 0 )); break; case SPL_OP_SGT: spl_push(task, SPL_NEW_INT( r > 0 )); break; case SPL_OP_SLE: spl_push(task, SPL_NEW_INT( r <= 0 )); break; case SPL_OP_SGE: spl_push(task, SPL_NEW_INT( r >= 0 )); break; default: spl_report(SPL_REPORT_RUNTIME, task, "Unimplemented string-compare opcode 0x%02x\n", op); retval = -1; } goto finish; } case SPL_OP_PEQ: { struct spl_node *n2 = SPL_POP_CLEANUP(task); struct spl_node *n1 = SPL_POP_CLEANUP(task); spl_push(task, SPL_NEW_INT( n1 == n2 )); goto finish; } case SPL_OP_POSTINC: case SPL_OP_POSTDEC: case SPL_OP_PREINC: case SPL_OP_PREDEC: { char *id = spl_get_string(SPL_POP_CLEANUP(task)); struct spl_node *n = spl_lookup(task, task->ctx, id, 0); struct spl_node *r = spl_get(0); int v = spl_get_int(n); switch ( op ) { case SPL_OP_POSTINC: spl_set_int(r, v+1); break; case SPL_OP_POSTDEC: spl_set_int(r, v-1); break; case SPL_OP_PREINC: spl_set_int(r, ++v); break; case SPL_OP_PREDEC: spl_set_int(r, --v); break; } spl_create(task, task->ctx, id, r, 0); spl_push(task, SPL_NEW_INT(v)); goto finish; } case SPL_OP_TOINT: { int val = spl_get_int(SPL_POP_CLEANUP(task)); spl_push(task, SPL_NEW_INT(val)); goto finish; } case SPL_OP_TOFLOAT: { double val = spl_get_float(SPL_POP_CLEANUP(task)); spl_push(task, SPL_NEW_FLOAT(val)); goto finish; } case SPL_OP_NOP: { goto finish; } case SPL_OP_DELETE: { struct spl_node *n = SPL_POP_CLEANUP(task); spl_delete(task, task->ctx, spl_get_string(n)); goto finish; } case SPL_OP_RLRET: { retval = spl_get_int(SPL_POP_CLEANUP(task)); goto finish; } case SPL_OP_CHECKP: { if ( task->stack && (task->stack->node->flags & SPL_NODE_FLAG_RETINFO) == 0 ) { spl_report(SPL_REPORT_RUNTIME, task, "VM Stack not empty on checkpoint!\n"); retval = -1; } goto finish; } case SPL_OP_LOAD: { char *name = spl_get_string(SPL_POP_CLEANUP(task)); if ( spl_module_load(task->vm, name, 0) == -1 ) retval = -1; goto finish; } case SPL_OP_XCHG: { struct spl_node *a = spl_pop(task); struct spl_node *b = spl_pop(task); spl_push(task, a); spl_push(task, b); goto finish; } case SPL_OP_EVAL: { char *program = spl_get_string(SPL_POP_CLEANUP(task)); struct spl_asm *as = spl_asm_create(); as->vm = task->vm; if ( spl_compiler(as, program, "eval", 0, 0) ) { spl_asm_destroy(as); spl_push(task, SPL_NEW_INT(-1)); goto finish; } spl_asm_add(as, SPL_OP_ZERO, 0); spl_asm_add(as, SPL_OP_RETURN, 0); struct spl_node *r = spl_get(0); if (task->debug_str) spl_set_string(r, strdup(task->debug_str)); r->ctx = spl_get(task->ctx); r->code = spl_code_get(task->code); r->code_ip = task->code_ip + arg; r->flags |= SPL_NODE_FLAG_RETINFO; spl_push(task, r); spl_task_setcode(task, spl_asm_dump(as)); spl_asm_destroy(as); goto finish; } case SPL_OP_LXCHG: { char *an = spl_get_string(SPL_POP_CLEANUP(task)); char *bn = spl_get_string(SPL_POP_CLEANUP(task)); struct spl_node *a = spl_get(spl_lookup(task, task->ctx, an, 0)); struct spl_node *b = spl_get(spl_lookup(task, task->ctx, bn, 0)); spl_create(task, task->ctx, an, b, 0); spl_create(task, task->ctx, bn, a, 0); goto finish; } case SPL_OP_DEBUG: { struct spl_node *n = SPL_POP_CLEANUP(task); char *msg = spl_get_string(n); int msg_len = strlen(msg); spl_report(SPL_REPORT_RUNTIME|SPL_REPORT_DEBUG, task, "%s%s", msg, msg_len == 0 || msg[msg_len-1] != '\n' ? "\n" : ""); goto finish; } case SPL_OP_WARNING: { struct spl_node *n = SPL_POP_CLEANUP(task); char *msg = spl_get_string(n); int msg_len = strlen(msg); spl_report(SPL_REPORT_RUNTIME|SPL_REPORT_WARNING, task, "%s%s", msg, msg_len == 0 || msg[msg_len-1] != '\n' ? "\n" : ""); goto finish; } case SPL_OP_ERROR: { struct spl_node *n = SPL_POP_CLEANUP(task); char *msg = spl_get_string(n); int msg_len = strlen(msg); spl_report(SPL_REPORT_RUNTIME, task, "%s%s", msg, msg_len == 0 || msg[msg_len-1] != '\n' ? "\n" : ""); retval = -1; goto finish; } case SPL_OP_SIG: { if (memcmp(task->code->code+orig_ip, SPL_SIGNATURE, 16)) { spl_report(SPL_REPORT_RUNTIME, task, "Bytecode has invalid signature!\n"); spl_task_setcode(task, 0); retval = -1; goto finish; } task->code_ip += 15; goto finish; } case SPL_OP_HALT: { spl_task_setcode(task, 0); goto finish; } default: break; } spl_report(SPL_REPORT_RUNTIME, task, "Opcode 0x%02x not implemented!\n", op); retval = -1; finish:; struct spl_node *ex = 0; while (task->cleanup) { struct spl_node_stack *n = task->cleanup->next; if (task->cleanup->node && (task->cleanup->node->flags & SPL_NODE_FLAG_CLEXCEPT)) { if (ex) spl_put(task->vm, ex); ex = task->cleanup->node; } else spl_put(task->vm, task->cleanup->node); spl_vm_free_node_stack_element(task->vm, task->cleanup); task->cleanup = n; } if (ex) { spl_push(task, ex); goto throw_entry_point; } if (task->goterr) { retval = -1; task->goterr = 0; } task->flags &= ~SPL_TASK_FLAG_BUSY; return retval; } spl-1.0pre6/README.API0000644000000000000000000010321011010115257012721 0ustar rootroot SPL C-API Documentation ======================= SPL has a pretty simple C interface which allows easy embedding of the SPL virtual machine and the SPL compiler in other applications and extending SPL using functions and modules written in C. A very complete example program (using almost the entire API) is "splrun.c". It is the tool which is usually used to run SPL programms on the command line. A rather short example for executing SPL code is "examples/c-api-test4.c". Beware of the other C API demo programms in "examples/": They are test cases for fiddling with SPL byte code and SPL assembler code. Usually one does not want to use SPL on this level. A nice example for writing SPL modules in C is "spl_modules/mod_termio.c". All SPL data structures and public functions are defined in the "spl.h" header file. Running SPL Scripts ------------------- Running an SPL Script is easy: The SPL compiler compiles the SPL Script to SPL Assembler commands. But this assembler commands are not genereted as text ouput, they are passed to an SPL Assembler instance. Optionally it is possible to run the SPL optimizer over the generated assembler code: char *spl_source = "debug 'Hello World!';"; struct spl_asm *as = spl_asm_create(); if (spl_compiler(as, spl_source, "spl_script", spl_malloc_file, 1)) error(); spl_asm_add(as, SPL_OP_HALT, 0); spl_optimizer(as); The first two argument to spl_compiler() are the SPL Assembler instance and the SPL Source to be compiled. The third parameter is the name of the script file (this is primarily used for error messages). The fourth parameter is a function which can be used by the compiler for loading external files. Loading external files is not allowed if this is a NULL pointer. The last parameter is a bool value which specifies if the compiler should generate debug symbols. The SPL Assembler instance can then be used to dump SPL bytecode. This bytecode is then passed to a newly generated task of an SPL Virtual Machine: struct spl_vm *vm = spl_vm_create(); struct spl_task *task = spl_task_create(vm, "main"); spl_task_setcode(task, spl_asm_dump(as)); task->code->id = strdup("spl_script"); spl_asm_destroy(as); Before the script is executed it is required to configure the SPL Virtual Machine. Usually one wants to activate the standard builtin functions and set a search path for loading additional SPL Modules: spl_builtin_register_all(vm); asprintf(&vm->path, ".:./spl_modules:%s", spl_system_modules_dir()); When it should be possible to execute SPL callbacks from C functions then one also needs to define a runloop function which should be used for that purpose. Usually the pre-defined 'spl_simple_runloop' function is used for that: vm->runloop = spl_simple_runloop; Now it is possible to execute the script by calling spl_exec() until the HALT opcode is reached. This opcode removes the code page from the current task because there is nothing left to be executed on the page: while ( task->code ) { spl_gc_maybe(vm); task = spl_schedule(task); if ( spl_exec(task) < 0 ) break; } The spl_gc_maybe() function runs the garbage collector when it is time to do so. The spl_schedule() function does the scheduling between the tasks. For a single-threaded script like in this example it is a NO-OP. Finally cleaning up all the SPL data structures is easy. Simply destroying the SPL Virtual Machine also destroys everything which is connected to it (including stuff such as unloading modules loaded by the script): spl_vm_destroy(vm); Have a look at "splrun.c" in the SPL source tree for a very complete example of an SPL runtime implementation. An application which is embedding SPL should be compiled and linked with the options printed by "spl-config --cflags", "spl-config --ldflags" and "spl-config --ldlibs". Writing SPL functions in C -------------------------- We will start with an example. In this example we create two new SPL functions named 'myadd' and 'mysub' which implement integer addition and substraction. First we need to create a C function which implements myadd() and mysub(): struct spl_node *spl_builtin_myaddsub(struct spl_task *task, void *data) { int a = spl_clib_get_int(task); int b = spl_clib_get_int(task); if (!strcmp(data, "add")) return SPL_NEW_INT(a + b); if (!strcmp(data, "sub")) return SPL_NEW_INT(a - b); return 0; } We also could have written two small C functions for the two SPL functions, but then we would have had no example for the second parameter to SPL C functions. All SPL C functions have the same prototype: They return an SPL node pointer and expect an SPL task struct as first and a void pointer as second argument. An SPL node pointer is the abstract representation of a "value" in SPL. The task struct contains all information about the currently running SPL task. Functions such as 'spl_clib_get_int(task)' can be used to pop the arguments from the VM stack. Arguments are pushed from right to left on the machine stack so the first argument must be popped first, then the second, and so on. Scalar values can be popped using the three functions: int spl_clib_get_int(struct spl_task *task); double spl_clib_get_float(struct spl_task *task); char *spl_clib_get_string(struct spl_task *task); You must not free the pointer returned by spl_clib_get_string() it is automatically freed as soon as control is passed back to the SPL virtual machine. You also must not modify the string. In addition to that the three functions int spl_clib_get_argc(struct spl_task *task); struct spl_node *spl_clib_get_hargs(struct spl_task *task); struct spl_node *spl_clib_get_node(struct spl_task *task); can be used to get the number of remaining arguments on the VM stack, get the hash with the named arguments or pop an argument as SPL node from the stack respectively. The spl_clib_get_node() function is only needed when complex (non-scalar) data structures are passed as parameters. The return values of spl_clib_get_hargs() and spl_clib_get_node() must be freed using spl_put() (see the seperate section about SPL data structures below). New SPL nodes for scalar values can be created using the helper functions struct spl_node *SPL_NEW_INT(int v); struct spl_node *SPL_NEW_FLOAT(double v); struct spl_node *SPL_NEW_STRING(char *v); struct spl_node *SPL_NEW_STRING_DUP(const char *v); struct spl_node *SPL_NEW_SPL_STRING(struct spl_string *v); The SPL_NEW_STRING() function expects the parameter to be already malloced. The SPL_NEW_STRING_DUP() function is internally using strdup() to create a seperate malloced copy of the string. SPL is internally using binary trees with reference counters for representing strings. That improves the performance of string concatenations massively. If you have already created an SPL string you can easily create a value for it using the SPL_NEW_SPL_STRING() function. It is also possible to return a NULL pointer in a SPL C function. This is automatically converted to an 'undef' value in SPL. Finally the new functions need to be registered with the virtual machine. This is done using the spl_clib_reg() function: spl_clib_reg(vm, "myadd", spl_builtin_myaddsub, "add"); spl_clib_reg(vm, "mysub", spl_builtin_myaddsub, "sub"); The first parameter is the SPL VM, the second the name of the function and the third a pointer to the C function implementing the SPL function. The last parameter (a void pointer) is simply passed as second parameter to the C function implementing the SPL function whenever it is called. This way our spl_builtin_myaddsub() function can distinguish if it has been called as myadd() or mysub(). A good place for the spl_clib_reg() calls is right after calling spl_builtin_register_all() for the virtual machine. Writing SPL Modules in C ------------------------ Extending SPL as described above is only possible when embedding SPL in own projects. But usually one is using an already existing SPL runtime environment and simply wants to add a few functions. This can be done by writing SPL modules. Here is the sourcecode of a simple example module, mod_hello.c: #include #include static struct spl_node *handler_hello(struct spl_task *task, void *data) { printf("Hello World!\n"); return 0; } void SPL_ABI(spl_mod_hello_init)(struct spl_vm *vm, struct spl_module *mod, int restore) { spl_clib_reg(vm, "hello", handler_hello, 0); } void SPL_ABI(spl_mod_hello_done)(struct spl_vm *vm, struct spl_module *mod) { return; } The SPL_ABI() is a macro which add a little prefix with the SPL ABI version to the identifier passed as argument. This ensures that a SPL runtime never loads a module which has been compiled for another SPL ABI. Every module must export the functions 'spl_mod__init' and 'spl_mod__done'. The _init function is called when a virtual machine loads the module and the _done function is called when a virtual machine which has loaded the module is going to be destroyed. The third argument to the _init function is set to 1 when the module is loaded while restoring a dumped session. When a module is e.g. creating SPL variables when loaded they get dumped and restored automatically and so do not need to be recreated when the module is loaded while restoring the session. Compiling and loading the module is straight forward: $ gcc -shared mod_hello.c -o mod_hello.so $ splrun -q 'load "hello"; hello();' Hello World! Note that the module *.so file must be named mod_.so and that the in the filename and the _init and _done functions must be identical. SPL Data Structures ------------------- All SPL values are stored in SPL nodes (struct spl_node). Basically the following data structure is associated with an spl_node: - scalar values (string, integer and floating point) - a hash with ordering information with references to other nodes - a context and a class pointer (also references to other nodes) - the context type information (function context, object, etc.) - a code pointer (reference to a code page and an address in it) - a flags field (additional type information and various other stuff) - data for hosted nodes (see seperate section below) - some internal data (e.g. for garbage collection) Most of this information is not accessed directly but using helper functions. We have seen already the helper functions for creating new SPL nodes with scalar values. The helper functions for reading the scalar value from an existing node are: int spl_get_int(struct spl_node *node); double spl_get_float(struct spl_node *node); char *spl_get_string(struct spl_node *node); struct spl_string *spl_get_spl_string(struct spl_node *node); char *spl_get_value(struct spl_node *node); int spl_get_type(struct spl_node *node); The first three functions are self explainatory. While spl_get_string() returns a single continous string (you must not free or modify this string), spl_get_spl_string() returns the spl_string structure used internally in SPL to store strings. The spl_get_value() function does the same as the spl_get_string() function but returns a NULL pointer when the value is undefined (spl_get_string() return "" in this case). The spl_get_type() is used by the dynamically typed operators to decide which typed operator should be used and returns one of SPL_TYPE_NONE, SPL_TYPE_INT, SPL_TYPE_FLOAT or SPL_TYPE_OBJ. Usually SPL_TYPE_NONE is interpreted as if SPL_TYPE_INT were returned. SPL is using a hybrid garbage collector which also performs reference counting. Thus one needs to update the reference counters whenever creating new or removing old references for an SPL node. This can be done using the following two functions: struct spl_node *spl_get(struct spl_node *node); void spl_put(struct spl_vm *vm, struct spl_node *node); The spl_get() function creates a new node when called with a NULL pointer as argument. The argument 'vm' to spl_put() is required for the garbage collector. For this and similar reasons most functions for working with SPL nodes require either a pointer to the SPL virtual machine or a pointer to the currently running SPL task as argument. SPL nodes generated with one of the SPL_NEW_*() functions described above have their reference counter already set to one. So it is wrong to make an additional call to spl_get() for these nodes unless you are creating more than one link to the new node. Each SPL node may have child nodes. Such child nodes can be created, looked up and removed using the following three functions: struct spl_node *spl_create(struct spl_task *task, struct spl_node *node, const char *key, struct spl_node *newnode, int flags); struct spl_node *spl_lookup(struct spl_task *task, struct spl_node *node, const char *key, int flags); void spl_delete(struct spl_task *task, struct spl_node *node, const char *key); The 'flags' argument to spl_create() and spl_lookup() are bitmap. The following values are supported by both functions: SPL_LOOKUP_TEST Do not trigger a runtime error when the entry looked for can not be found. This is example given used by the SPL 'declared' statement. SPL_LOOKUP_NOCTX SPL_LOOKUP_NOSTATIC These are used internally in recursive spl_lookup() calls, example given when looking something up in the class derivation path. In addition to the SPL_LOOKUP_TEST flag the following flags are supported by the spl_create() function: SPL_CREATE_LOCAL Create it directly here. Do not follow the context pointers and skip the local blocks. Do not trigger a runtime error when the variable does not exist yet. In most cases one needs to pass this flag. SPL_CREATE_BEGIN When creating a new key, create it at the begin of the key list. Without this flag new keys are added to the end of the key list. SPL_CREATE_NOSTATIC Used internally. SPL_CREATE_FUNCLOCAL Create function local unless already defined in a local command block. This is used internally for storing regular expression results. The following two functions from the "time" module are used to convert a UNIX 'tm' struct to an SPL data structure and vice versa. I think this is an excellent example for using spl_lookup() and spl_create(): static void convert_node_to_tm(struct spl_task *task, struct spl_node *node, struct tm *ttm) { memset(ttm, 0, sizeof(struct tm)); ttm->tm_sec = spl_get_int(spl_lookup(task, node, "sec", SPL_LOOKUP_TEST)); ttm->tm_min = spl_get_int(spl_lookup(task, node, "min", SPL_LOOKUP_TEST)); ttm->tm_hour = spl_get_int(spl_lookup(task, node, "hour", SPL_LOOKUP_TEST)); ttm->tm_mday = spl_get_int(spl_lookup(task, node, "mday", SPL_LOOKUP_TEST)); ttm->tm_mon = spl_get_int(spl_lookup(task, node, "mon", SPL_LOOKUP_TEST)); ttm->tm_year = spl_get_int(spl_lookup(task, node, "year", SPL_LOOKUP_TEST)); ttm->tm_wday = spl_get_int(spl_lookup(task, node, "wday", SPL_LOOKUP_TEST)); ttm->tm_yday = spl_get_int(spl_lookup(task, node, "yday", SPL_LOOKUP_TEST)); ttm->tm_isdst = spl_get_int(spl_lookup(task, node, "isdst", SPL_LOOKUP_TEST)); spl_put(task->vm, node); } static struct spl_node *convert_tm_to_node(struct spl_task *task, struct tm *ttm) { struct spl_node *result = spl_get(0); spl_create(task, result, "sec", SPL_NEW_INT(ttm->tm_sec), SPL_CREATE_LOCAL); spl_create(task, result, "min", SPL_NEW_INT(ttm->tm_min), SPL_CREATE_LOCAL); spl_create(task, result, "hour", SPL_NEW_INT(ttm->tm_hour), SPL_CREATE_LOCAL); spl_create(task, result, "mday", SPL_NEW_INT(ttm->tm_mday), SPL_CREATE_LOCAL); spl_create(task, result, "mon", SPL_NEW_INT(ttm->tm_mon), SPL_CREATE_LOCAL); spl_create(task, result, "year", SPL_NEW_INT(ttm->tm_year), SPL_CREATE_LOCAL); spl_create(task, result, "wday", SPL_NEW_INT(ttm->tm_wday), SPL_CREATE_LOCAL); spl_create(task, result, "yday", SPL_NEW_INT(ttm->tm_yday), SPL_CREATE_LOCAL); spl_create(task, result, "isdst", SPL_NEW_INT(ttm->tm_isdst), SPL_CREATE_LOCAL); return result; } The 'keys' used in spl_create() and spl_lookup() are simple strings in which the dot character can be used as seperator for recursive lookups. For example the following spl_lookup() call looks up the variable 'A', then inside of 'A' the variable 'B' and finally inside of that 'B' the variable 'C': spl_lookup(task, node, "A.B.C", 0); In addition to that there are some special keys starting with the '!' prefix (for example '!CLS' looks up the parent class of a class or object) or the '#' prefix (special variables generated by the compiler). The following two functions can be used to encode everything which is not a number, 7-bit ascii letter or underscore and decode such an encoded again: char *spl_hash_encode(const char *source); char *spl_hash_decode(const char *source); The algorithm of spl_hash_encode() is also used when hash elements are addressed using square brackets in SPL. The return value of this function is a malloced string which must be freed by the caller. SPL Strings ----------- SPL stores strings in spl_string structs. Such a struct has a reference counter and (optionally) a left child, a right child and a text value. The string represented by such an spl_string struct is the concatenation of the string represented by the left child, the text value and the string by the right child. The following function can be used to create a new string struct: struct spl_string *spl_string_new(int flags, struct spl_string *left, struct spl_string *right, char *text, struct spl_code *code); The 'flags' argument is a bitmap for which the following values are supported: SPL_STRING_STATIC Usually the 'text' argument is a malloced pointer which is automatically freed when the spl_string struct is destroyed. This flag must be set when the SPL string subsystem should not free the string. SPL_STRING_DOTCAT Automatically insert a dot character between the left child and the text value. This is used to speed up the creation of variable lookup paths (see spl_lookup() above). SPL_STRING_UTF8 Indicates that the string contains UTF8 characters. This flag is automatically maintained by the strings library and does not need to be passed to spl_string_new(). SPL_STRING_NOAUTOGET Do not increment the reference counters of the children. The 'left' and 'right' arguments do specify the left and right children and must be NULL when no left or right child exists. The 'code' argument is used when the 'text' pointer points to the data section of a code page and the reference counter of the code page should be decremented when this spl_string struct gets destoyed. This argument is usually simply set to NULL. Often the 'text' argument is dynamically created. There is a printf-like helper function for automatically allocating the text argument and filling it with the string sprintf() would generate: struct spl_string *spl_string_printf(int flags, struct spl_string *left, struct spl_string *right, const char *fmt, ...); Sometime one needs to manually increment or decrement the reference counter of an SPL string. This can be done using the following two functions: struct spl_string *spl_string_get(struct spl_string *s); void spl_string_put(struct spl_string *s); Finally there is a function for generating the flat string representation of an SPL string: char *spl_string(struct spl_string *s); This function stores the flat representation of the string as the new text value and dereferences the children. That also means that the caller must not modify or free the return value of spl_string(). Hosted SPL Nodes ---------------- Lets have a look at the following SPL/SDL (SDL is the "Simple DirectMedia Library") example program: load "sdl"; sdl_init(640, 480, fullscreen: 0); sdl_title("Sprites Demo"); var sprite_background = sdl_sprite_create(); var sprite_ball = sdl_sprite_create(); sprite_background.image = sdl_image_load("background.png"); sprite_ball.image = sdl_image_load("ball.png"); while (1) { sprite_ball.x = (sprite_ball.x + 5) % 640; sprite_ball.y = (sprite_ball.y + 1) % 480; sdl_sprite_update(); sdl_delay(25); } You can easily see that the SPL nodes pointed to by the variable names 'sprite_background' and 'sprite_ball' are somewhat special. Changing the values of the member variables 'x' and 'y' have the side effect of changing the position of the sprite on the screen. If you play with it a bit you will also find some other unusal effects. For example it is not possible to create new member variables of this structures and the "x" and "y" members seem to only accept integer values. The sdl_sprite_create() function creates a so-called "Hosted Node", an SPL node that is not a freestanding general purpose data structure but has a close connection to a set of SPL functions or an SPL module. Here is the C sourcecode of the sdl_sprite_create() function (with the parts which are specific to the SDL module and not interesting here removed): static struct spl_node *handler_sdl_sprite_create(struct spl_task *task, void *data) { struct sdl_sprite_hnode_data *hnd = calloc(1, sizeof(struct sdl_sprite_hnode_data)); /* initialize hnd */ struct spl_node *n = SPL_NEW_STRING_DUP("SDL Sprite"); n->hnode_name = strdup("sdl_sprite"); n->hnode_data = hnd; return n; } So a hosted node is an ordinary SPL node with the attribute "hnode_name" set to an identifier specifying the node type, in this case "sdl_sprite". The same naming scheme which applies to global variable and function names does also apply for hnode identifiers. The spl_node attribute "hnode_data" is a void pointer which can be freely used by the hnode implementation to store local data. The SDL module must also register a handler function for "sdl_sprite" nodes. This is done using the following function call in the modules _init function: spl_hnode_reg(vm, "sdl_sprite", handler_sdl_sprite_node, 0); The function prototype of handler_sdl_sprite_node() reads: void handler_sdl_sprite_node(struct spl_task *task, struct spl_vm *vm, struct spl_node *node, struct spl_hnode_args *args, void *data); The last argument to spl_hnode_reg() is simply passed through to the handler_sdl_sprite_node() function (the 'data' argument). The handler function is called whenever an action is performed on an "sdl_sprite" hnode. The action itself is passed using a spl_hnode_args struct and return values are also passed back using that struct: struct spl_hnode_args { int action; const char *key; int flags; struct spl_node *value; }; The action field specifies the kind of action that should be performed. The meaning of the other fields depends on the action. Following actions are defined: SPL_HNODE_ACTION_LOOKUP Lookup a member variable of the hnode. The "key" field contains the name of the member variable and the "flags" field the value of the flags argument to spl_lookup(). The value of the member should be passed back using the "value" field. SPL_HNODE_ACTION_CREATE Like SPL_HNODE_ACTION_LOOKUP but the "value" field contains the value to be set for the member. SPL_HNODE_ACTION_DELETE Deletion of a member has been requested. The "key" field contains the name of the member that should be deleted. SPL_HNODE_ACTION_NEXT SPL_HNODE_ACTION_PREV For listing the member variables. The "key" field contains the name of a member. The "value" field should be set to a spl node as returned by SPL_NEW_STRING() which contains the name of the next or preview member respectively. When the "key" field is a null pointer the first member should be returned for SPL_HNODE_ACTION_NEXT and the last member for SPL_HNODE_ACTION_PREV. SPL_HNODE_ACTION_COPY Someone tries to copy the hosted node using the ':=' operator. Create a full copy of this node and return the new hosted node in the "value" field. SPL_HNODE_ACTION_PUT This node is going to be destroyed. Free all resources which are connected to this node. SPL_HNODE_ACTION_DUMP The virtual machine state is going to be dumped. Serialize the state of this node and set the spl_node attribute 'hnode_dump' to a string representing the serialized node. Be prepared that this handler can later be called with 'hnode_data' beeing a NULL pointer. Then the node status must be restored from the string in 'hnode_dump'. Hosted nodes can be created so they look pretty similar to normal SPL data structures (as the SDL sprites described above) or very different (e.g. XML document handlers can be used like hashes with XPath queries as key values). Most hosted nodes do not implement all of the actions described above. E.g. the NEXT and PREV actions are rarely implemented. Sometimes it is too difficult or even impossible to implement dump and restore. In these cases it is also possible to not implement SPL_HNODE_ACTION_DUMP which then triggers a runtime error when one tries to dump a virtual machine with active hosted nodes without a valid 'hnode_dump' attribute. Error Handling -------------- There are two ways of error reporting in SPL: Creating VM runtime errors, warnings or debug output and throwing exceptions. In SPL scripts the runtime messages can be produced using the 'panic', 'warning' and 'debug' statements and exceptions can be thrown using the 'throw' statement. VM runtime errors, warnings and debug output can be created using the spl_report() function. Example given: spl_report(SPL_REPORT_RUNTIME, task, "This is an error!\n"); spl_report(SPL_REPORT_RUNTIME|SPL_REPORT_WARNING, task, "This is a warning message!\n"); spl_report(SPL_REPORT_RUNTIME|SPL_REPORT_DEBUG, task, "Did you know that %d + %d is %d?\n", 23, 42, 23+42); The 1st spl_report() call in the example creates a runtime error message. The printed error message will contain a stack backtrace and the virtual machine stops execution. The 2nd spl_report() call in the example creates a runtime warning message. The printed message will contain a stack backtrace. But the virtual machine will continue execution as if nothing special happened. The 3rd spl_report() call in the example creates a debug message. The printed message will not contain a stack backtrace and the virtual machine will continue execution. Note that spl_report() supports the well-known printf format strings. Exceptions can be thrown using the spl_clib_exception() function. The following small example module (mod_exdemo.c) implements the function exdemo() which can throw an exception. #include static struct spl_node *handler_exdemo(struct spl_task *task, void *data) { int argc = spl_clib_get_argc(task); if (argc != 4) spl_clib_exception(task, "ExdemoEx", "description", SPL_NEW_SPL_STRING(spl_string_printf(0, 0, 0, "You passed %d arguments to exdemo().\n" "This function must be called with 4 arguments.", argc)), NULL); return 0; } void SPL_ABI(spl_mod_exdemo_init)(struct spl_vm *vm, struct spl_module *mod, int restore) { if (!restore) spl_eval(vm, 0, strdup(mod->name), "object ExdemoEx { }"); spl_clib_reg(vm, "exdemo", handler_exdemo, 0); } SPL exceptions are objects and thus we first need to create a class for our example exceptions. This is simply done using spl_eval() in the modules _init function. It is important that this spl_eval() is not executed when a dumped vm machine state is restored. Note that the ExdemoEx class has no properties at all. This is not unusual for exception classes. The handler_exdemo() function does throw exceptions when it is not called with 4 arguments. The exceptions are thrown using the spl_clib_exception() helper function. This function is called with the current task pointer and the name of the exception class followed by a variable number of arguments. This additional arguments are pairs of a string pointer holding a property name and an SPL node pointer with the value for this property. The list is terminated with a single NULL pointer. In our example we only set the "description" property. This property is automatically included in the 'uncaught exception' error messages: $ gcc -shared mod_exdemo.c -o mod_exdemo.so $ splrun -q 'load "exdemo"; exdemo(1, 2, 3);' SPL Runtime Error: Thrown uncaught exception: [ ExdemoEx ] >> You passed 3 arguments to exdemo(). >> This function must be called with 4 arguments. in: ROOT (byte 28 in code block 'command line') The spl_clib_exception() helper is not 100% equal to the SPL 'throw' instruction. The most important difference is that it does not run the constructor for the new exception object. Callbacks from C to SPL ----------------------- Sometimes one needs to pass an SPL callback function to a C function and then call this callback function from the C code. This is tricky, but possible: #include static struct spl_node *handler_callback(struct spl_task *task, void *data) { struct spl_node *callback = spl_clib_get_node(task); struct spl_node *arg1 = spl_clib_get_node(task); struct spl_node *arg2 = spl_clib_get_node(task); struct spl_task *cb_task = spl_clib_callback_task(task->vm); struct spl_asm *as = spl_asm_create(); spl_asm_add(as, SPL_OP_PUSHC, "retval"); spl_asm_add(as, SPL_OP_ZERO, 0); spl_asm_add(as, SPL_OP_PUSHA, "arg2"); spl_asm_add(as, SPL_OP_PUSHA, "arg1"); spl_asm_add(as, SPL_OP_DCALL, "callback"); spl_asm_add(as, SPL_OP_POPL, 0); spl_asm_add(as, SPL_OP_HALT, 0); spl_task_setcode(cb_task, spl_asm_dump(as)); spl_asm_destroy(as); spl_create(cb_task, cb_task->ctx, "callback", callback, SPL_CREATE_LOCAL); spl_create(cb_task, cb_task->ctx, "arg1", arg1, SPL_CREATE_LOCAL); spl_create(cb_task, cb_task->ctx, "arg2", arg2, SPL_CREATE_LOCAL); spl_clib_callback_run(cb_task); struct spl_node *retval = spl_get(spl_lookup(cb_task, cb_task->ctx, "retval", 0)); spl_task_destroy(cb_task->vm, cb_task); return retval; } void SPL_ABI(spl_mod_callback_init)(struct spl_vm *vm, struct spl_module *mod, int restore) { spl_clib_reg(vm, "callback", handler_callback, 0); } This example module (mod_callback.c) implements a function which should be called with three arguments. The first one is a callback function and the other two are arguments which are passed thru to the callback function. The return value of the callback function is also the return value of the example function. Example given: $ gcc -shared mod_callback.c -o mod_callback.so $ splrun -q 'load "callback"; debug callback(function(a,b) { return a+b; }, 23, 42);' SPL Debug: 65 As can be seen, it is not possible to call the callback function directly. Instead one needs to create a seperate task (using spl_clib_callback_task()) and generate a little helper codepage which then calls the callback function. This codepage can then be executed using the spl_clib_callback_run() function. In the example program I have used temporary SPL variables in the local context of the task created by spl_clib_callback_task() to pass the callback, the arguments and the return value to and from the callback codepage. A task context is not very different from any other SPL node. So the variables can be easily created with spl_create() and looked up with spl_lookup(). The SPL assembler code used in the example above is pretty generic and is sufficient for the most cases where an SPL callback should be called from C code. Simply add as many SPL_OP_PUSHA records as you have arguments. The command 'splrun -AN' can be used to create SPL assembler code from SPL scripts. So that command can be used if the above example does not cover your requirements. For example the code used in the above example can be generated like this: $ splrun -AN -q 'var retval=callback(arg1, arg2);' # SPL Assembler Dump :16 PUSHC "retval" # (1 byte arg) 10 :18 ZERO :19 PUSHA "arg2" # (1 byte arg) 0 :21 PUSHA "arg1" # (1 byte arg) 5 :23 DCALL "callback" # (1 byte arg) 17 :25 POPL :26 HALT # Total size: 37 bytes (11 text, 26 data). You could also call the SPL compiler directly in the C function to generate the SPL assembler code. But the SPL compiler is rather slow and calling it for such a small code snippet is an unnecessary overhead. SPL and Multithreading ---------------------- SPL can be used in multithreaded environments using POSIX Threads. Be sure that pthread support is enabled when running GNU make and that your program links to the pthread library with -lpthread. Using the output of spl-config --ldflags and spl-config --ldlibs should be sufficient if SPL itself has been built with pthread support. Only one SPL VM is allowed per thread. Multiple threads in one SPL script is not possible. However, you can use the task module to allow for parallel activities within one SPL VM. In this case each SPL function call will be atomic and the SPL scheduler will switch between SPL tasks.This form of parallel SPL tasks will look like a single thread or process to the embedding C program or operating system. Multiple truly threaded SPL VMs can be used by creating one SPL VM per thread. In this case thread scheduling will be independent to SPL function calls. This allows for increased responsiveness of the individual SPL VMs in the case of heavy duty function calls in the SPL code. Communication between threaded SPL VMs can be established with synchronized registered C functions that perform some kind of communication, for example event handling. If the communication path can be kept simple, such a solution can be very efficient and still provide the benefits of a threaded environment. In addition, utilizing pthread conditions and (timed) condition wait calls provided by registered C functions can avoid excessive polling of the communication path. See the man page for pthread_cond_timedwait(3) for details. spl-1.0pre6/treedump.c0000644000000000000000000002576611064463357013465 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * treedump.c: Dump the SPL VM state as human-readable tree */ #include #include #include "spl.h" #include "compat.h" static void print_node(FILE *file, struct spl_node *node, const char *linkid, int flags) { if ( linkid ) fprintf(file, "%s", linkid); else fprintf(file, "%s", node->path); if ( flags & SPL_TREEDUMP_FLAG_ADDR ) fprintf(file, " [%p]", node); fprintf(file, "\n"); if (node->value && (flags & SPL_TREEDUMP_FLAG_VALUE) ) { char *value = spl_hash_encode(spl_get_string(node)); fprintf(file, "\tV: %s\n", *value == '?' ? value+1 : value); free(value); } if ( node->hnode_name && (flags & SPL_TREEDUMP_FLAG_HNODE) ) fprintf(file, "\tH: %s\n", node->hnode_name); if ( node->flags && (flags & SPL_TREEDUMP_FLAG_FLAGS) ) { fprintf(file, "\tF:"); #define X(n) if ( node->flags & SPL_NODE_FLAG_ ## n ) fprintf(file, " " #n); X(FUNCTION) X(METHOD) X(RETINFO) X(STATIC) X(CLASS) X(CLNULL) X(CLEXCEPT) X(EXCEPTION) X(TRY) X(CATCH) X(RERES) X(ARGC) X(IS_INT) X(IS_FLOAT) X(GC) #undef X fprintf(file, "\n"); } if (node->ctx && (flags & SPL_TREEDUMP_FLAG_CTX) ) fprintf(file, "\tC: %s\n", node->ctx->path); if (node->cls && (flags & SPL_TREEDUMP_FLAG_CLS) ) fprintf(file, "\tO: %s\n", node->cls->path); if (node->ctx_type && (flags & SPL_TREEDUMP_FLAG_CTXTYPE) ) fprintf(file, "\tT: %s\n", node->ctx_type == SPL_CTX_FUNCTION ? "FUNCTION" : node->ctx_type == SPL_CTX_OBJECT ? "OBJECT" : node->ctx_type == SPL_CTX_LOCAL ? "LOCAL" : node->ctx_type == SPL_CTX_ROOT ? "ROOT" : "???"); if ( linkid && (flags & SPL_TREEDUMP_FLAG_LINK) ) fprintf(file, "\tL: %s\n", node->path); } static void dump_node(int pass, FILE *file, struct spl_node *node, struct spl_node *parent, char *path, int recurs_level, int flags, const char *prefix) { if (recurs_level > 1024) { spl_report(SPL_REPORT_RUNTIME, 0, "Trying to treedump cyclic object tree!\n"); return; } if (pass == 0) { if (node->dump_tag == 1) return; node->dump_tag = 1; if (node->path) free(node->path); node->path = 0; node->path_metric = 0; node->path_parent = 0; if (node->ctx) dump_node(pass, file, node->ctx, node, path, recurs_level+1, flags, prefix); if (node->cls) dump_node(pass, file, node->cls, node, path, recurs_level+1, flags, prefix); } else node->dump_tag = 0; if (pass == 1) { int this_metric = 2; for (char *p = path; (p = strstr(p, "*CLS*")); p++) this_metric+=10; for (char *p = path; (p = strstr(p, "..")); p++) this_metric++; if (prefix && *prefix) do { int path_len = strlen(path); int prefix_len = strlen(prefix); if (path_len < prefix_len) { char tmp[path_len+2]; snprintf(tmp, path_len+2, "%s.", path); if (strncmp(prefix, tmp, path_len+1)) break; } else if (path_len > prefix_len) { char tmp[prefix_len+2]; snprintf(tmp, prefix_len+2, "%s.", prefix); if (strncmp(path, tmp, prefix_len+1)) break; } else if (strcmp(path, prefix)) break; for (const char *p = prefix; (p = strstr(p, "..")); p++) this_metric--; if (this_metric < 1) this_metric = 1; } while (0); if (!node->path_metric || node->path_metric > this_metric) { if (node->path) free(node->path); node->path = strdup(path); node->path_metric = this_metric; node->path_parent = parent; } else return; if (node->ctx) { int id_len = strlen(path)+2; char id[id_len]; snprintf(id, id_len, "%s.", path); dump_node(pass, file, node->ctx, node, id, recurs_level+1, flags, prefix); } if (node->cls) { int id_len = strlen(path)+7; char id[id_len]; snprintf(id, id_len, "%s.*CLS*", path); dump_node(pass, file, node->cls, node, id, recurs_level+1, flags, prefix); } } if (pass == 2) { print_node(file, node, 0, flags); if (node->ctx && node->ctx->path_parent == node) dump_node(pass, file, node->ctx, node, path, recurs_level+1, flags, prefix); if (node->cls && node->cls->path_parent == node) dump_node(pass, file, node->cls, node, path, recurs_level+1, flags, prefix); } char *last_mod = 0; struct spl_node_sub *s = node->subs_begin; while (s) { if (s->module && !(flags & SPL_TREEDUMP_FLAG_MODS)) goto skip_this_sub; if (pass == 1) { int id_len = strlen(path)+strlen(s->key)+2; if (s->module) id_len += strlen(s->module)+9; char id[id_len]; if (s->module) { snprintf(id, id_len, "%s.(MOD).(%s).%s", path, s->module, s->key); } else snprintf(id, id_len, "%s.%s", path, s->key); dump_node(pass, file, s->node, node, id, recurs_level+1, flags, prefix); } else if (pass != 2 || s->node->path_parent == node) { if (pass == 2 && s->module && (!last_mod || strcmp(last_mod, s->module))) { if (!last_mod) fprintf(file, "%s.(MOD)\n", node->path); fprintf(file, "%s.(MOD).(%s)\n", node->path, s->module); last_mod = s->module; } dump_node(pass, file, s->node, node, path, recurs_level+1, flags, prefix); } else if (pass == 2) { int id_len = strlen(node->path)+strlen(s->key)+2; if (s->module) id_len += strlen(s->module)+9; char id[id_len]; if (s->module) { if (!last_mod || strcmp(last_mod, s->module)) { fprintf(file, "%s.(MOD).(%s)\n", path, s->module); last_mod = s->module; } snprintf(id, id_len, "%s.(MOD).(%s).%s", node->path, s->module, s->key); } else snprintf(id, id_len, "%s.%s", node->path, s->key); print_node(file, s->node, id, flags); } skip_this_sub: s = s->next; } } void spl_treedump(struct spl_vm *vm, FILE *file, int flags, const char *prefix) { struct spl_task *t; dump_node(0, 0, vm->root, 0, 0, 2, flags, prefix); t = flags & SPL_TREEDUMP_FLAG_TASKS ? vm->task_list : 0; while (t) { struct spl_node_stack *s = t->stack; while (s) { dump_node(0, 0, s->node, 0, 0, 2, flags, prefix); s = s->next; } t = t->next; } dump_node(1, 0, vm->root, 0, "ROOT", 2, flags, prefix); t = flags & SPL_TREEDUMP_FLAG_TASKS ? vm->task_list : 0; while (t) { int id_len = (t->id ? strlen(t->id) : 10) + 32; char id[id_len]; if (t->ctx) { snprintf(id, id_len, "TASK(%s).CTX", t->id); dump_node(1, 0, t->ctx, 0, id, 2, flags, prefix); } struct spl_node_stack *s = t->stack; for (int i=0; s; i++) { snprintf(id, id_len, "TASK(%s).STACK(%d)", t->id, i); dump_node(1, 0, s->node, 0, id, 2, flags, prefix); s = s->next; } t = t->next; } dump_node(2, file, vm->root, 0, 0, 2, flags, prefix); t = flags & SPL_TREEDUMP_FLAG_TASKS ? vm->task_list : 0; while (t) { int id_len = (t->id ? strlen(t->id) : 10) + 32; char id[id_len]; if ( flags & SPL_TREEDUMP_FLAG_ADDR ) fprintf(file, "TASK(%s) <%p>\n", t->id, t); else fprintf(file, "TASK(%s)\n", t->id); if (t->ctx) { snprintf(id, id_len, "TASK(%s).CTX", t->id); if ( !strcmp(id, t->ctx->path) ) dump_node(2, file, t->ctx, 0, 0, 2, flags, prefix); else print_node(file, t->ctx, id, flags); } struct spl_node_stack *s = t->stack; for (int i=0; s; i++) { snprintf(id, id_len, "TASK(%s).STACK(%d)", t->id, i); if ( !strcmp(id, s->node->path) ) dump_node(2, file, s->node, 0, 0, 2, flags, prefix); else print_node(file, s->node, id, flags); s = s->next; } t = t->next; } } static char *bt_getfuncname(struct spl_node *ctx, struct spl_task *task) { while (ctx) { if (ctx->ctx_type == SPL_CTX_FUNCTION) { struct spl_node *f = spl_lookup(task, ctx, "?#func", SPL_LOOKUP_NOCTX); if (f) return spl_get_string(f); break; } if (ctx->ctx_type == SPL_CTX_ROOT) return "ROOT"; if (ctx->ctx_type != SPL_CTX_LOCAL) break; ctx = ctx->ctx; } return "????"; } static void bt_layout_info(char *debug, struct spl_code *code, int code_ip, char **info_p) { if (*info_p) free(*info_p); if (!debug || !*debug) { my_asprintf(info_p, " (byte %d in code block '%s')", code_ip, code->id ? code->id : ""); return; } int lineno=0, charno=0; char *srcfile = debug; lineno = atoi(srcfile); while ( *srcfile && *srcfile != ':' ) srcfile++; if ( *srcfile == ':' ) srcfile++; charno = atoi(srcfile); while ( *srcfile && *srcfile != ':' ) srcfile++; if ( *srcfile == ':' ) srcfile++; my_asprintf(info_p, " (near line %d, char %d in '%s')", lineno, charno, srcfile); } void spl_backtrace(struct spl_task *task, FILE *file) { char *info = 0; if (!task || !task->code) return; if ( (task->id && *task->id && strcmp(task->id, "main")) || (task->module && *task->module) ) fprintf(file, "While executing task '%s'%s%s%s:\n", task->id ? task->id : "", task->module ? " (" : "", task->module ? task->module : "", task->module ? ")" : ""); bt_layout_info(task->debug_str, task->code, task->code_ip, &info); fprintf(file, "in: %s%s\n", bt_getfuncname(task->ctx, task), info); struct spl_node_stack *retinfo = task->stack; while (retinfo) { if (retinfo->node->flags & SPL_NODE_FLAG_RETINFO) { bt_layout_info(spl_get_string(retinfo->node), retinfo->node->code, retinfo->node->code_ip, &info); fprintf(file, "called by: %s%s\n", bt_getfuncname(retinfo->node->ctx, task), info); } retinfo = retinfo->next; } free(info); } char *spl_treedump_string(struct spl_vm *vm, int flags, const char *prefix) { #if !defined USEWIN32API && !defined USECYGWINAPI && !defined USEMACOSXAPI && !defined USEBSDAPI && !defined USEIRIXAPI char *bp; size_t size; FILE *stream; stream = open_memstream(&bp, &size); spl_treedump(vm, stream, flags, prefix); fclose(stream); return bp; #else FILE *tempfile = tmpfile(); spl_treedump(vm, tempfile, flags, prefix); size_t size = ftell(tempfile); char *bp = malloc(size + 1); rewind(tempfile); fread(bp, size, 1, tempfile); bp[size] = 0; fclose(tempfile); return bp; #endif } char *spl_backtrace_string(struct spl_task *task) { #if !defined USEWIN32API && !defined USECYGWINAPI && !defined USEMACOSXAPI && !defined USEBSDAPI && !defined USEIRIXAPI char *bp; size_t size; FILE *stream; stream = open_memstream(&bp, &size); spl_backtrace(task, stream); fclose(stream); return bp; #else FILE *tempfile = tmpfile(); spl_backtrace(task, tempfile); size_t size = ftell(tempfile); char *bp = malloc(size + 1); rewind(tempfile); fread(bp, size, 1, tempfile); bp[size] = 0; fclose(tempfile); return bp; #endif } spl-1.0pre6/utf8.c0000644000000000000000000001014311064463357012505 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * utf8.c: Simple utf8 conversion and check functions */ #include #include #include #include "spl.h" #include "utf8tab.h" typedef unsigned char charset_map_table_type[4]; static struct { charset_map_table_type *table; char *name; } charset_map[] = { { UTF8TAB_ISO8859_1, "ascii" }, { UTF8TAB_ISO8859_1, "latin_1" }, { UTF8TAB_ISO8859_1, "iso8859_1" }, { 0, 0 } }; const char *spl_utf8_check(const char *text) { const unsigned char *utext = (const unsigned char *)text; int inc = 0; for (const unsigned char *t = utext; *t; t += inc + 1) { #define IN(off, mask, value) ((t[inc=off] & (mask)) == (value)) if (IN(0, 0x80, 0x00)) continue; if (IN(0, 0xE0, 0xC0) && IN(1, 0xC0, 0x80)) continue; if (IN(0, 0xF0, 0xE0) && IN(1, 0xC0, 0x80) && IN(2, 0xC0, 0x80)) continue; if (IN(0, 0xF8, 0xF0) && IN(1, 0xC0, 0x80) && IN(2, 0xC0, 0x80) && IN(3, 0xC0, 0x80)) continue; return (char*)t; #undef IN } return 0; } char *spl_utf8_import(const char *text, const char *charset) { const unsigned char *utext = (const unsigned char *)text; charset_map_table_type *tab = 0; if (!strcmp("utf_8", charset)) return strdup(text); for (int i=0; charset_map[i].table; i++) if (!strcmp(charset_map[i].name, charset)) tab = charset_map[i].table; if (!tab) return 0; int result_len = 0; for (const unsigned char *t = utext; *t; t++) if ((*t & 0x80) == 0) result_len++; else result_len += strlen((char*)(tab[*t-128])); char *result = malloc(result_len+1); char *r = result; for (const unsigned char *t = utext; *t; t++) if ((*t & 0x80) == 0) *(r++) = *t; else { strcpy(r, (const char*)tab[*t-128]); r += strlen((const char*)tab[*t-128]); } assert(r == result+result_len); result[result_len] = 0; return result; } char *spl_utf8_export(const char *text, const char *charset) { const unsigned char *utext = (const unsigned char *)text; charset_map_table_type *tab = 0; if (!strcmp("utf_8", charset)) return strdup(text); for (int i=0; charset_map[i].table; i++) if (!strcmp(charset_map[i].name, charset)) tab = charset_map[i].table; if (!tab) return 0; int result_len = 0; char *result = malloc(strlen(text)+1); int inc = 0; for (const unsigned char *t = utext; *t; t += inc + 1) { #define IN(off, mask, value) ((t[inc=off] & (mask)) == (value)) if (IN(0, 0x80, 0x00)) { result[result_len++] = t[0]; continue; } for (int i=0; i<128; i++) { inc = strlen((const char*)tab[i]); if (!strncmp((const char*)t, (const char*)tab[i], inc)) { result[result_len++] = 128+i; // in fact a strlen of 2 would result in t increase of 3 // decrement inc to get correct t increment in for loop // raphael, 2007-10-08 inc--; goto next_export_char; } } result[result_len++] = '?'; if (IN(0, 0xE0, 0xC0) && IN(1, 0xC0, 0x80)) continue; if (IN(0, 0xF0, 0xE0) && IN(1, 0xC0, 0x80) && IN(2, 0xC0, 0x80)) continue; if (IN(0, 0xF8, 0xF0) && IN(1, 0xC0, 0x80) && IN(2, 0xC0, 0x80) && IN(3, 0xC0, 0x80)) continue; /* Input is not UTF-8. This should not happen.. */ for (inc=1; t[inc] & 0x80; t++) result[result_len++] = '?'; next_export_char:; #undef IN } result[result_len++] = 0; return realloc(result, result_len); } spl-1.0pre6/compat.h0000644000000000000000000001122411064460235013100 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * compat.h: Some defines and inlines for non-gnu systems * */ #ifndef SPL_COMPAT_H #define SPL_COMPAT_H #include #define UNUSED __attribute__((unused)) #if !defined __GLIBC__ || !defined __GLIBC_MINOR__ || defined _FORCE_COMPAT_FUNCS #define my_strndupa(s, n) \ ({ \ char *__new = (char *) __builtin_alloca ((n) + 1); \ strncpy(__new, (s), (n)); \ __new[(n)] = '\0'; __new; \ }) #define my_strndup(s, n) \ ({ \ char *__new = (char *) malloc ((n) + 1); \ strncpy(__new, (s), (n)); \ __new[(n)] = '\0'; __new; \ }) #define my_alloca(_s) __builtin_alloca(_s) static inline void *my_memrchr(const void *s, int c, size_t n) { const char *sx = s; for (int counter = n; counter >= 0; counter--) if (sx[counter] == c) return (void*)(sx+counter); return 0; } static inline void *my_memmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) { if (needlelen <= haystacklen) { for (unsigned int i=0; i <= haystacklen-needlelen; i++) if (!memcmp(haystack+i, needle, needlelen)) return (void*)(haystack+i); } return 0; } static inline char *my_strsep(char **s, const char *ct) { char *begin = *s, *end = *s; if (!begin) return 0; while (*end && !strchr(ct, *end)) end++; if (*end) { *end = 0; *s = end+1; } else *s = 0; return begin; } #else # define my_strndupa strndupa # define my_strndup strndup # define my_alloca alloca # define my_memrchr memrchr # define my_memmem memmem # define my_strsep strsep #endif #if defined USEWIN32API #ifndef COMPAT_H_NO_WIN_INCL # include # include # include #endif # define MY_O_BINARY O_BINARY # define my_sleep(sec) Sleep((sec)*1000) #else # define MY_O_BINARY 0 # define my_sleep sleep #endif #if !defined __GLIBC__ || !defined __GLIBC_MINOR__ || defined _FORCE_COMPAT_FUNCS /* The Win32 and IRIX snprintf functions returns -1 if the target string is to * small. So we need this crappy hack to allocate a buffer of the right size.. */ # define my_asprintf(__result, fmt, ...) \ ({ \ int __size = 64, __rc; \ *(__result) = malloc(__size + 1); \ while (1) { \ __rc = snprintf(*(__result), __size + 1, \ (fmt), ## __VA_ARGS__); \ if (__rc >= 0 && __rc < __size) break; \ __size *= 2; \ *(__result) = realloc(*(__result), __size + 1); \ } \ __rc; \ }) # define my_vasprintf(__result, fmt, ap) \ ({ \ int __size = 64, __rc; \ *(__result) = malloc(__size + 1); \ while (1) { \ __rc = vsnprintf(*(__result), __size + 1, \ (fmt), (ap)); \ if (__rc >= 0 && __rc < __size) break; \ __size *= 2; \ *(__result) = realloc(*(__result), __size + 1); \ } \ __rc; \ }) #else # define my_asprintf asprintf # define my_vasprintf vasprintf #endif #if !defined __GLIBC__ || !defined __GLIBC_MINOR__ || defined _FORCE_COMPAT_FUNCS #include static inline int my_dprintf(int fd, const char *fmt, ...) \ { \ char *data; \ va_list ap; \ va_start(ap, fmt); \ const int rc = my_vasprintf(&data, fmt, ap); \ va_end(ap); \ for (int written = 0, rc2; written < rc; written += rc2) { \ rc2 = write(fd, data+written, rc-written); \ if (rc2 <= 0) break; \ } \ free(data); \ return rc; \ } static inline ssize_t my_sendfile(int out_fd, int in_fd, off_t *offset, size_t count) { char buffer[count]; const int read_rc = read(in_fd, buffer, count); if (read_rc <= 0) return read_rc; int write_rc = 0; while (write_rc < read_rc) { int rc = write(out_fd, buffer+write_rc, read_rc-write_rc); if (rc <= 0) return rc ? rc : write_rc; write_rc += rc; } return write_rc; } #else # define my_dprintf dprintf # define my_sendfile sendfile #endif #endif spl-1.0pre6/README.LANG0000644000000000000000000015674111010115257013052 0ustar rootroot SPL Language Reference Manual ============================= The SPL sources include many example programs. E.g.: example/example*.spl hanoi.spl SPL scripts can be executed with "splrun ". WebSPL scripts (*.webspl) can be executed using "webspl.cgi" or "webspld" (see README file or the 1st chapter in the big SPL manual). Basics ------ SPL is a C-like language (such as Java or PHP). Constructs such as "if" statements and "while" loops work as in those other languages. I am not explaining the basic language constructs derived from C in this manual. Please have a look at http://en.wikipedia.org/wiki/C_syntax if you have no idea of how the basic syntax of C (and other C-like languages) looks like. SPL is a dynamically typed language. So don't wonder how you can define a variable as "int", "float" or "array of objects". Just use the variables as "int", "float" or "array of objects" and they will be. However - the operators can have types: + - * / % ** Dynamically typed addition, subtraction, multiplication, division, modulo and power operators. #+ #- #* #/ #% #** Integer addition, subtraction, multiplication, division, modulo and power operators. .+ .- .* ./ .% .** Floating point addition, subtraction, multiplication, division, modulo and power operators. == != <= < > >= Dynamically typed comparison operators #== #!= #<= #< #> #>= Integer comparison .== .!= .<= .< .> .>= Floating point comparison ~== ~!= ~<= ~< ~> ~>= String comparison ! || && Logical NOT, OR and AND (the keywords 'not', 'or' and 'and' are also available and do exactly the same thing) ~ String concatenation .( ... ) Cast the expression in the parentheses to a floating point value. #( ... ) Cast the expression in the parentheses to an integer value. The dynamically typed operators are dynamically mapped to the integer, floating point or object operators (the object operators are described in a seperate section below). But they are never mapped to string operators. So if you want to e.g. compare two strings you must always use the '~==' operator. SPL also has support for the usual combined operation-assign operators such as '+=' (or '~=' for appending to a string). Also the famous '++' and '--' operators (pre- and postfix) do exist in SPL. The operator '<=>' can be used to switch the values of two variables. Have a look at the 'hanoi.spl' program in this directory. It is a nice example for a simple SPL program. Generating Debug Output ----------------------- The keyword 'debug' may be used to create debug output. The debug message itself is simply passed as argument. Since this is a language keyword (and not a function), no parentheses are required for the argument: debug "Hello World!"; The way the debug messages are diplayed depends on the host application embedding the SPL runtime. They may be printed to the console, displayed in popup windows, written to logfiles or simply be ignored. The keyword 'warning' works the same way as 'debug', but also includes a stack backtrace in the messege and the keyword 'panic' also terminates the program execution. Variables --------- As said already, SPL is entirely typeless. There is just the abstract construct of variables. A variable can represent everything, a simple scalar value, an array or hash, a function or an object. Variables are declared using the "var" keyword: var foobar; Values can be assigned using the '=' operator: foobar = 42; You can also use the variable as array or hash by appending '[ .. ]' to the variable name (as you might already be used to from other languages): foorbar[42] = 23; But more about that in the section about arrays and hashes. The keyword 'declared' can be used to check if a variable has been declared. The keyword 'defined' can be used to check if a declared variable has a value assigned. The keyword 'undef' represents an undefined value and can e.g. be used to remove the value of a variable: var x; if (declared x) debug "Variable x has been declared."; if (defined x) debug "This is never reached."; x = 42; if (defined x) debug "Now x is declared and defined."; x = undef; if (not defined x) debug "Now x is not defined again."; The keyword 'lengthof' can be used to get the length (in characters) of the srtring representation of a variable: debug lengthof "Hello World"; Using an undeclared variable (outside a 'declared' test) causes a runtime error. Functions --------- Functions can be declared with the "function" keyword, such as: function hypot(x, y) { return ( x.**2 .+ y.**2 ) .** (1./2); } And may be called by simply using the function name and passing the arguments in parentheses: var h = hypot(5, 12); Note that the parentheses are required when calling functions in SPL. However, there are some language keywords with function-like behavior. But they don't have parentheses around their arguments because they are not functions but compiler keywords: debug, warning, panic, delete, import, load, new, return, defined, declared, pop, shift, push, unshift, throw Note that functions can be copied as any other variable. So it is e.g. possible to simply pass a callback function as parameter to another function without any syntactical black magic. It is also possible to define anonymous functions. In this case the function definition evaluates to the function pointer and can e.g. be assigned to a variable. The syntax is the same as for normal function declarations, but the function name is skipped: var hypot = function(x, y) { return ( x.**2 .+ y.**2 ) .** (1./2); }; This is especially useful when functions need to be passed as arguments to other functions: array_sort_by_values(myarray, function(a,b) { return a < b; }); The keyword declared can be used to check if a function is actually available: if (declared hypot) hypot(3,4); All functions that are built into the SPL language are C functions which are usually registered at startup. Such functions are also called builtins. To see if a registered C function is available, declared_builtin can be used: if (declared_builtin("write")) debug "the function write is available"; Using the keyword declared for C functions does not work because declared only checks for SPL variables. Likewise, declared_builtin does not work for SPL functions or variables. SPL can easily be extended with additional C functions. For this purpose, modules can be created and loaded in SPL. It is also possible to use the SPL virtual machine in a C program where C functions can be registered to the VM (virtual machine). For details on modules, see the section on Loadable Modules. To learn how to use the SPL VM in C programs, see the C API Documentation in README.API or at the end of the manual. Array Operations ---------------- In SPL, there is no difference between arrays, hashes and other complex data structures like objects. A variable simply may have child variables. The children of a variable may be accessed using the dot-operator or brackets: foo.bar = 42; foo["bar"] = 42; the only difference between the two methods is that the name of the child variables is limited to the lexical rules for identifiers (the regular expression /[a-zA-Z_][a-zA-Z0-9_]*/) and that child variables must be explicitly defined with the 'var' keyword when accessed using the dot operator. It is possible to iterate over all children of a variable with the 'foreach' statement: foreach index (array) debug array[index]; where the 'index' variable is automatically defined by the foreach statement and only valid for the loop body. A foreach loop always returns the elements in the same order in which they have been created (except for elements added using the 'unshift' statement). The foreach loop usually iterates over the indexes of an array. It is also possible to iterate over the values using the foreach[] loop. So this is identical to the previous example: foreach[] value (array) debug value; There are four special variables which only exist within foreach loops: $[ .. is set to '1' when this is the first element and to '0' otherwise $] .. is set to '1' when this is the last element and to '0' otherwise $# .. the current index (not the value pointed to by the index) $@ .. the array which is processed by the foreach loop Arrays may be defined in-place using square brackets. There are two methods of doing so: With automatically assigned and with explicitly declared keys. When declaring the keys, the '=>' operator can be used when the key is specified as value and the ':' operator when the key is specified like a variable name. It is also possible to mix those two methods: var a1 = [ "a", "b", "c" ]; var a2 = [ "x" => "a", "y" => "b", z: "c" ]; var a3 = [ x: "a", 100 => "b", "c" ]; has the same effect as: var a1, a2, a3; a1[0] = "a"; a1[1] = "b"; a1[2] = "c"; a2["x"] = "a"; a2["y"] = "b"; a2["z"] = "c"; a3["x"] = "a"; a3[100] = "b"; a3[101] = "c"; So, when adding elements to an array without explicitly defining a key, the highest numeric key defined in the array will be incremented by one to build the key for the new element. There are two other instructions which can be used to add elements to an array: push array, 23; unshift array, 42; The push instruction adds an element to the end of the array, unshift to the beginning. The difference between these instructions is in the position the new element will have in the array, not in the key which is assigned to it. The key is the next integer value in both cases. There are also instructions for removing the first and last entry from an array: var last = pop array; var first = shift array; But it is also possible to simply remove elements by using the key: delete array[42]; Besides the 'foreach' loop, it is also possible to manually 'walk through' an array using the 'next' and 'prev' instructions: var a = [ 3 => 'x', 5 => 'y', 7 => 'z' ]; var three = next a, undef; var five = prev a, 7; var seven = next a, 5; They return the next or previous index value in the array passed as 1st parameter, relative to the index passed as 2nd parameter. If the 2nd parameter is undef, 'next' returns the first and 'prev' the last index. If there is no next or previous element, undef is returned. The keyword 'elementsof' can be used to get the number of elements in an array: debug elementsof [ 1, 2, 3, 4, 5, 6 ]; Additional functions for working with arrays (such as sorting) are provided by the "array" module which is described in SPLDOC (there are separate sections about SPL Modules and SPLDOC later in this document). Vaargs like arguments --------------------- SPL also supports 'vaargs' like argument passing. The last argument in a function declaration may be prefixed with an '@' character. In that case the variable representing this argument will be an array of all remaining arguments. If an '@' character is put in front of a function argument when calling a function, that argument is interpreted as an array and an argument for each array element will be inserted. E.g.: function foo(@args) { foreach i (args) debug args[i]; } function bar(@args) { foo("one", @args, "four"); } bar("two", "three"); This is useful for writing wrapper functions and functions with a variable number of arguments. Named Function Arguments ------------------------ SPL also supports named arguments (aka options). With this arguments, not the position in the function call but the assigned name identifies an argument. All named parameters are assigned to a hash which is declared with a '%' prefix in the function prototype. The '%' prefix may also be used when calling a function to pass all elements of a hash as named arguments. E.g.: function foo(%args) { foreach i (args) debug "$i -> ${args[i]}"; } function bar(%args) { foo(one: 1, %args, four: 4); } bar(two: 2, three: 3); This is e.g. useful for function arguments which are rarely used. When a named option is specified multiple times, the most left specification is used. That way it is possible to pass a hash with default values as last parameter using the '%' prefix. Objects ------- If you have no idea what object oriented programming is, read http://en.wikipedia.org/wiki/Object_Oriented_Programming first. SPL does not really know about the difference between classes and objects. If only one instance of a class/object is needed, it is possible to simply use the name defined with the 'object' keyword. (But if you do so, no constructor will be executed.) Because of this, we don't speak about classes and objects in SPL, but of objects and instances of objects. Objects can be defined in SPL like this: object Foo { var counter; method increment_counter() { return counter++; { method init(start_value) { counter = start_value; return this; } } The method 'init' has a special meaning: It is the constructor. There are no destructors in SPL, but it is common to create a method named 'destroy' for the job and call it manually when the object isn't needed anymore. If there is no init, a default init method is provided that returns this. If a custom init method is used special care must be given to return this at the end as is shown in the example above. If this is not returned, creating a new object with the new operator will return undef. The constructor must return a self pointer. The keyword 'this' can be used to create such a pointer. Thus the "return this;" in the example above. If the new object is derived from another one, the parent object is specified right after the object name in the object definition: object Bar Foo { method decrement_counter() { return counter--; } } Objects may be instantiated using the 'new' operator. If any arguments are specified when instantiating an object, they are passed through to the object constructor as they are: var mycounter = new Foo(42); SPL does support nested functions as well as nested objects. So it is possible to define 'local objects' in your functions. It is also possible to derive one object from more than one parent. This can be done using the 'import' statement: object Foobar Foo { import Bar; [...] } But the parent objects which are 'imported' neither show up in the object derivation path of the object, nor in its reflection string. When an object or one of its instances is used as scalar variable, they evaluate to their reflection strings. E.g.: object A { } object B A { } var x = new B(); debug A; // prints: SPL Debug: A debug B; // prints: SPL Debug: A | B debug x; // prints: SPL Debug: [ A | B ] Objects and instances are automatically created as references. So they can be passed as arguments and being returned from functions without implicitly being copied by those operations. If a method is overloaded in a child object and the implementation from the parent object needs to be called in the child object, this can be done using the context dispatching operator '*': object A { method foo() { debug "Now in foo from A."; } } object B A { method foo() { debug "Now in foo from B."; *A.foo(); } } Without the '*', A.foo() would be called in the context of A and not in the context of B (or the currently active instance of B). The keyword 'static' can be used to declare static object variables. A static object variable is shared between all objects in the derivation path and all instances of these objects. The declaration works exactly the same way as for declaring normal variables in objects, just that the keyword 'static' is used instead of the keyword 'var'. There is nothing like 'private functions' in SPL. All methods (and variables) are public. If some are intended for internal use only, they must be protected by choosing a name which avoids collisions. There is a separate section on the recommended naming conventions later in this document. Object Operators ---------------- There is no operator overloading support in SPL. Instead, there are object operators (as they are seperate integer and floating point operators too). Whenever such an object operator is used, an "operator_*" method is called in the left operand and both operands are passed as parameters. The method name is different for each object operator: (+) and (+)= are calling the method operator_add(a, b) (-) and (-)= are calling the method operator_sub(a, b) (*) and (*)= are calling the method operator_mul(a, b) (/) and (/)= are calling the method operator_div(a, b) (%) and (%)= are calling the method operator_mod(a, b) (**) and (**)= are calling the method operator_pow(a, b) (<) is calling the method operator_lt(a, b) (>) is calling the method operator_gt(a, b) (<=) is calling the method operator_le(a, b) (>=) is calling the method operator_ge(a, b) (==) is calling the method operator_eq(a, b) (!=) is calling the method operator_ne(a, b) The operator precedence is the same as for the normal integer and floating point operators. The generic dynamically typed operators (+, -, *, etc.) are automatically mapped to the object operators when used with objects. Exceptions ---------- SPL has an exception handling mechanism for error-reporting and -handling. The exceptions themselves are objects. In fact, every object could be used as an exception. But it is recommended to only use objects as exceptions which have been designed for this purpose. Usually the names of such exception objects end with the "Ex" postfix. E.g. all exceptions thrown by the SQL modules are of the type "SqlEx". Exceptions may be thrown using the keyword 'throw' and are catched in 'try' blocks. Here is a small example: object MyEx { } object MySpecialEx MyEx { } try (x) { throw new MySpecialEx(); debug "** this line is never reached **"; catch MyEx: debug "Got a 'MyEx' exception. The backtrace is:\n" ~ x.backtrace; } If no 'catch' rule matches the exception object or any of its parent objects, a runtime error is printed and the execution of the program is terminated. The 'throw' instruction automatically adds the 'backtrace' variable to the exception object. This variable contains a human-readable backtrace of the current task. If the exception object has a 'description' variable, its text value will be printed as part of the runtime error for uncatched exceptions. It is considered good coding style to only use exceptions for error handling. Strings and Here-Documents -------------------------- Strings can be quoted using double (") or single (') quotes. The only difference between the two quoting styles is that within double quotes it is possible to use single quotes unescaped and vice versa. Strings can be concatenated using the ~ operator. But a list of string constants is concatenated automatically. So this three code snippets are identical: debug "hello world"; debug "hello" ~ " " ~ "world"; debug "hello" " " "world"; The usual backslash escape sequences (such as '\n' for newlines) can be used in both quoting styles. Additionally, there are some special substitution sequences which are all starting with a dollar sign ($). These sequences are described in the next section. The section after it describes so-called templates, an even more advanced way of handling strings in SPL programs. Another way of quoting strings in SPL are so-called Here-Documents. They work a little bit differently in SPL compared to other languages such as Perl. An example: debug <>' instead of '<<' is used, the Here-Document is literal and no substitutions are performed. The '<>Token') may be followed by any special character, which is then used as so-called indenting character. All characters from the beginning of a line in the Here-Document until the first occurrence of that indenting character are ignored: debug <>>' does the same, but without processing any special substitutions. Special Substitutions in Strings -------------------------------- SPL has support for some special substitution operators in strings and templates (the latter are described in the next section). All these substitution operations start with the dollar sign ($): $variable Will be substituted by the current value of the variable. ${expression} Will be substituted by the value of the expression. This can be everything - including a really complex program code snippet. $(function) This is an embedded function. It will be executed and the return value substituted. E.g.: $( if (should_insert_foo()) return "Foo"; return "Bar"; ) $ Insert results from a regular expression (by name or numeric ID). The brackets can be left away for single digit IDs, $- and $+. $[ comment ] This is a comment. It is always substituted with an empty string. $$, $:, $? Just a "$", ":" or "?" respectively. (for escaping $.., and are terminated by . It is possible to use an empty string as token too, so <> ... also works fine. Within templates, all the $ substitutions are supported. In addition to them, there are also some special XML-like tags allowed in templates: ... A comment. Everything between the tags will be ignored. ... The SPL expression passed using the "code" attribute will be evaluated and the content of the tag will only be embedded if the result is true. ... This tag must directly follow a tag. Its content will only be embedded if the content of the tag has not been embedded. ... The content of this tag will be evaluated as embedded function. The return value (if any) is substituted. (like "$( ... )") ... The same as above, but the SPL program code is passed using the "code" attribute. The content of the tag is available through a variable with the name "_data_" when executing the code. This is e.g. useful if you want to pass a part of your document through an encoding function: This is xml encoded: < & > The encoding operator (::) is described later. ... The tag is evaluated like an embedded function. So e.g. variables declared in an tag is local to that tag. The tag is evaluated in the same context as the template text itself. So it can be used to e.g. declare functions or variables used in the rest of the template. Do not use the 'return' keyword in an tag unless you really want to return from the the current function context. A tag is always substituted with an empty string. ... This tag is always substituted with an empty string, but the variable named in the "var" attribute will be declared and set to the content of the tag. ... As above, but the value will again be passed through the specified SPL expression and is available as "_data_" in the expression. ... Builds a foreach instruction using the variable name passed in the "var" attribute as iterator and the list passed in the "list" attribute as list to iterate over. If the behavior of a foreach[] loop is wanted, the variable name in the "var" attribute must be prefixed with "[]". ... Localize the tag body (see "Internationalization and Localization" below). Localization domains can be passed by using the syntax .... Note that no other special tags are allowed inside of this tag. ... The tag body is parsed using the specified indenting character. Here is a small example of what can be done using SPL Templates. It selects messages from a database and creates HTML code for showing them in java script popup windows. SELECT message, timestamp FROM message_table WHERE user_id = ${sql::userid} AND urgent = 1 SPL also has support for PHP-like code snippets in templates using '' tags. The main difference to the and tags is that it is possible to let SPL code blocks and template blocks overlap with this syntax. E.g.: First Line word #$i is '$word' Last Line The and tags would require the '{' .. '}' block to be closed in the same code block in which it has been opened. Templates can also be started with <:Token>, in which case the ':' is used as indenting character. This does not change the termination string for the token (it is still ). Other indenting characters than ':' are not supported for inline templates (except they are introduced using the tag). As with the substitutions, you should consider wisely how to use SPL Templates in your programs because it is also possible to use them to produce obfuscated code. The and Template Tags ------------------------------------------- In addition to the template tags described above there is also support for and template tags. The tags are transformed to calls to splcall_*() functions. The tag body is transformed to a function which evaluates it and this function is passed as argument to the splcall_*() function. The return value of the splcall_*() is substituted. Example given: function splcall_toupper(textfunc) { var text = textfunc(); text =~ e/[a-z]/g chr(ord($0)+ord('A')-ord('a')); return text; } debug <>This is very important!; The tag attributes are passed as named parameters to the splcall_*() functions. When the attributes are quoted with ' or " they are interpreted as strings and when they are quoted with ( .. ) they are interpreted as SPL expressions. It is also possible to ommit the tag body by terminating the tag with '/>'. Example given: function splcall_getuser(%args) { return sql_value(db, "SELECT username " "FROM user WHERE id = $args.id"); } debug <>User $userid: ; The tags work exactly like the tags, but instead of passing the tag body as argument and substituting the return value the tag body is only evaluated and included in the template output when the return value of the splif_*() function returns true. It is not possible to ommit the tag body of tag. The tag can also be used with an tag. Example given: function splif_adminuser(%args) { return sql_value(db, "SELECT isadmin " "FROM user WHERE id = $args.id"); } debug <:> : Data: $data_string : Secret Data: $secret_data_string : Secret Data: ** admins only ** ; The and tags don't really add any functionality which are not already provided by the tags. But they can be used to simplify writing SPL template files a lot, especially if the person writing the template files is not a programmer. Loadable Modules ---------------- There are two different types of loadable modules: SPL byte code modules and machine byte code modules. Machine byte code modules are *.so files (or *.dll on Win32 host). The README.API file (and the last chapter in the manual) describes how to write such modules in C. SPL byte code modules are written in SPL and compiled using the "splrun" command line tool. Both types of modules are loaded using the 'load' instruction. E.g.: load "sql"; When executing such an instruction, the SPL virtual machine is first looking for a machine byte code module with such a name in the module search path and then for SPL byte code modules. The module files are named "mod_.splb" (or "mod_.so" or "mod_.dll" respectively). Each module can only be loaded once. Additional 'load' instructions for an already loaded module are ignored. A module is written like any other SPL program, but should only declare variables, functions and objects and should not execute any real code (or at least limit that to some simple initializations). SPL source files can be compiled to byte code files like this: ./splrun -N -e -x mod_foobar.splb mod_foobar.spl The -e is optional and instructs the compiler to include debug symbols in the byte code file. Running 'splrun' without any parameters prints out the full list of available command line options. SPLDOC Comments --------------- Modules should have SPLDOC Comments in their source, to make it possible to create API references for them directly from the source code with the 'spldoc.spl' tool. Running 'make spldoc' in the SPL source dir creates the documentation to all modules which are included in the SPL distribution. The generated API references are written to the "spldoc/" directory. They can also be found on the SPL web page. SPLDOC comments look like this: /** * This is an SPLDOC comment for the foobar function */ function foobar(); So they start with "/**" on a line on its own. Then comes the description text. If the first character in the line is an asterisk (*), it will be ignored. The SPLDOC comment ends with '*/' and right in the next line (a blank line is not allowed here) comes the commented function prototype (or variable declaration, etc). The first SPLDOC comment in a module source file is special: it describes the module itself (so the first line after the comment has no special meaning in this case). In order to be parse-able by SPLDOC, a source file must be "well formatted". That means, the functions, variables, etc. which are not an object member must be declared (and commented) first, then the objects. It is important that not only the object members, but also the object itself is commented. Otherwise SPLDOC wouldn't 'see' the object and the documentation wouldn't match the module. In an object, you should always define static variables first, then dynamic variables, then functions and then methods. Everything which is not an object member must be defined before the first object and should be defined in the same order (first variables, then functions). Have a look at "spl_modules/mod_wsf.spl" and "spldoc/wsf.html" (which is created by 'make spldoc') for a good example. Naming Conventions ------------------ SPL has a flat root name space. So it is important to have some naming conventions to avoid collisions in this flat name space. The module names themselves are using a kind of hierarchy. E.g. there is a module called "wsf" and a module "wsf_dialog" based on it. In the module "wsf" all object names start with the prefix "Wsf" and all functions and global variables start with "wsf_". In the "wsf_dialog" module, the object prefix is "WsfDialog" and the function/variable prefix is "wsf_dialog_". A few more words about object names. Each part of the object which start a new 'logical block' (and not each word!) should start with a capital letter. E.g.: object Myobject { } object MyobjectFoobar Myobject { } or: object MyobjectBase { } object MyobjectFoobar MyobjectBase { } But _not_: object MyObject { } object MyObjectFooBar MyObject { } It's up to you to decide what a "logical block" is in your case. There are some additional naming conventions for exception objects: They all end with "Ex", but if e.g. exceptions are derived from each other (and some text is appended to the object name), it is appended before the "Ex" postfix. Private variables and functions of modules (which are not documented using SPLDOC and are for internal use in the module only) must begin with "__". Private methods of an object (which are not documented and should not be called from methods in derived objects) must begin with "__". Regular Expressions ------------------- If you have no idea what regular expressions are, read http://en.wikipedia.org/wiki/Regular_expressions first. SPL is using the PCRE library for regular expression matching. So it is pretty compatible with Perl regular expression. If the PCRE library cannot be found by the SPL GNUmakefile, SPL is compiled without regular expression support and a runtime error is produced whenever regular expression instructions are executed. The syntax for regular expression matching is similar to the Perl syntax: x =~ /foobar/; x =~ s/foo/bar/g; Perl-like modifiers supported by SPL: i .. ignore case in pattern matching s .. dot metacharacters also match the newline charater x .. ignore unescaped white spaces and allow comments using '#' m .. multi line matching, ^ and $ also match newline characters g .. match (and substitute) globally, not only the first match Modifiers new in SPL: N .. include captured strings as child nodes in result, using numbers P .. include named captured strings (?P...) in result, using names A .. add an array with an element per match (with 'g' modifier) R .. when substituting, return new text and keep original unmodified I .. declare named captured strings (?P...) as local variables E .. store the text between the matches in $- (before) and $+ (after) L .. the $-variables have the data from the last match (with 'g' mod.) S .. return the text fragments between the matches (split mode) The return value of '=~' is the number of matches found (except when the 'R' modifier is used). If the 'g' modifier isn't used, the return value may only be 0 or 1. With the modifiers 'N', 'P' and 'A', the result will also have child variables with additional data about the matches. When the 'A' modifier is used without 'N' or 'P' the result is an array with the entire matches. With the 'N' or 'P' modifier the result is an array with the captures as child variables. So splitting up an input file in lines can be done like this (the file_read() function is provided by the "file" module): var lines = file_read("demo.txt") =~ /[^\n]*/Ag; It is possible to declare names for capturing parentheses using the python syntax (?P...). This is much of a help when dealing with complex regular expression with many capturing parentheses. Referring to the strings matched by a regular expression can be done by using $N, $ and $ (in addition to including them in the result value using the 'N', 'P' and 'A' modifiers). The special variable $0 represents the whole matched text and is also available if no capturing parentheses were present in the regular expression. These special variables are declared locally - they do not invalidate regular expression results in any higher context. So it is save to e.g. do a regular expression, then call a function which is also using regular expression, and after that refer to the matches of the first regular expression using these special variables. The special modifier 'E' stores the text which has not matched in $- (before the match) and $+ (after the match). In combination with 'N' or 'I' there will also be ["-"] and ["+"] elements in the returned object. When the 'E' modifier is used together with 'g', $- and $+ contains the text snippets between the matches. A very special variable is the variable $$. It is the local variable in which the regular expression results are stored. So instead of writing $0 you could write $$[0], or $$.foobar instead of $. This variable is function local, but it is possible to write 'var $$;' in a local block to create a seperate set of regex result variables. It is also possible to backup the value of $$ in another variable and restore it later on. Here is a nice example for using complex regular expressions: var x = "foolish bigfoot"; var r = x =~ /(?P(?P\S)\S*)\s*/ALPg; foreach i (r) { var $$; r[i].word =~ s/foo(.*)/bar$1/; debug "Match #$i: [${r[i].firstchar}] ${r[i].word} ($0)"; } var text1 = "Ever seen a ${r[0].word} $1?"; var text2 = text1 =~ s/seen/beeing eaten by/R; debug text1; debug text2; This script creates the following output: SPL Debug: Match #0: [f] barlish (foolish) SPL Debug: Match #1: [b] bigbart (foot) SPL Debug: Ever seen a barlish bigfoot? SPL Debug: Ever beeing eaten by a barlish bigfoot? It is possible to use the import statement with $$. That has almost the same effect as using the 'I' modifier, but gives you a better control on where the variables for the regular expression matches are declared: var text = "Hello World"; if (text =~ /(?P\S+)\s+(?P\S+)/) { import $$; debug "$foo $bar"; } if (declared foo) panic "This is never reached"; if (text =~ /(?P\S+)\s+(?P\S+)/I) { debug "$foo $bar"; } if (declared foo) debug "Foo is now defined here too."; A full description of the regular expression syntax supported by PCRE (and SPL) can be found in the "pcrepattern" man page. Instead of the slash (/) as quoting character for regular expressions, it is also possible to use colons (:), commas (,), exclamation marks (!), percentage sign (%) and the at-character (@). It is also possible to use regular expression substitutions in which the substitution text is re-evaluated for every match. But the syntax for this is slightly different as in perl: var text = "Some ASCII Codes: A = #A, B = #B, C = #C, D = #D"; debug text =~ e/#(.)/Rg ord($1); For complex to-be-evaluated expressions it is required to put the expression in parentheses. Note that not all of the modifiers listed above are allowed for and that the $-variables are not set by e// expressions. References ---------- Handling references (aka pointers) is easy in SPL. It is done more or less automatically by the virtual machine: Simple variables (such as scalars or functions) are always passed by value and complex variables (arrays, objects and everything else with child variables) are passed by reference. At least that's what it looks like. In fact the SPL virtual machine implements a complex copy-on-write behavior, but this is hidden in the machine internals. The basic idea behind copy-on-write systems is discussed at http://en.wikipedia.org/wiki/Copy_on_write Sometimes it is neccessary to create recursive copies of complex data structures. This can be done by assigning the variables using the ':=' operator (instead of using the normal '=' operator). It is possible to test if two variable names point to the same object using the '^==' operator. Testing for not-equal is performed with the '^!=' operator. The Quoting/Encoding Operator (::) ---------------------------------- There is a special operator in SPL for quoting and encoding text (::). In fact it is nothing else than a simple function call: foo::bar is identical to encode_foo(bar) but in some cases easier to write and read. There are various modules which provide encode_* functions. E.g. the "sql" module provides an "encode_sql()". Embedded Functions ------------------ Embedded functions are functions which are simply "inlined" in an expression using the special parentheses "({" ... "})". E.g. this code fragment: var x = 42; function foobar() { if (x == 42) return 23; if (x == 23) return 42; return 0; } debug foobar() + 23; does the same thing as this code fragment: var x = 42; debug ({ if (x == 42) return 23; if (x == 23) return 42; return 0; }) + 23; The mechanism used here is the same as the one used for the $( ... ) substitution described above. Gotos, break and continue ------------------------- SPL has support for gotos. The syntax is pretty much the same as in C: var i; for (i=0; i<42; i++) { if (i == 23) goto break_out; jump_back:; } if (i > 42) return; break_out: write("demo2: Now i is $i.\n"); goto jump_back; A goto label must always point to an instruction (as in ANSI C). That's why there is a ';' after the "jump_back" label: The ';' adds an empty instruction. It is not possible to use gotos to jump from one function in another (or from a function to the main program). Also note that statements such as 'var' are compiled to real virtual machine statements and if a goto is used to jump over a 'var' statement, the variable won't be declared and any use of that variable would result in a runtime error. So you should only use gotos to do stuff such as jumping out of loops. SPL also has support for the 'break' and 'continue' statements. They are internally implemented as gotos. Switch Statements ----------------- The switch statements are different in SPL compared to languages such as C. An SPL switch statement actually is nothing else then a series of "if .. else if" statements with a different syntax: var list = [ 1, 2, 3, 4 ]; while (1) switch { var x = shift list; case x == 1: debug "x is 1"; case x == 2: debug "x is 2"; case x == 3: debug "x is 3"; default: debug "whatever!"; exit; } The code before the first 'case' statement is always executed. This code block can be used to declare variables which are only used in the switch block. Each case block comes with a condition which defines if that block should be executed. The first case block with a true condition will be executed. Note that the SPL runtime does not optimize switch statements (e.g. using lookup tables). So a huge list of cases should better be implemented using a hash with function references. Compiler Pragmas and Preprocessor Statements -------------------------------------------- There are some compiler pragmas and preprocessor statements for the SPL compiler. First of all there are three statements for including external files at compile time. The compiler must be able (allowed) to read files when the pragmas for processing files are executed. This is for example not the case for the small code snippets which are compiled by the various eval implementations provided by some modules. The file include statements are: #file-as-const Filename #file-as-code Filename #file-as-template Filename #file-as-bytecode Filename All four statements include an external file. The first includes a file as string constant with no additional processing. The 2nd just continues compilation in the specified file (and returns when the end-of-file is reached). The third includes the file as template. This is pretty similar to #file-as-const, but $-substitutions and tags are evaluated (see "Templates" above). The fourth includes (aka. 'links in') an already precompiled SPL bytecode file. If the filename is prefixed with an asterisk character (*), the file is interpreted as so-called embedded-file. Embedded files must be declared in the same SPL program file as they are referred to and are similar to the Perl __DATA__ construct. Embedded files are declared (a little bit like Here-Documents) using: #embedded-file Filename Token .... Token The declaration can be anywhere in the file, but usually it is done after the actual program code. A very different kind of compiler pragma is '#encoding'. SPL usually expects all input in UTF-8. But if your files are not UTF-8 encoded, the '#encoding' pragma can be used to specify the encoding: #encoding iso8859_1 At the moment only the encodings "ascii", "iso8859_1" and "latin_1" (these are three names for the same character set) and "utf_8" are known to the SPL compiler. SPL also has statements for defining and deleting macros: #define pi 3.14159265 #define mysqrt(x) ((x)*(x)) debug sqrt(pi); #undef pi #undef mysqrt The macro value is terminated by the end of the line. Multi-line values are also possible by beginning all additional lines with a backslash character. Hexadecimal, Octal and Binary Numbers ------------------------------------- Hexadecimal, octal and binary numbers can be used in SPL with the prefixes '0x', '0o' and '0b'. So the following 4 lines of code are equal: debug 255; debug 0xff; debug 0o377; debug 0b11111111; The C-like prefix '0' for oktal numbers does not work here. Numbers with leading zeros are interpreted as decimal numbers in SPL. The 'eval' statement -------------------- The 'eval' statement can be used to to execute dynamically created SPL code. Example given: eval "debug 'Hello World';"; An 'eval' returns -1 on compiler errors and zero otherwise. It is strongly recommended to check the return code of an eval statement. Hosted Variables ---------------- Hosted variables (hnodes) are variables which are managed by SPL modules. Usually they are handlers such as open database connections. The module documentations describe the behavior of the hosted variables provided by the modules. Such hosted variables usually behave more or less like they are normal SPL variables. But some of them are very different. Some operators - especially those used for array and object operations - may show a very differnt bahvior. So read the module documentations carefully and don't wonder if e.g. such a variable looks like an assoziative array but the foreach loop doesn't work with it. Internationalization and Localization ------------------------------------- The SPL builtins library provides some bindings for the standard gettext setlocale(), bindtextdomain(), textdomain(), gettext() and dgettext() functions. In addition to that there also exists a special localization operator (the underscore) which can be used as prefix for string constants in SPL: debug _"Hello World!"; debug _<:> : The underscore can also be used as prefix for inline templates. : But that disables the support for the tags. ; The special thing about that operator is that the dollar substitutions are handled in a different way in such strings. E.g. the statement debug _"The sum of $a and $b is ${a+b}."; Is automatically transformed by the SPL compiler to a call to a special translation function: debug _("The sum of {0} and {1} is {2}.", undef, a, b, a+b); (The 2nd parameter is the text domain to be used. Passing undef means that the text domain set using textdomain() should be used.) That way it is possible to even translate messages that contain substitutions by creating a .po file containing something like: msgid "The sum of {0} and {1} is {2}." msgstr "Die summe von {0} und {1} ist {2}." When a different text domain than the one set by the last call to textdomain() should be used, the prefix '_DOMAINNAME_' can be used instead of a simple '_'. For example when the above message should be translated using the 'foobar' domain, the following code could be used: debug _foobar_"The sum of $a and $b is ${a+b}."; A dummy C file with all the translatable strings in a SPL source file can be generated using 'splrun -NX'. This dummy C file can then be used as input for the xgettext program for creating or updating .po files: splrun -NX demo.spl | xgettext -C - A more detailed description of the generic gettext API and the tools can be found in the 'gettext' info page. Walking through Contexts ------------------------ First of all: This section is more meant as information about what is possible than a recommendation to actually use the methods described here. SPL variables (aka SPL "nodes") may have child nodes. Those child nodes have unique names and a defined order. The arrays described above are in fact just nodes with such child nodes. When addressing nodes in that tree, i.e. when creating a path in that node tree, a dot is used to tell the virtual machine which part of the path specifies the parent, which the child, grandchild, etc. If parts of the path need to be created dynamically, the [ .. ] operator is used. E.g.: foo.bar[1234] will instruct the SPL virtual machine to look up the variable "foo", then look up its child "bar" and then its child "1234". But where to look for "foo" in the first place? Each task has a so-called context node. That is the node in which all the local variables of the current command block are stored (as children). If the variable can't be found in the current context, it will be looked for in the context node of that context node, and so on. That means, whenever a new command block is opened (e.g. with '{'), a new context node is created and the old task context node becomes the context node of the new task context node. The '}' destroys the new context node and the old context node becomes the new task context node again. (The foreach and for loops create a local context even if the loop body is not a '{' ... '}' block.) All this is done automatically by SPL and the resulting behavior is exactly as one would expect it from a C-like programming language. But here comes the interesting part: If a variable name starts with '[*]', it means that one context is skipped in the look up path. So it is possible to address the upper contexts directly. It is for example possible to declare global variables from a function context: function create_foobar(value) { var [*].foobar = value; } create_foobar(42); debug foobar; It is even possible to write to the context pointer directly and so change the context pointer. E.g.: object A { var foobar = "I am 'foobar' from object A."; } object B { method print_foobar() { if (declared foobar) debug foobar; else debug "Variable 'foobar' not found."; } } B.print_foobar(); B.[*] = A; B.print_foobar(); The '[*]' operator skips over local command blocks and only stops at function (or other non-local) contexts. One possible use case of all this is inserting a lookup context in splcall_*() functions. That way it is possible to implement loops with tags which declare their own local variables. But other that with the context dispatching operator (the '*' prefix for function calls) the body can still access the local variables from the context in which the body has been declared. Example given: function splcall_myloop(textfunc, %args) { var mytextfunc, myctx, text; mytextfunc := textfunc; myctx.[*] = mytextfunc.[*]; mytextfunc.[*] = myctx; for (var i = args.from; i<=args.to; i++) { myctx["counter"] = i; text ~= mytextfunc(); } return text; } write(<:> : $i: Current counter: $counter ); In addition the to '[*]' operator there is also the '[+]' operator for following the class pointer (pointing to the parent of an object), the '[/]' operator always points to the root node and the '[.]' operator always points to the current object (aka 'this' pointer). Command Blocks without local context ------------------------------------ Command blocks declared with '{' ... '}' have their own local context. Thus, variables or functions declared in that block are only local to this context. It is also possible to declare command blocks without a local context using the '{[' ... ']}' brackets. Example given: var msg = ""; function x() { msg ~= "World"; } /* append "Hello " */ { function x() { msg ~= "Hello "; } x(); } /* append "World" */ x(); /* append "!" */ {[ function x() { msg ~= "!"; } ]} x(); debug msg; This program prints "Hello World!". It wouldn't if '{' ... '}' would not have a local context or '{[' ... ']}' would have one. Inline Assembly --------------- This is another thing which is more of academic interest. Since the SPL compiler creates byte code for the SPL virtual machine, it is possible to create code for that machine more directly too. One way of doing that is by using SPL assembler code. The compiler allows inlining assembler code. The keyword "asm" expects a list of assembler statements. Each statement in an extra string constant (no substitutions, etc are allowed in these strings). There is no delimiter for these string constants. E.g.: asm 'pushc "Hello World"' 'debug'; There isn't anything which can be done by using the assembler but can't be done by the high-level language. So the inline assembler is more important for academic purposes or obscure optimizations. The command "../splrun -AN" can be used to dump the assembler code generated by the SPL compiler. Just in case you are interested. SPL Performance --------------- Variable values in SPL are always stored in so-called SPL Nodes ('struct spl_node' in spl.h). Such an SPL node is a heavy data structure, aprox. 100 bytes large. It has fields for a wide range of value types and some additional fields for internal purposes (example given garbage collection). The size, complexity and propabilities of the spl_node struct is the reason why SPL is very good for handling big and complex data very fast on the one hand but small data with small low-level-operations very slow on the other hand. Example given: Most scripts do a lot of string concatenations. SPL is internally storing all strings as binary trees so all the malloc-copy-free cycles found in other scripting languages are not required and string concatenation is a pretty fast operation in SPL. But small operations such as incrementing an integer are extreamly slow in SPL. Two extreme examples: Damn slow in SPL compared to other scripting languages: for (var i=0; i < 100000; i++) { /* do nothing */ } Damn fast in SPL compared to other scripting languages: var text = "foobar"; for (var i=0; i < 1000; i++) text = "$text$text"; The 1st example does nothing else than incrementing a counter up to 100000. This is pretty slow in SPL because the increment operator creates a new value (i.e. a new spl_node struct) and frees the old value for every increment operation. Other scripting languages would simply overwrite the integer pointed to by the variable 'i'. This is not possible in SPL because 'i' points to an spl_node and this node is strictly read only because other variables may also point to it and so changing it would also change these other variables. The 2nd example would try to allocate about 6^1000 bytes of memory (about 1.416e+769 Gigabytes) in other languages. But SPL never tries to actually allocate the memory because in this example program the string is only used in other string concatenations. Both examples are extreme and not representative for real world applications. In general one should try to do the "big logic" in SPL but implement the inner loops of performance critical algorithms somewhere else (example given in SPL functions written in C). The last chapter of the SPL manual (i.e. the file README.API) describes how to use the SPL C-API, example given for writing SPL modules in C. Command Line Debugger --------------------- The command line debugger is still under construction. spl-1.0pre6/sha1.c0000644000000000000000000001773511064463357012471 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * This is based on the Public Domain SHA-1 implementation by Steve Reid. * * sha1.c: An implementation of the SHA-1 algorithm. * */ #include #include // MacOS X has __BYTE_ORDER defined but no endian.h. // Win32 (MINGW) and BSD systems also show the behavior. #if !defined USEMACOSXAPI && !defined USEWIN32API && !defined USEBSDAPI && !defined USEIRIXAPI # include #endif #include "spl.h" typedef struct { unsigned long state[5]; unsigned long count[2]; unsigned char buffer[64]; } SHA1_CTX; static void SHA1Transform(unsigned long state[5], unsigned char buffer[64]); static void SHA1Init(SHA1_CTX* context); static void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int len); static void SHA1Final(unsigned char digest[20], SHA1_CTX* context); #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) /* blk0() and blk() perform the initial expand. */ /* I got the idea of expanding during the round function from SSLeay */ #if __BYTE_ORDER == __LITTLE_ENDIAN #define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ |(rol(block->l[i],8)&0x00FF00FF)) #elif __BYTE_ORDER == __BIG_ENDIAN #define blk0(i) block->l[i] #else # error __BYTE_ORDER is not defined (should be __LITTLE_ENDIAN or __BIG_ENDIAN) #endif #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ ^block->l[(i+2)&15]^block->l[i&15],1)) /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); /* Hash a single 512-bit block. This is the core of the algorithm. */ static void SHA1Transform(unsigned long state[5], unsigned char buffer[64]) { unsigned long a, b, c, d, e; typedef union { unsigned char c[64]; unsigned long l[16]; } CHAR64LONG16; CHAR64LONG16* block; static unsigned char workspace[64]; block = (CHAR64LONG16*)workspace; memcpy(block, buffer, 64); /* Copy context->state[] to working vars */ a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; /* 4 rounds of 20 operations each. Loop unrolled. */ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); /* Add the working vars back into context.state[] */ state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; /* Wipe variables */ a = b = c = d = e = 0; } /* SHA1Init - Initialize new context */ static void SHA1Init(SHA1_CTX* context) { /* SHA1 initialization constants */ context->state[0] = 0x67452301; context->state[1] = 0xEFCDAB89; context->state[2] = 0x98BADCFE; context->state[3] = 0x10325476; context->state[4] = 0xC3D2E1F0; context->count[0] = context->count[1] = 0; } /* Run your data through this. */ static void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int len) { unsigned int i, j; j = (context->count[0] >> 3) & 63; if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++; context->count[1] += (len >> 29); if ((j + len) > 63) { memcpy(&context->buffer[j], data, (i = 64-j)); SHA1Transform(context->state, context->buffer); for ( ; i + 63 < len; i += 64) { SHA1Transform(context->state, &data[i]); } j = 0; } else i = 0; memcpy(&context->buffer[j], &data[i], len - i); } /* Add padding and return the message digest. */ static void SHA1Final(unsigned char digest[20], SHA1_CTX* context) { unsigned long i; unsigned char finalcount[8]; for (i = 0; i < 8; i++) { finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ } SHA1Update(context, (unsigned char *)"\200", 1); while ((context->count[0] & 504) != 448) { SHA1Update(context, (unsigned char *)"\0", 1); } SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ for (i = 0; i < 20; i++) { digest[i] = (unsigned char) ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); } /* Wipe variables */ i = 0; memset(context->buffer, 0, 64); memset(context->state, 0, 20); memset(context->count, 0, 8); memset(&finalcount, 0, 8); /* make SHA1Transform overwrite it's own static vars */ SHA1Transform(context->state, context->buffer); } void spl_sha1(unsigned char *buffer, unsigned int buffer_len, unsigned char *digest) { SHA1_CTX context; SHA1Init(&context); SHA1Update(&context, buffer, buffer_len); SHA1Final(digest, &context); } #ifdef SPL_TEST_SHA1_MAIN // gcc -DSPL_TEST_SHA1_MAIN=1 -std=c99 -Wall -o sha1_test sha1.c && ./sha1_test static void sha1_test(unsigned char *buffer, unsigned int buffer_len) { unsigned char digest[20]; spl_sha1(buffer, buffer_len, digest); for (int i=0; i<20; i++) printf("%s%s%02X", i == 0 ? " " : "", i%4 == 0 ? " " : "", digest[i]); printf("\n"); } int main() { unsigned char test_data1[] = "abc"; unsigned char test_data2[] = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"; unsigned char test_data3[1000001]; for (int i=0; i<1000000; i++) test_data3[i] = 'a'; test_data3[1000000] = 0; printf("\nSHA-1 Test Vectors from FIPS PUB 180-1:\n"); printf("\n\"abc\"\n"); printf(" A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D\n"); sha1_test(test_data1, sizeof(test_data1)-1); printf("\n\"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq\"\n"); printf(" 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1\n"); sha1_test(test_data2, sizeof(test_data2)-1); printf("\nA million repetitions of \"a\"\n"); printf(" 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F\n"); sha1_test(test_data3, sizeof(test_data3)-1); printf("\n"); return 0; } #endif spl-1.0pre6/webspl.c0000644000000000000000000001551311064463357013121 0ustar rootroot/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * webspl.c: CGI Script for using SPL for web applications */ #include #include #include #include #include #include #include #include #include #include #include "spl.h" #include "webspl_common.h" #include "compat.h" static struct spl_vm *vm; static struct spl_task *task; static void get_session_lock(const char *basedir, const char *session) { char *lockfile; int fd, retries = 0; create_dumpdir(basedir); expire_dumpdir(basedir, EXPIRE_DUMPDIR_TIMEOUT, EXPIRE_DUMPDIR_INTERVAL); my_asprintf(&lockfile, "%s/webspl_cache/%.*s.lock", basedir, (int)strcspn(session, ":"), session); retry_lock: fd = open(lockfile, O_CREAT|O_EXCL, 0600); if ( fd == -1 && errno == EEXIST ) { my_sleep(1); if ( retries++ > 10 ) { spl_report(SPL_REPORT_HOST, vm, "Stalled lock on session file!\n"); exit(0); } goto retry_lock; } close(fd); free(lockfile); } static void free_session_lock(const char *basedir, const char *session) { char *lockfile; my_asprintf(&lockfile, "%s/webspl_cache/%.*s.lock", basedir, (int)strcspn(session, ":"), session); unlink(lockfile); free(lockfile); } int main() { char basedir[1024] = "/tmp"; char *script_file = getenv("PATH_TRANSLATED"); char *scriptdir = script_file; getcwd(basedir, 1024); if (!scriptdir) { printf("Content-type: text/plain\n\n"); printf("Called webspl.cgi directly!\n"); printf("This script should only be used as action handler!\n\n"); printf("For debuging, e.g.:\n"); printf("QUERY_STRING='sid=HVMwQjeajMv3cOtAAP5WEUEP:wsfc_4&switch=3' \\\n"); printf("PATH_TRANSLATED='cfpmanager/cfpmanager.webspl' ./webspl.cgi\n\n"); return 1; } if ( scriptdir ) { char *lastslash = strrchr(scriptdir, '/'); if ( lastslash ) { char *new_scriptdir = malloc(lastslash-scriptdir + 1); strncpy(new_scriptdir, scriptdir, lastslash-scriptdir); new_scriptdir[lastslash-scriptdir] = 0; scriptdir = new_scriptdir; } else scriptdir = basedir; } else scriptdir = basedir; chdir(scriptdir); struct cgi_config *cfg = cgi_config_read(script_file); printf("Cache-Control: no-cache, must-revalidate, no-store\n"); printf("Pragma: nocache\n"); printf("Expires: 0\n"); SPL_REGISTER_BUILTIN_MODULE(cgi); spl_report = spl_mod_cgi_reportfunc; vm = spl_vm_create(); my_asprintf(&vm->path, ".:./spl_modules:%s/spl_modules:%s", basedir, spl_system_modules_dir()); my_asprintf(&vm->codecache_dir, "%s/webspl_cache", basedir); vm->runloop = spl_simple_runloop; spl_builtin_register_all(vm); spl_clib_reg(vm, "write", spl_mod_cgi_write, 0); vm->cgi_ctx = spl_mod_cgi_get_cgi_ctx(0, cfg); if ( !*vm->cgi_ctx->session ) { respawn_this_session: free(vm->cgi_ctx->session); vm->cgi_ctx->session = get_new_session(); get_session_lock(basedir, vm->cgi_ctx->session); int script_file_len = strlen(script_file); if (script_file_len > 8 && !strcmp(script_file+script_file_len-8, ".websplb")) { struct spl_code *code = spl_code_get(0); code->code_type = SPL_CODE_MAPPED; code->code = spl_mmap_file(script_file, &code->size); if (!code->code) { spl_report(SPL_REPORT_HOST, vm, "Can't open bytecode file!\n"); spl_code_put(code); return 1; } task = spl_task_create(vm, "main"); task->flags |= SPL_TASK_FLAG_PUBLIC; spl_task_setcode(task, code); task->code->id = strdup(script_file); } else { char *spl_source = spl_malloc_file(script_file, 0); if ( !spl_source ) { spl_report(SPL_REPORT_HOST, vm, "Can't open script file!\n"); return 1; } struct spl_asm *as = spl_asm_create(); as->vm = vm; if ( spl_compiler(as, spl_source, script_file, spl_malloc_file, 1) ) return 1; spl_asm_add(as, SPL_OP_HALT, "1"); spl_optimizer(as); task = spl_task_create(vm, "main"); task->flags |= SPL_TASK_FLAG_PUBLIC; spl_task_setcode(task, spl_asm_dump(as)); task->code->id = strdup(script_file); spl_asm_destroy(as); free(spl_source); } } else { char *dumpfile; my_asprintf(&dumpfile, "%s/webspl_cache/%.*s.spld", basedir, (int)strcspn(vm->cgi_ctx->session, ":"), vm->cgi_ctx->session); get_session_lock(basedir, vm->cgi_ctx->session); FILE *f = fopen(dumpfile, "r"); if (!f) { if (cgi_config_get_int(cfg, "spl.respawnsessions")) { free_session_lock(basedir, vm->cgi_ctx->session); goto respawn_this_session; } const char *expirelocation = cgi_config_get_str(cfg, "spl.expirelocation"); if (expirelocation) printf("Content-Type: text/html\n\n" "\n" " \n", expirelocation); else spl_report(SPL_REPORT_HOST, vm, "Can't read dumpfile!\n"); free_session_lock(basedir, vm->cgi_ctx->session); return 1; } spl_restore_ascii(vm, f); fclose(f); char *task_name = strchr(vm->cgi_ctx->session, ':'); if ( task_name ) task = spl_task_lookup(vm, task_name+1); else task = spl_task_lookup(vm, "main"); if ( !task || !(task->flags & SPL_TASK_FLAG_PUBLIC) ) { spl_report(SPL_REPORT_HOST, vm, "Can't find task or task is not public!\n"); free_session_lock(basedir, vm->cgi_ctx->session); return 1; } task->flags &= ~SPL_TASK_FLAG_PAUSED; } while ( task && task->code ) { spl_gc_maybe(vm); task = spl_schedule(task); if ( spl_exec(task) < 0 ) break; } char *dumpfile; my_asprintf(&dumpfile, "%s/webspl_cache/%.*s.spld", basedir, (int)strcspn(vm->cgi_ctx->session, ":"), vm->cgi_ctx->session); task = spl_task_lookup(vm, "main"); if ( task && task->code ) { FILE *f = fopen(dumpfile, "w"); if ( f ) { if (spl_dump_ascii(vm, f)) spl_report(SPL_REPORT_HOST, vm, "Dump failed: %s\n", dumpfile); fclose(f); } else spl_report(SPL_REPORT_HOST, vm, "Can't write dumpfile: %s\n", dumpfile); } else unlink(dumpfile); free(dumpfile); free_session_lock(basedir, vm->cgi_ctx->session); if (vm->cgi_ctx) { if (vm->cgi_ctx->config) { cgi_config_free(vm->cgi_ctx->config); vm->cgi_ctx->config = 0; } spl_mod_cgi_free_cgi_ctx(vm->cgi_ctx); vm->cgi_ctx = 0; } spl_vm_destroy(vm); return 0; }