From f345f0f6d2962633e2e3a560b044ab1b91a8f937 Mon Sep 17 00:00:00 2001 From: MirceaKitsune Date: Tue, 11 Jun 2013 00:18:35 +0300 Subject: [PATCH] By fteqcc, hello gmqcc --- misc/source/fteqcc-src/LICENSE | 87 - misc/source/fteqcc-src/Makefile | 90 - misc/source/fteqcc-src/cmdlib.h | 93 - misc/source/fteqcc-src/comprout.c | 252 - misc/source/fteqcc-src/dotnet2008/qcc.sln | 26 - misc/source/fteqcc-src/dotnet2008/qcc.vcproj | 814 -- misc/source/fteqcc-src/dotnet2010/qcc.sln | 26 - misc/source/fteqcc-src/dotnet2010/qcc.vcxproj | 343 - misc/source/fteqcc-src/execloop.h | 1202 --- misc/source/fteqcc-src/gui.h | 21 - misc/source/fteqcc-src/hash.c | 292 - misc/source/fteqcc-src/hash.h | 39 - misc/source/fteqcc-src/initlib.c | 1207 --- misc/source/fteqcc-src/pr_comp.h | 575 - misc/source/fteqcc-src/pr_edict.c | 3172 ------ misc/source/fteqcc-src/pr_exec.c | 1233 --- misc/source/fteqcc-src/pr_multi.c | 447 - misc/source/fteqcc-src/pr_x86.c | 1445 --- misc/source/fteqcc-src/progsint.h | 486 - misc/source/fteqcc-src/progslib.h | 323 - misc/source/fteqcc-src/progtype.h | 23 - misc/source/fteqcc-src/qcc.h | 933 -- misc/source/fteqcc-src/qcc_cmdlib.c | 928 -- misc/source/fteqcc-src/qcc_pr_comp.c | 9521 ----------------- misc/source/fteqcc-src/qcc_pr_lex.c | 3661 ------- misc/source/fteqcc-src/qccgui.c | 2419 ----- misc/source/fteqcc-src/qccguistuff.c | 325 - misc/source/fteqcc-src/qccmain.c | 3654 ------- misc/source/fteqcc-src/qcctui.c | 117 - misc/source/fteqcc-src/qcd.h | 7 - misc/source/fteqcc-src/qcd_main.c | 232 - misc/source/fteqcc-src/qcdecomp.c | 1004 -- misc/source/fteqcc-src/readme.txt | 50 - misc/source/fteqcc-src/test.c | 221 - misc/source/fteqcc-src/vc6/qcc.dsp | 256 - misc/source/fteqcc-src/vc6/qcc.dsw | 29 - misc/source/gmqcc-src/.travis.yml | 14 + misc/source/gmqcc-src/AUTHORS | 9 + misc/source/gmqcc-src/CHANGES | 113 + misc/source/gmqcc-src/INSTALL | 44 + misc/source/gmqcc-src/LICENSE | 21 + misc/source/gmqcc-src/Makefile | 253 + misc/source/gmqcc-src/README | 14 + misc/source/gmqcc-src/TODO | 157 + misc/source/gmqcc-src/ast.c | 3150 ++++++ misc/source/gmqcc-src/ast.h | 638 ++ misc/source/gmqcc-src/code.c | 387 + misc/source/gmqcc-src/conout.c | 447 + misc/source/gmqcc-src/correct.c | 548 + misc/source/gmqcc-src/distro/Makefile | 53 + misc/source/gmqcc-src/distro/deb/Makefile | 45 + misc/source/gmqcc-src/distro/gentoo/INSTALL | 35 + .../distro/gentoo/gmqcc-0.3.0.ebuild | 20 + .../gmqcc-src/distro/slackware/this/Makefile | 35 + .../distro/slackware/this/slack-desc | 12 + misc/source/gmqcc-src/distro/win32/Makefile | 17 + misc/source/gmqcc-src/doc/gmqcc.1 | 650 ++ misc/source/gmqcc-src/doc/gmqpak.1 | 38 + misc/source/gmqcc-src/doc/html/download.c | 284 + misc/source/gmqcc-src/doc/html/gmqcc.png | Bin 0 -> 2399 bytes misc/source/gmqcc-src/doc/html/style.css | 92 + misc/source/gmqcc-src/doc/qcvm.1 | 105 + misc/source/gmqcc-src/doc/specification.tex | 759 ++ misc/source/gmqcc-src/exec.c | 1624 +++ misc/source/gmqcc-src/fs.c | 312 + misc/source/gmqcc-src/ftepp.c | 1909 ++++ misc/source/gmqcc-src/gmqcc.h | 1166 ++ misc/source/gmqcc-src/gmqcc.ini.example | 267 + misc/source/gmqcc-src/intrin.h | 416 + misc/source/gmqcc-src/ir.c | 4004 +++++++ misc/source/gmqcc-src/ir.h | 289 + misc/source/gmqcc-src/lexer.c | 1522 +++ misc/source/gmqcc-src/lexer.h | 345 + misc/source/gmqcc-src/main.c | 808 ++ misc/source/gmqcc-src/misc/check-doc.sh | 43 + misc/source/gmqcc-src/msvc/gmqcc.sln | 38 + misc/source/gmqcc-src/msvc/pak/pak.vcxproj | 78 + .../gmqcc-src/msvc/pak/pak.vcxproj.filters | 16 + misc/source/gmqcc-src/opts.c | 374 + misc/source/gmqcc-src/opts.def | 132 + misc/source/gmqcc-src/pak.c | 584 + misc/source/gmqcc-src/parser.c | 6462 +++++++++++ misc/source/gmqcc-src/stat.c | 628 ++ misc/source/gmqcc-src/syntax/README | 24 + misc/source/gmqcc-src/syntax/geany/README | 8 + .../gmqcc-src/syntax/geany/filetypes.qc | 55 + .../gmqcc-src/syntax/gtksourceview/README | 5 + .../gmqcc-src/syntax/gtksourceview/qc.lang | 173 + misc/source/gmqcc-src/syntax/jedit/README | 26 + misc/source/gmqcc-src/syntax/jedit/qc.xml | 271 + misc/source/gmqcc-src/syntax/kate/README | 9 + misc/source/gmqcc-src/syntax/kate/qc.xml | 155 + misc/source/gmqcc-src/syntax/nano/README | 12 + misc/source/gmqcc-src/syntax/nano/qc.nanorc | 22 + misc/source/gmqcc-src/test.c | 1332 +++ misc/source/gmqcc-src/tests/aliases.qc | 34 + misc/source/gmqcc-src/tests/aliases.tmpl | 9 + misc/source/gmqcc-src/tests/arrays.qc | 52 + misc/source/gmqcc-src/tests/arrays.tmpl | 7 + misc/source/gmqcc-src/tests/bitnot.qc | 14 + misc/source/gmqcc-src/tests/bitnot.tmpl | 9 + misc/source/gmqcc-src/tests/break.qc | 23 + misc/source/gmqcc-src/tests/break.tmpl | 8 + misc/source/gmqcc-src/tests/builtin.qc | 3 + misc/source/gmqcc-src/tests/builtin.tmpl | 7 + misc/source/gmqcc-src/tests/calls.qc | 12 + misc/source/gmqcc-src/tests/calls.tmpl | 6 + .../gmqcc-src/tests/correct-logic-1-s.tmpl | 12 + .../gmqcc-src/tests/correct-logic-1.tmpl | 12 + .../gmqcc-src/tests/correct-logic-2-s.tmpl | 12 + .../gmqcc-src/tests/correct-logic-2.tmpl | 12 + misc/source/gmqcc-src/tests/correct-logic.qc | 24 + .../gmqcc-src/tests/correct-vs-short-1.tmpl | 14 + .../gmqcc-src/tests/correct-vs-short-2.tmpl | 14 + .../gmqcc-src/tests/correct-vs-short-3.tmpl | 14 + .../gmqcc-src/tests/correct-vs-short.qc | 18 + misc/source/gmqcc-src/tests/defs.qh | 19 + misc/source/gmqcc-src/tests/enum.qc | 68 + misc/source/gmqcc-src/tests/enum.tmpl | 25 + misc/source/gmqcc-src/tests/equality.qc | 8 + misc/source/gmqcc-src/tests/equality.tmpl | 6 + misc/source/gmqcc-src/tests/exponentiation.qc | 14 + .../gmqcc-src/tests/exponentiation.tmpl | 9 + .../source/gmqcc-src/tests/exprforbuiltins.qc | 7 + .../gmqcc-src/tests/exprforbuiltins.tmpl | 7 + misc/source/gmqcc-src/tests/fieldparams.qc | 17 + misc/source/gmqcc-src/tests/fieldparams.tmpl | 7 + .../gmqcc-src/tests/functions-as-params.qc | 11 + .../gmqcc-src/tests/functions-as-params.tmpl | 6 + misc/source/gmqcc-src/tests/goto.qc | 32 + misc/source/gmqcc-src/tests/goto.tmpl | 10 + misc/source/gmqcc-src/tests/ifs.qc | 10 + misc/source/gmqcc-src/tests/ifs.tmpl | 6 + misc/source/gmqcc-src/tests/mul_vf.qc | 14 + misc/source/gmqcc-src/tests/mul_vf.tmpl | 5 + misc/source/gmqcc-src/tests/ngraphs.qc | 4 + misc/source/gmqcc-src/tests/ngraphs.tmpl | 6 + misc/source/gmqcc-src/tests/noref.qc | 7 + misc/source/gmqcc-src/tests/noref.tmpl | 4 + misc/source/gmqcc-src/tests/noreturn.qc | 31 + misc/source/gmqcc-src/tests/noreturn1.tmpl | 5 + misc/source/gmqcc-src/tests/noreturn2.tmpl | 5 + misc/source/gmqcc-src/tests/noreturn3.tmpl | 5 + misc/source/gmqcc-src/tests/noreturn4.tmpl | 5 + misc/source/gmqcc-src/tests/operators.qc | 57 + misc/source/gmqcc-src/tests/operators.tmpl | 21 + misc/source/gmqcc-src/tests/param8.qc | 22 + misc/source/gmqcc-src/tests/param8.tmpl | 7 + misc/source/gmqcc-src/tests/parens.qc | 41 + misc/source/gmqcc-src/tests/parens.tmpl | 22 + misc/source/gmqcc-src/tests/perl-logic.qc | 6 + misc/source/gmqcc-src/tests/perl-logic.tmpl | 6 + misc/source/gmqcc-src/tests/pmacros.qc | 37 + misc/source/gmqcc-src/tests/pmacros.tmpl | 6 + misc/source/gmqcc-src/tests/pointlife.qc | 19 + misc/source/gmqcc-src/tests/pointlife.tmpl | 6 + misc/source/gmqcc-src/tests/pops.qc | 19 + misc/source/gmqcc-src/tests/pops.tmpl | 7 + misc/source/gmqcc-src/tests/pp_va_args.qc | 22 + misc/source/gmqcc-src/tests/pp_va_args.tmpl | 8 + misc/source/gmqcc-src/tests/ppcat.qc | 13 + misc/source/gmqcc-src/tests/ppcat.tmpl | 10 + misc/source/gmqcc-src/tests/pperror.qc | 5 + misc/source/gmqcc-src/tests/pperror.tmpl | 4 + misc/source/gmqcc-src/tests/predef_func.qc | 9 + misc/source/gmqcc-src/tests/predef_func.tmpl | 5 + .../gmqcc-src/tests/predef_func_concat.tmpl | 5 + misc/source/gmqcc-src/tests/rassign.qc | 35 + misc/source/gmqcc-src/tests/rassign.tmpl | 8 + misc/source/gmqcc-src/tests/short-logic.qc | 46 + misc/source/gmqcc-src/tests/short-logic.tmpl | 9 + misc/source/gmqcc-src/tests/switch.qc | 35 + misc/source/gmqcc-src/tests/switch.tmpl | 12 + misc/source/gmqcc-src/tests/ternary-fte.tmpl | 15 + misc/source/gmqcc-src/tests/ternary.qc | 43 + misc/source/gmqcc-src/tests/ternary.tmpl | 21 + .../gmqcc-src/tests/truth-flags-1-s.tmpl | 14 + .../source/gmqcc-src/tests/truth-flags-1.tmpl | 14 + .../gmqcc-src/tests/truth-flags-2-s.tmpl | 14 + misc/source/gmqcc-src/tests/truth-flags-2.qc | 27 + .../source/gmqcc-src/tests/truth-flags-2.tmpl | 14 + .../gmqcc-src/tests/truth-flags-3-s.tmpl | 14 + .../source/gmqcc-src/tests/truth-flags-3.tmpl | 14 + misc/source/gmqcc-src/tests/truth.qc | 26 + misc/source/gmqcc-src/tests/truth.tmpl | 10 + misc/source/gmqcc-src/tests/truth2.tmpl | 10 + misc/source/gmqcc-src/tests/truth3.tmpl | 10 + misc/source/gmqcc-src/tests/typedefs.qc | 12 + misc/source/gmqcc-src/tests/typedefs.tmpl | 6 + misc/source/gmqcc-src/tests/uninit.qc | 21 + misc/source/gmqcc-src/tests/uninit.tmpl | 4 + misc/source/gmqcc-src/tests/uninit2.tmpl | 4 + misc/source/gmqcc-src/tests/utf8-2.tmpl | 6 + misc/source/gmqcc-src/tests/utf8.qc | 10 + misc/source/gmqcc-src/tests/utf8.tmpl | 4 + misc/source/gmqcc-src/tests/varargs.qc | 20 + misc/source/gmqcc-src/tests/varargs.tmpl | 12 + misc/source/gmqcc-src/tests/varargs2.qc | 12 + misc/source/gmqcc-src/tests/varargs2.tmpl | 5 + misc/source/gmqcc-src/tests/variadic.qc | 3 + misc/source/gmqcc-src/tests/variadic.tmpl | 6 + misc/source/gmqcc-src/tests/vec_ops.qc | 7 + misc/source/gmqcc-src/tests/vec_ops.tmpl | 9 + misc/source/gmqcc-src/tests/vector-init.qc | 21 + misc/source/gmqcc-src/tests/vector-init.tmpl | 4 + misc/source/gmqcc-src/tests/vector-init2.tmpl | 4 + misc/source/gmqcc-src/tests/vector-init3.tmpl | 4 + misc/source/gmqcc-src/utf8.c | 283 + misc/source/gmqcc-src/util.c | 543 + 209 files changed, 33466 insertions(+), 35553 deletions(-) delete mode 100644 misc/source/fteqcc-src/LICENSE delete mode 100644 misc/source/fteqcc-src/Makefile delete mode 100644 misc/source/fteqcc-src/cmdlib.h delete mode 100644 misc/source/fteqcc-src/comprout.c delete mode 100644 misc/source/fteqcc-src/dotnet2008/qcc.sln delete mode 100644 misc/source/fteqcc-src/dotnet2008/qcc.vcproj delete mode 100644 misc/source/fteqcc-src/dotnet2010/qcc.sln delete mode 100644 misc/source/fteqcc-src/dotnet2010/qcc.vcxproj delete mode 100644 misc/source/fteqcc-src/execloop.h delete mode 100644 misc/source/fteqcc-src/gui.h delete mode 100644 misc/source/fteqcc-src/hash.c delete mode 100644 misc/source/fteqcc-src/hash.h delete mode 100644 misc/source/fteqcc-src/initlib.c delete mode 100644 misc/source/fteqcc-src/pr_comp.h delete mode 100644 misc/source/fteqcc-src/pr_edict.c delete mode 100644 misc/source/fteqcc-src/pr_exec.c delete mode 100644 misc/source/fteqcc-src/pr_multi.c delete mode 100644 misc/source/fteqcc-src/pr_x86.c delete mode 100644 misc/source/fteqcc-src/progsint.h delete mode 100644 misc/source/fteqcc-src/progslib.h delete mode 100644 misc/source/fteqcc-src/progtype.h delete mode 100644 misc/source/fteqcc-src/qcc.h delete mode 100644 misc/source/fteqcc-src/qcc_cmdlib.c delete mode 100644 misc/source/fteqcc-src/qcc_pr_comp.c delete mode 100644 misc/source/fteqcc-src/qcc_pr_lex.c delete mode 100644 misc/source/fteqcc-src/qccgui.c delete mode 100644 misc/source/fteqcc-src/qccguistuff.c delete mode 100644 misc/source/fteqcc-src/qccmain.c delete mode 100644 misc/source/fteqcc-src/qcctui.c delete mode 100644 misc/source/fteqcc-src/qcd.h delete mode 100644 misc/source/fteqcc-src/qcd_main.c delete mode 100644 misc/source/fteqcc-src/qcdecomp.c delete mode 100644 misc/source/fteqcc-src/readme.txt delete mode 100644 misc/source/fteqcc-src/test.c delete mode 100644 misc/source/fteqcc-src/vc6/qcc.dsp delete mode 100644 misc/source/fteqcc-src/vc6/qcc.dsw create mode 100644 misc/source/gmqcc-src/.travis.yml create mode 100644 misc/source/gmqcc-src/AUTHORS create mode 100644 misc/source/gmqcc-src/CHANGES create mode 100644 misc/source/gmqcc-src/INSTALL create mode 100644 misc/source/gmqcc-src/LICENSE create mode 100644 misc/source/gmqcc-src/Makefile create mode 100644 misc/source/gmqcc-src/README create mode 100644 misc/source/gmqcc-src/TODO create mode 100644 misc/source/gmqcc-src/ast.c create mode 100644 misc/source/gmqcc-src/ast.h create mode 100644 misc/source/gmqcc-src/code.c create mode 100644 misc/source/gmqcc-src/conout.c create mode 100644 misc/source/gmqcc-src/correct.c create mode 100644 misc/source/gmqcc-src/distro/Makefile create mode 100644 misc/source/gmqcc-src/distro/deb/Makefile create mode 100644 misc/source/gmqcc-src/distro/gentoo/INSTALL create mode 100644 misc/source/gmqcc-src/distro/gentoo/gmqcc-0.3.0.ebuild create mode 100644 misc/source/gmqcc-src/distro/slackware/this/Makefile create mode 100644 misc/source/gmqcc-src/distro/slackware/this/slack-desc create mode 100644 misc/source/gmqcc-src/distro/win32/Makefile create mode 100644 misc/source/gmqcc-src/doc/gmqcc.1 create mode 100644 misc/source/gmqcc-src/doc/gmqpak.1 create mode 100644 misc/source/gmqcc-src/doc/html/download.c create mode 100644 misc/source/gmqcc-src/doc/html/gmqcc.png create mode 100644 misc/source/gmqcc-src/doc/html/style.css create mode 100644 misc/source/gmqcc-src/doc/qcvm.1 create mode 100644 misc/source/gmqcc-src/doc/specification.tex create mode 100644 misc/source/gmqcc-src/exec.c create mode 100644 misc/source/gmqcc-src/fs.c create mode 100644 misc/source/gmqcc-src/ftepp.c create mode 100644 misc/source/gmqcc-src/gmqcc.h create mode 100644 misc/source/gmqcc-src/gmqcc.ini.example create mode 100644 misc/source/gmqcc-src/intrin.h create mode 100644 misc/source/gmqcc-src/ir.c create mode 100644 misc/source/gmqcc-src/ir.h create mode 100644 misc/source/gmqcc-src/lexer.c create mode 100644 misc/source/gmqcc-src/lexer.h create mode 100644 misc/source/gmqcc-src/main.c create mode 100644 misc/source/gmqcc-src/misc/check-doc.sh create mode 100644 misc/source/gmqcc-src/msvc/gmqcc.sln create mode 100644 misc/source/gmqcc-src/msvc/pak/pak.vcxproj create mode 100644 misc/source/gmqcc-src/msvc/pak/pak.vcxproj.filters create mode 100644 misc/source/gmqcc-src/opts.c create mode 100644 misc/source/gmqcc-src/opts.def create mode 100644 misc/source/gmqcc-src/pak.c create mode 100644 misc/source/gmqcc-src/parser.c create mode 100644 misc/source/gmqcc-src/stat.c create mode 100644 misc/source/gmqcc-src/syntax/README create mode 100644 misc/source/gmqcc-src/syntax/geany/README create mode 100644 misc/source/gmqcc-src/syntax/geany/filetypes.qc create mode 100644 misc/source/gmqcc-src/syntax/gtksourceview/README create mode 100644 misc/source/gmqcc-src/syntax/gtksourceview/qc.lang create mode 100644 misc/source/gmqcc-src/syntax/jedit/README create mode 100644 misc/source/gmqcc-src/syntax/jedit/qc.xml create mode 100644 misc/source/gmqcc-src/syntax/kate/README create mode 100644 misc/source/gmqcc-src/syntax/kate/qc.xml create mode 100644 misc/source/gmqcc-src/syntax/nano/README create mode 100644 misc/source/gmqcc-src/syntax/nano/qc.nanorc create mode 100644 misc/source/gmqcc-src/test.c create mode 100644 misc/source/gmqcc-src/tests/aliases.qc create mode 100644 misc/source/gmqcc-src/tests/aliases.tmpl create mode 100644 misc/source/gmqcc-src/tests/arrays.qc create mode 100644 misc/source/gmqcc-src/tests/arrays.tmpl create mode 100644 misc/source/gmqcc-src/tests/bitnot.qc create mode 100644 misc/source/gmqcc-src/tests/bitnot.tmpl create mode 100644 misc/source/gmqcc-src/tests/break.qc create mode 100644 misc/source/gmqcc-src/tests/break.tmpl create mode 100644 misc/source/gmqcc-src/tests/builtin.qc create mode 100644 misc/source/gmqcc-src/tests/builtin.tmpl create mode 100644 misc/source/gmqcc-src/tests/calls.qc create mode 100644 misc/source/gmqcc-src/tests/calls.tmpl create mode 100644 misc/source/gmqcc-src/tests/correct-logic-1-s.tmpl create mode 100644 misc/source/gmqcc-src/tests/correct-logic-1.tmpl create mode 100644 misc/source/gmqcc-src/tests/correct-logic-2-s.tmpl create mode 100644 misc/source/gmqcc-src/tests/correct-logic-2.tmpl create mode 100644 misc/source/gmqcc-src/tests/correct-logic.qc create mode 100644 misc/source/gmqcc-src/tests/correct-vs-short-1.tmpl create mode 100644 misc/source/gmqcc-src/tests/correct-vs-short-2.tmpl create mode 100644 misc/source/gmqcc-src/tests/correct-vs-short-3.tmpl create mode 100644 misc/source/gmqcc-src/tests/correct-vs-short.qc create mode 100644 misc/source/gmqcc-src/tests/defs.qh create mode 100644 misc/source/gmqcc-src/tests/enum.qc create mode 100644 misc/source/gmqcc-src/tests/enum.tmpl create mode 100644 misc/source/gmqcc-src/tests/equality.qc create mode 100644 misc/source/gmqcc-src/tests/equality.tmpl create mode 100644 misc/source/gmqcc-src/tests/exponentiation.qc create mode 100644 misc/source/gmqcc-src/tests/exponentiation.tmpl create mode 100644 misc/source/gmqcc-src/tests/exprforbuiltins.qc create mode 100644 misc/source/gmqcc-src/tests/exprforbuiltins.tmpl create mode 100644 misc/source/gmqcc-src/tests/fieldparams.qc create mode 100644 misc/source/gmqcc-src/tests/fieldparams.tmpl create mode 100644 misc/source/gmqcc-src/tests/functions-as-params.qc create mode 100644 misc/source/gmqcc-src/tests/functions-as-params.tmpl create mode 100644 misc/source/gmqcc-src/tests/goto.qc create mode 100644 misc/source/gmqcc-src/tests/goto.tmpl create mode 100644 misc/source/gmqcc-src/tests/ifs.qc create mode 100644 misc/source/gmqcc-src/tests/ifs.tmpl create mode 100644 misc/source/gmqcc-src/tests/mul_vf.qc create mode 100644 misc/source/gmqcc-src/tests/mul_vf.tmpl create mode 100644 misc/source/gmqcc-src/tests/ngraphs.qc create mode 100644 misc/source/gmqcc-src/tests/ngraphs.tmpl create mode 100644 misc/source/gmqcc-src/tests/noref.qc create mode 100644 misc/source/gmqcc-src/tests/noref.tmpl create mode 100644 misc/source/gmqcc-src/tests/noreturn.qc create mode 100644 misc/source/gmqcc-src/tests/noreturn1.tmpl create mode 100644 misc/source/gmqcc-src/tests/noreturn2.tmpl create mode 100644 misc/source/gmqcc-src/tests/noreturn3.tmpl create mode 100644 misc/source/gmqcc-src/tests/noreturn4.tmpl create mode 100644 misc/source/gmqcc-src/tests/operators.qc create mode 100644 misc/source/gmqcc-src/tests/operators.tmpl create mode 100644 misc/source/gmqcc-src/tests/param8.qc create mode 100644 misc/source/gmqcc-src/tests/param8.tmpl create mode 100644 misc/source/gmqcc-src/tests/parens.qc create mode 100644 misc/source/gmqcc-src/tests/parens.tmpl create mode 100644 misc/source/gmqcc-src/tests/perl-logic.qc create mode 100644 misc/source/gmqcc-src/tests/perl-logic.tmpl create mode 100644 misc/source/gmqcc-src/tests/pmacros.qc create mode 100644 misc/source/gmqcc-src/tests/pmacros.tmpl create mode 100644 misc/source/gmqcc-src/tests/pointlife.qc create mode 100644 misc/source/gmqcc-src/tests/pointlife.tmpl create mode 100644 misc/source/gmqcc-src/tests/pops.qc create mode 100644 misc/source/gmqcc-src/tests/pops.tmpl create mode 100644 misc/source/gmqcc-src/tests/pp_va_args.qc create mode 100644 misc/source/gmqcc-src/tests/pp_va_args.tmpl create mode 100644 misc/source/gmqcc-src/tests/ppcat.qc create mode 100644 misc/source/gmqcc-src/tests/ppcat.tmpl create mode 100644 misc/source/gmqcc-src/tests/pperror.qc create mode 100644 misc/source/gmqcc-src/tests/pperror.tmpl create mode 100644 misc/source/gmqcc-src/tests/predef_func.qc create mode 100644 misc/source/gmqcc-src/tests/predef_func.tmpl create mode 100644 misc/source/gmqcc-src/tests/predef_func_concat.tmpl create mode 100644 misc/source/gmqcc-src/tests/rassign.qc create mode 100644 misc/source/gmqcc-src/tests/rassign.tmpl create mode 100644 misc/source/gmqcc-src/tests/short-logic.qc create mode 100644 misc/source/gmqcc-src/tests/short-logic.tmpl create mode 100644 misc/source/gmqcc-src/tests/switch.qc create mode 100644 misc/source/gmqcc-src/tests/switch.tmpl create mode 100644 misc/source/gmqcc-src/tests/ternary-fte.tmpl create mode 100644 misc/source/gmqcc-src/tests/ternary.qc create mode 100644 misc/source/gmqcc-src/tests/ternary.tmpl create mode 100644 misc/source/gmqcc-src/tests/truth-flags-1-s.tmpl create mode 100644 misc/source/gmqcc-src/tests/truth-flags-1.tmpl create mode 100644 misc/source/gmqcc-src/tests/truth-flags-2-s.tmpl create mode 100644 misc/source/gmqcc-src/tests/truth-flags-2.qc create mode 100644 misc/source/gmqcc-src/tests/truth-flags-2.tmpl create mode 100644 misc/source/gmqcc-src/tests/truth-flags-3-s.tmpl create mode 100644 misc/source/gmqcc-src/tests/truth-flags-3.tmpl create mode 100644 misc/source/gmqcc-src/tests/truth.qc create mode 100644 misc/source/gmqcc-src/tests/truth.tmpl create mode 100644 misc/source/gmqcc-src/tests/truth2.tmpl create mode 100644 misc/source/gmqcc-src/tests/truth3.tmpl create mode 100644 misc/source/gmqcc-src/tests/typedefs.qc create mode 100644 misc/source/gmqcc-src/tests/typedefs.tmpl create mode 100644 misc/source/gmqcc-src/tests/uninit.qc create mode 100644 misc/source/gmqcc-src/tests/uninit.tmpl create mode 100644 misc/source/gmqcc-src/tests/uninit2.tmpl create mode 100644 misc/source/gmqcc-src/tests/utf8-2.tmpl create mode 100644 misc/source/gmqcc-src/tests/utf8.qc create mode 100644 misc/source/gmqcc-src/tests/utf8.tmpl create mode 100644 misc/source/gmqcc-src/tests/varargs.qc create mode 100644 misc/source/gmqcc-src/tests/varargs.tmpl create mode 100644 misc/source/gmqcc-src/tests/varargs2.qc create mode 100644 misc/source/gmqcc-src/tests/varargs2.tmpl create mode 100644 misc/source/gmqcc-src/tests/variadic.qc create mode 100644 misc/source/gmqcc-src/tests/variadic.tmpl create mode 100644 misc/source/gmqcc-src/tests/vec_ops.qc create mode 100644 misc/source/gmqcc-src/tests/vec_ops.tmpl create mode 100644 misc/source/gmqcc-src/tests/vector-init.qc create mode 100644 misc/source/gmqcc-src/tests/vector-init.tmpl create mode 100644 misc/source/gmqcc-src/tests/vector-init2.tmpl create mode 100644 misc/source/gmqcc-src/tests/vector-init3.tmpl create mode 100644 misc/source/gmqcc-src/utf8.c create mode 100644 misc/source/gmqcc-src/util.c diff --git a/misc/source/fteqcc-src/LICENSE b/misc/source/fteqcc-src/LICENSE deleted file mode 100644 index 2f3289af..00000000 --- a/misc/source/fteqcc-src/LICENSE +++ /dev/null @@ -1,87 +0,0 @@ -GNU GENERAL PUBLIC LICENSE -Version 2, June 1991 - -Copyright (C) 1989, 1991 Free Software Foundation, Inc. -59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - -Everyone is permitted to copy and distribute verbatim copies -of this license document, but changing it is not allowed. -Preamble -The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. - -When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. - -To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. - -For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. - -We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. - -Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. - -Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. - -The precise terms and conditions for copying, distribution and modification follow. - -TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION -0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. - -1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. - -You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. - -2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: - - -a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. - -b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. - -c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) -These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. -Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. - -3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: - -a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, - -b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, - -c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) -The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. -If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. - -4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. - -5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. - -6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. - -7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. - -This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. - -8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. - -9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. - -10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. - -NO WARRANTY - -11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - -12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - - -END OF TERMS AND CONDITIONS diff --git a/misc/source/fteqcc-src/Makefile b/misc/source/fteqcc-src/Makefile deleted file mode 100644 index 786d22e3..00000000 --- a/misc/source/fteqcc-src/Makefile +++ /dev/null @@ -1,90 +0,0 @@ -COMMON_OBJS=comprout.o hash.o qcc_cmdlib.o qcd_main.o -QCC_OBJS=qccmain.o qcc_pr_comp.o qcc_pr_lex.o -VM_OBJS=pr_exec.o pr_edict.o pr_multi.o initlib.o qcdecomp.o -GTKGUI_OBJS=qcc_gtk.o qccguistuff.o -WIN32GUI_OBJS=qccgui.o qccguistuff.o -TUI_OBJS=qcctui.o -LIB_OBJS= - -CC?=gcc -CFLAGS?=-Wall - -all: qcc - -USEGUI_CFLAGS= -# set to -DUSEGUI when compiling the GUI -BASE_CFLAGS=-ggdb $(USEGUI_CFLAGS) - -BASE_LDFLAGS=-s -# set to "" for debugging - -DO_CC=$(CC) $(BASE_CFLAGS) -o $@ -c $< $(CFLAGS) - -lib: - -R_win_nocyg: $(QCC_OBJS) $(COMMON_OBJS) $(WIN32GUI_OBJS) - $(CC) $(BASE_CFLAGS) -o fteqcc.exe -O3 $(BASE_LDFLAGS) $(QCC_OBJS) $(COMMON_OBJS) $(WIN32GUI_OBJS) -mno-cygwin -mwindows -lcomctl32 -R_nocyg: $(QCC_OBJS) $(COMMON_OBJS) $(WIN32GUI_OBJS) - $(CC) $(BASE_CFLAGS) -o fteqcc.exe -O3 $(BASE_LDFLAGS) $(QCC_OBJS) $(COMMON_OBJS) $(WIN32GUI_OBJS) -mno-cygwin -lcomctl32 -R_win: $(QCC_OBJS) $(COMMON_OBJS) $(WIN32GUI_OBJS) - $(CC) $(BASE_CFLAGS) -o fteqcc.exe -O3 $(BASE_LDFLAGS) $(QCC_OBJS) $(COMMON_OBJS) $(WIN32GUI_OBJS) -mwindows -lcomctl32 - -win_nocyg: - $(MAKE) USEGUI_CFLAGS="-DUSEGUI -DQCCONLY" R_win_nocyg -nocyg: - $(MAKE) USEGUI_CFLAGS="-DUSEGUI -DQCCONLY" R_nocyg -win: - $(MAKE) USEGUI_CFLAGS="-DUSEGUI -DQCCONLY" R_win - -R_qcc: $(QCC_OBJS) $(COMMON_OBJS) $(TUI_OBJS) - $(CC) $(BASE_CFLAGS) -o fteqcc.bin -O3 $(BASE_LDFLAGS) $(QCC_OBJS) $(TUI_OBJS) $(COMMON_OBJS) -qcc: - $(MAKE) USEGUI_CFLAGS="" R_qcc - -qccmain.o: qccmain.c qcc.h - $(DO_CC) - -qcc_cmdlib.o: qcc_cmdlib.c qcc.h - $(DO_CC) - -qcc_pr_comp.o: qcc_pr_comp.c qcc.h - $(DO_CC) - -qcc_pr_lex.o: qcc_pr_lex.c qcc.h - $(DO_CC) - -comprout.o: comprout.c qcc.h - $(DO_CC) - -hash.o: hash.c qcc.h - $(DO_CC) - -qcd_main.o: qcd_main.c qcc.h - $(DO_CC) - -qccguistuff.o: qccguistuff.c qcc.h - $(DO_CC) - -qcc_gtk.o: qcc_gtk.c qcc.h - $(DO_CC) `pkg-config --cflags gtk+-2.0` - -R_gtkgui: $(QCC_OBJS) $(COMMON_OBJS) $(GTKGUI_OBJS) - $(CC) $(BASE_CFLAGS) $(USEGUI_CFLAGS) -o fteqccgui.bin -O3 $(GTKGUI_OBJS) $(QCC_OBJS) $(COMMON_OBJS) `pkg-config --libs gtk+-2.0` -gtkgui: - $(MAKE) USEGUI_CFLAGS="-DUSEGUI -DQCCONLY" R_gtkgui - -clean: - $(RM) fteqcc.bin fteqcc.exe $(QCC_OBJS) $(COMMON_OBJS) $(VM_OBJS) $(GTKGUI_OBJS) $(WIN32GUI_OBJS) $(TUI_OBJS) - -qcvm.so: $(QCC_OBJS) $(VM_OBJS) $(COMMON_OBJS) - $(CC) $(BASE_CFLAGS) -o $@ -O3 $(BASE_LDFLAGS) $(QCC_OBJS) $(VM_OBJS) $(COMMON_OBJS) -shared - - -test.o: test.c - $(DO_CC) - -testapp.bin: qcvm.so test.o - $(CC) $(BASE_CFLAGS) -o testapp.bin -O3 $(BASE_LDFLAGS) qcvm.so test.o - -regressiontest: testapp.bin - ./testapp.bin regression.dat -srcfile regression.src diff --git a/misc/source/fteqcc-src/cmdlib.h b/misc/source/fteqcc-src/cmdlib.h deleted file mode 100644 index 768d899a..00000000 --- a/misc/source/fteqcc-src/cmdlib.h +++ /dev/null @@ -1,93 +0,0 @@ -// cmdlib.h - -#ifndef __CMDLIB__ -#define __CMDLIB__ - -#include "progsint.h" - -/*#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef NeXT -#include -#endif -*/ - -// the dec offsetof macro doesn't work very well... -#define myoffsetof(type,identifier) ((size_t)&((type *)NULL)->identifier) - - -// set these before calling CheckParm -extern int myargc; -extern char **myargv; - -//char *strupr (char *in); -//char *strlower (char *in); -int QCC_filelength (int handle); -int QCC_tell (int handle); - -int QC_strcasecmp (const char *s1, const char *s2); - -#ifdef _MSC_VER -#define QC_vsnprintf _vsnprintf -#else -#define QC_vsnprintf vsnprintf -#endif - -#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) - #ifndef LIKEPRINTF - #define LIKEPRINTF(x) __attribute__((format(printf,x,x+1))) - #endif -#endif -#ifndef LIKEPRINTF -#define LIKEPRINTF(x) -#endif - -double I_FloatTime (void); - -void VARGS QCC_Error (int errortype, const char *error, ...) LIKEPRINTF(2); -int CheckParm (char *check); - - -int SafeOpenWrite (char *filename, int maxsize); -int SafeOpenRead (char *filename); -void SafeRead (int handle, void *buffer, long count); -void SafeWrite (int handle, void *buffer, long count); -void SafeClose(int handle); -int SafeSeek(int hand, int ofs, int mode); -void *SafeMalloc (long size); - - -long QCC_LoadFile (char *filename, void **bufferptr); -void QCC_SaveFile (char *filename, void *buffer, long count); - -void DefaultExtension (char *path, char *extension); -void DefaultPath (char *path, char *basepath); -void StripFilename (char *path); -void StripExtension (char *path); - -void ExtractFilePath (char *path, char *dest); -void ExtractFileBase (char *path, char *dest); -void ExtractFileExtension (char *path, char *dest); - -long ParseNum (char *str); - - -char *QCC_COM_Parse (char *data); -char *QCC_COM_Parse2 (char *data); - -extern char qcc_token[1024]; -extern int qcc_eof; - - - -#endif diff --git a/misc/source/fteqcc-src/comprout.c b/misc/source/fteqcc-src/comprout.c deleted file mode 100644 index a2a7a3bd..00000000 --- a/misc/source/fteqcc-src/comprout.c +++ /dev/null @@ -1,252 +0,0 @@ -//compile routines - -#include "qcc.h" -#undef progfuncs - -char errorfile[128]; -int errorline; - -progfuncs_t *qccprogfuncs; - -#include - -extern int qcc_compileactive; -jmp_buf qcccompileerror; -char qcc_gamedir[128]; -void QCC_PR_ResetErrorScope(void); - - - -#ifdef MINIMAL - -#else - -int qccalloced; -int qcchunksize; -char *qcchunk; -void *qccHunkAlloc(size_t mem) -{ - mem = (mem + 7)&~7; - - qccalloced+=mem; - if (qccalloced > qcchunksize) - QCC_Error(ERR_INTERNAL, "Compile hunk was filled"); - - memset(qcchunk+qccalloced-mem, 0, mem); - return qcchunk+qccalloced-mem; -} -void qccClearHunk(void) -{ - if (qcchunk) - { - free(qcchunk); - qcchunk=NULL; - } -} -int qccpersisthunk; -void PostCompile(void) -{ - if (!qccpersisthunk) - qccClearHunk(); - - if (asmfile) - { - fclose(asmfile); - asmfile = NULL; - } -} -pbool PreCompile(void) -{ - QCC_PR_ResetErrorScope(); - - qccClearHunk(); - strcpy(qcc_gamedir, ""); - qcchunk = malloc(qcchunksize=128*1024*1024); - while(!qcchunk && qcchunksize > 8*1024*1024) - { - qcchunksize /= 2; - qcchunk = malloc(qcchunksize); - } - qccalloced=0; - - return !!qcchunk; -} - -void QCC_main (int argc, char **argv); -void QCC_ContinueCompile(void); -void QCC_FinishCompile(void); - -int comp_nump;char **comp_parms; -//void Editor(char *fname, int line, int numparms, char **compileparms); -pbool CompileParams(progfuncs_t *progfuncs, int doall, int nump, char **parms) -{ - comp_nump = nump; - comp_parms = parms; - *errorfile = '\0'; - qccprogfuncs = progfuncs; - if (setjmp(qcccompileerror)) - { - PostCompile(); - if (*errorfile) - { - if (!externs->useeditor) - printf("Error in %s on line %i\n", errorfile, errorline); - else - externs->useeditor(progfuncs, errorfile, errorline, nump, parms); - } - return false; - } - - if (!PreCompile()) - return false; - QCC_main(nump, parms); - - while(qcc_compileactive) - QCC_ContinueCompile(); - - PostCompile(); - - return true; -} -int Comp_Begin(progfuncs_t *progfuncs, int nump, char **parms) -{ - comp_nump = nump; - comp_parms = parms; - qccprogfuncs = progfuncs; - *errorfile = '\0'; - if (setjmp(qcccompileerror)) - { - PostCompile(); - if (*errorfile) - externs->useeditor(progfuncs, errorfile, errorline, nump, parms); - return false; - } - - if (!PreCompile()) - return false; - QCC_main(nump, parms); - - return true; -} -int Comp_Continue(progfuncs_t *progfuncs) -{ - qccprogfuncs = progfuncs; - if (setjmp(qcccompileerror)) - { - PostCompile(); - if (*errorfile && externs->useeditor) - externs->useeditor(progfuncs, errorfile, errorline, comp_nump, comp_parms); - return false; - } - - if (qcc_compileactive) - QCC_ContinueCompile(); - else - { - PostCompile(); - - if (*errorfile && externs->useeditor) - externs->useeditor(progfuncs, errorfile, errorline, comp_nump, comp_parms); - - return false; - } - - return true; -} -#endif -pbool CompileFile(progfuncs_t *progfuncs, char *filename) -{ -#ifdef MINIMAL - return false; -#else - char srcfile[32]; - char newname[32]; - static char *p[5]; - int parms; - char *s, *s2; - - p[0] = NULL; - parms = 1; - - strcpy(newname, filename); - s = newname; - if (strchr(s+1, '/')) - { - while(1) - { - s2 = strchr(s+1, '/'); - if (!s2) - { - *s = '\0'; - break; - } - s = s2; - } - p[parms] = "-src"; - p[parms+1] = newname; - parms+=2; - - strcpy(srcfile, s+1); - srcfile[strlen(srcfile)-4] = '\0'; - strcat(srcfile, ".src"); - - if (externs->FileSize(qcva("%s/%s", newname, srcfile))>0) - { - p[parms] = "-srcfile"; - p[parms+1] = srcfile; - parms+=2; - } - } - else - { - p[parms] = "-srcfile"; - p[parms+1] = newname; - newname[strlen(newname)-4] = '\0'; - strcat(newname, ".src"); - parms+=2; - } -// p[2][strlen(p[2])-4] = '\0'; -// strcat(p[2], "/"); - - while (!CompileParams(progfuncs, true, parms, p)) - { - return false; - } - return true; -#endif -} - -int QC_strncasecmp(const char *s1, const char *s2, int n) -{ - int c1, c2; - - while (1) - { - c1 = *s1++; - c2 = *s2++; - - if (!n--) - return 0; // strings are equal until end point - - if (c1 != c2) - { - if (c1 >= 'a' && c1 <= 'z') - c1 -= ('a' - 'A'); - if (c2 >= 'a' && c2 <= 'z') - c2 -= ('a' - 'A'); - if (c1 != c2) - return -1; // strings not equal - } - if (!c1) - return 0; // strings are equal - } - - return -1; -} - -void editbadfile(char *fname, int line) -{ - strcpy(errorfile, fname); - errorline = line; -} - diff --git a/misc/source/fteqcc-src/dotnet2008/qcc.sln b/misc/source/fteqcc-src/dotnet2008/qcc.sln deleted file mode 100644 index a50d9b37..00000000 --- a/misc/source/fteqcc-src/dotnet2008/qcc.sln +++ /dev/null @@ -1,26 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 10.00 -# Visual Studio 2008 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qcc", "qcc.vcproj", "{B9F10DBD-C3E7-4312-B357-0FEFB360979A}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - GUIDebug|Win32 = GUIDebug|Win32 - GUIRelease|Win32 = GUIRelease|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {B9F10DBD-C3E7-4312-B357-0FEFB360979A}.Debug|Win32.ActiveCfg = Debug|Win32 - {B9F10DBD-C3E7-4312-B357-0FEFB360979A}.Debug|Win32.Build.0 = Debug|Win32 - {B9F10DBD-C3E7-4312-B357-0FEFB360979A}.GUIDebug|Win32.ActiveCfg = GUIDebug|Win32 - {B9F10DBD-C3E7-4312-B357-0FEFB360979A}.GUIDebug|Win32.Build.0 = GUIDebug|Win32 - {B9F10DBD-C3E7-4312-B357-0FEFB360979A}.GUIRelease|Win32.ActiveCfg = GUIRelease|Win32 - {B9F10DBD-C3E7-4312-B357-0FEFB360979A}.GUIRelease|Win32.Build.0 = GUIRelease|Win32 - {B9F10DBD-C3E7-4312-B357-0FEFB360979A}.Release|Win32.ActiveCfg = Release|Win32 - {B9F10DBD-C3E7-4312-B357-0FEFB360979A}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/misc/source/fteqcc-src/dotnet2008/qcc.vcproj b/misc/source/fteqcc-src/dotnet2008/qcc.vcproj deleted file mode 100644 index 040d9ea5..00000000 --- a/misc/source/fteqcc-src/dotnet2008/qcc.vcproj +++ /dev/null @@ -1,814 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/misc/source/fteqcc-src/dotnet2010/qcc.sln b/misc/source/fteqcc-src/dotnet2010/qcc.sln deleted file mode 100644 index 4d85f4a3..00000000 --- a/misc/source/fteqcc-src/dotnet2010/qcc.sln +++ /dev/null @@ -1,26 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qcc", "qcc.vcxproj", "{B9F10DBD-C3E7-4312-B357-0FEFB360979A}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - GUIDebug|Win32 = GUIDebug|Win32 - GUIRelease|Win32 = GUIRelease|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {B9F10DBD-C3E7-4312-B357-0FEFB360979A}.Debug|Win32.ActiveCfg = Debug|Win32 - {B9F10DBD-C3E7-4312-B357-0FEFB360979A}.Debug|Win32.Build.0 = Debug|Win32 - {B9F10DBD-C3E7-4312-B357-0FEFB360979A}.GUIDebug|Win32.ActiveCfg = GUIDebug|Win32 - {B9F10DBD-C3E7-4312-B357-0FEFB360979A}.GUIDebug|Win32.Build.0 = GUIDebug|Win32 - {B9F10DBD-C3E7-4312-B357-0FEFB360979A}.GUIRelease|Win32.ActiveCfg = GUIRelease|Win32 - {B9F10DBD-C3E7-4312-B357-0FEFB360979A}.GUIRelease|Win32.Build.0 = GUIRelease|Win32 - {B9F10DBD-C3E7-4312-B357-0FEFB360979A}.Release|Win32.ActiveCfg = Release|Win32 - {B9F10DBD-C3E7-4312-B357-0FEFB360979A}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/misc/source/fteqcc-src/dotnet2010/qcc.vcxproj b/misc/source/fteqcc-src/dotnet2010/qcc.vcxproj deleted file mode 100644 index abcd564d..00000000 --- a/misc/source/fteqcc-src/dotnet2010/qcc.vcxproj +++ /dev/null @@ -1,343 +0,0 @@ - - - - - Debug - Win32 - - - GUIDebug - Win32 - - - GUIRelease - Win32 - - - Release - Win32 - - - - {B9F10DBD-C3E7-4312-B357-0FEFB360979A} - - - - Application - false - MultiByte - - - Application - false - MultiByte - - - Application - false - MultiByte - - - Application - false - MultiByte - - - - - - - - - - - - - - - - - - - - - - - <_ProjectFileVersion>10.0.30319.1 - .\Debug\ - .\Debug\ - true - .\qcc___Win32_GUIDebug\ - .\qcc___Win32_GUIDebug\ - true - .\qcc___Win32_GUIRelease0\ - .\qcc___Win32_GUIRelease0\ - false - .\Release\ - .\Release\ - false - AllRules.ruleset - - - AllRules.ruleset - - - AllRules.ruleset - - - AllRules.ruleset - - - - - - .\Debug/qcc.tlb - - - - - Disabled - %(PreprocessorDefinitions) - true - EnableFastChecks - MultiThreadedDebug - .\Debug/qcc.pch - .\Debug/ - .\Debug/ - .\Debug/ - true - Level3 - true - EditAndContinue - - - _DEBUG;%(PreprocessorDefinitions) - 0x0809 - - - ../../libs/zlib.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - ../../fteqcc_dbg.exe - true - true - .\Debug/fteqcc_dbg.pdb - Console - false - - - MachineX86 - - - true - .\Debug/qcc.bsc - - - - - .\qcc___Win32_GUIDebug/qcc.tlb - - - - - Disabled - %(PreprocessorDefinitions) - true - EnableFastChecks - MultiThreadedDebug - .\qcc___Win32_GUIDebug/qcc.pch - .\qcc___Win32_GUIDebug/ - .\qcc___Win32_GUIDebug/ - .\qcc___Win32_GUIDebug/ - true - Level3 - true - EditAndContinue - - - _DEBUG;%(PreprocessorDefinitions) - 0x0809 - - - ../../libs/zlib.lib;odbc32.lib;odbccp32.lib;comctl32.lib;%(AdditionalDependencies) - ..\..\fteqcc.exe - true - true - .\qcc___Win32_GUIDebug/fteqcc.pdb - Windows - false - - - MachineX86 - - - true - .\qcc___Win32_GUIDebug/qcc.bsc - - - - - .\qcc___Win32_GUIRelease0/qcc.tlb - - - - - MaxSpeed - OnlyExplicitInline - %(PreprocessorDefinitions) - true - MultiThreaded - true - .\qcc___Win32_GUIRelease0/qcc.pch - .\qcc___Win32_GUIRelease0/ - .\qcc___Win32_GUIRelease0/ - .\qcc___Win32_GUIRelease0/ - true - Level3 - true - - - NDEBUG;%(PreprocessorDefinitions) - 0x0809 - - - ../../libs/zlib.lib;odbc32.lib;odbccp32.lib;comctl32.lib;%(AdditionalDependencies) - ../../fteqccgui.exe - true - .\qcc___Win32_GUIRelease0/fteqccgui.pdb - Windows - false - - - MachineX86 - - - true - .\qcc___Win32_GUIRelease0/qcc.bsc - - - - - .\Release/qcc.tlb - - - - - MaxSpeed - OnlyExplicitInline - %(PreprocessorDefinitions) - true - MultiThreaded - true - .\Release/qcc.pch - .\Release/ - .\Release/ - .\Release/ - Level3 - true - - - NDEBUG;%(PreprocessorDefinitions) - 0x0809 - - - ../../libs/zlib.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - ../../fteqcc.exe - true - .\Release/fteqcc.pdb - Console - false - - - MachineX86 - - - true - .\Release/qcc.bsc - - - - - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - - - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - - - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - - - true - %(PreprocessorDefinitions) - true - %(PreprocessorDefinitions) - true - %(PreprocessorDefinitions) - true - %(PreprocessorDefinitions) - - - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - - - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - - - true - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - true - %(PreprocessorDefinitions) - - - true - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - true - %(PreprocessorDefinitions) - - - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - - - %(PreprocessorDefinitions) - true - %(PreprocessorDefinitions) - true - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - - - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - - - - - - - - - - - \ No newline at end of file diff --git a/misc/source/fteqcc-src/execloop.h b/misc/source/fteqcc-src/execloop.h deleted file mode 100644 index 208a79b3..00000000 --- a/misc/source/fteqcc-src/execloop.h +++ /dev/null @@ -1,1202 +0,0 @@ -//qc execution code. -//we have two conditions. -//one allows us to debug and trace through our code, the other doesn't. - -//hopefully, the compiler will do a great job at optimising this code for us, where required. -//if it dosn't, then bum. - -//the general overhead should be reduced significantly, and I would be supprised if it did run slower. - -//run away loops are checked for ONLY on gotos and function calls. This might give a poorer check, but it will run faster overall. - -//Appears to work fine. - -#if INTSIZE == 16 -#define cont cont16 -#define reeval reeval16 -#define st st16 -#define pr_statements pr_statements16 -#define fakeop fakeop16 -#define dstatement_t dstatement16_t -#define sofs signed short -#define uofs unsigned short -#elif INTSIZE == 32 -#define cont cont32 -#define reeval reeval32 -#define st st32 -#define pr_statements pr_statements32 -#define fakeop fakeop32 -#define dstatement_t dstatement32_t -#define sofs signed int -#define uofs unsigned int -#elif INTSIZE == 24 -#error INTSIZE should be set to 32. -#else -#error Bad cont size -#endif - -#ifdef DEBUGABLE -#define OPCODE (st->op & ~0x8000) -#else -#define OPCODE (st->op) -#endif - -#define ENGINEPOINTER(p) ((char*)(p) - progfuncs->stringtable) -#define QCPOINTER(p) (eval_t *)(p->_int+progfuncs->stringtable) -#define QCPOINTERM(p) (eval_t *)((p)+progfuncs->stringtable) - -//rely upon just st -{ -#ifdef DEBUGABLE -cont: //last statement may have been a breakpoint - s = st-pr_statements; - s+=1; - s=ShowStep(progfuncs, s); - st = pr_statements + s; - -reeval: -#else - st++; -#endif - switch (OPCODE) - { - case OP_ADD_F: - OPC->_float = OPA->_float + OPB->_float; - break; - case OP_ADD_V: - OPC->_vector[0] = OPA->_vector[0] + OPB->_vector[0]; - OPC->_vector[1] = OPA->_vector[1] + OPB->_vector[1]; - OPC->_vector[2] = OPA->_vector[2] + OPB->_vector[2]; - break; - - case OP_SUB_F: - OPC->_float = OPA->_float - OPB->_float; - break; - case OP_SUB_V: - OPC->_vector[0] = OPA->_vector[0] - OPB->_vector[0]; - OPC->_vector[1] = OPA->_vector[1] - OPB->_vector[1]; - OPC->_vector[2] = OPA->_vector[2] - OPB->_vector[2]; - break; - - case OP_MUL_F: - OPC->_float = OPA->_float * OPB->_float; - break; - case OP_MUL_V: - OPC->_float = OPA->_vector[0]*OPB->_vector[0] - + OPA->_vector[1]*OPB->_vector[1] - + OPA->_vector[2]*OPB->_vector[2]; - break; - case OP_MUL_FV: - OPC->_vector[0] = OPA->_float * OPB->_vector[0]; - OPC->_vector[1] = OPA->_float * OPB->_vector[1]; - OPC->_vector[2] = OPA->_float * OPB->_vector[2]; - break; - case OP_MUL_VF: - OPC->_vector[0] = OPB->_float * OPA->_vector[0]; - OPC->_vector[1] = OPB->_float * OPA->_vector[1]; - OPC->_vector[2] = OPB->_float * OPA->_vector[2]; - break; - - case OP_DIV_F: - OPC->_float = OPA->_float / OPB->_float; - break; - case OP_DIV_VF: - OPC->_vector[0] = OPB->_float / OPA->_vector[0]; - OPC->_vector[1] = OPB->_float / OPA->_vector[1]; - OPC->_vector[2] = OPB->_float / OPA->_vector[2]; - break; - - case OP_BITAND_F: - OPC->_float = (float)((int)OPA->_float & (int)OPB->_float); - break; - - case OP_BITOR_F: - OPC->_float = (float)((int)OPA->_float | (int)OPB->_float); - break; - - - case OP_GE_F: - OPC->_float = (float)(OPA->_float >= OPB->_float); - break; - case OP_GE_I: - OPC->_int = (int)(OPA->_int >= OPB->_int); - break; - case OP_GE_IF: - OPC->_float = (float)(OPA->_int >= OPB->_float); - break; - case OP_GE_FI: - OPC->_float = (float)(OPA->_float >= OPB->_int); - break; - - case OP_LE_F: - OPC->_float = (float)(OPA->_float <= OPB->_float); - break; - case OP_LE_I: - OPC->_int = (int)(OPA->_int <= OPB->_int); - break; - case OP_LE_IF: - OPC->_float = (float)(OPA->_int <= OPB->_float); - break; - case OP_LE_FI: - OPC->_float = (float)(OPA->_float <= OPB->_int); - break; - - case OP_GT_F: - OPC->_float = (float)(OPA->_float > OPB->_float); - break; - case OP_GT_I: - OPC->_int = (int)(OPA->_int > OPB->_int); - break; - case OP_GT_IF: - OPC->_float = (float)(OPA->_int > OPB->_float); - break; - case OP_GT_FI: - OPC->_float = (float)(OPA->_float > OPB->_int); - break; - - case OP_LT_F: - OPC->_float = (float)(OPA->_float < OPB->_float); - break; - case OP_LT_I: - OPC->_int = (int)(OPA->_int < OPB->_int); - break; - case OP_LT_IF: - OPC->_float = (float)(OPA->_int < OPB->_float); - break; - case OP_LT_FI: - OPC->_float = (float)(OPA->_float < OPB->_int); - break; - - case OP_AND_F: - OPC->_float = (float)(OPA->_float && OPB->_float); - break; - case OP_OR_F: - OPC->_float = (float)(OPA->_float || OPB->_float); - break; - - case OP_NOT_F: - OPC->_float = (float)(!OPA->_float); - break; - case OP_NOT_V: - OPC->_float = (float)(!OPA->_vector[0] && !OPA->_vector[1] && !OPA->_vector[2]); - break; - case OP_NOT_S: - OPC->_float = (float)(!(OPA->string) || !*PR_StringToNative(progfuncs, OPA->string)); - break; - case OP_NOT_FNC: - OPC->_float = (float)(!(OPA->function & ~0xff000000)); - break; - case OP_NOT_ENT: - OPC->_float = (float)(PROG_TO_EDICT(progfuncs, OPA->edict) == (edictrun_t *)sv_edicts); - break; - - case OP_EQ_F: - OPC->_float = (float)(OPA->_float == OPB->_float); - break; - case OP_EQ_IF: - OPC->_float = (float)(OPA->_int == OPB->_float); - break; - case OP_EQ_FI: - OPC->_float = (float)(OPA->_float == OPB->_int); - break; - - - case OP_EQ_V: - OPC->_float = (float)((OPA->_vector[0] == OPB->_vector[0]) && - (OPA->_vector[1] == OPB->_vector[1]) && - (OPA->_vector[2] == OPB->_vector[2])); - break; - case OP_EQ_S: - if (OPA->string==OPB->string) - OPC->_float = true; - else if (!OPA->string) - { - if (!OPB->string || !*PR_StringToNative(progfuncs, OPB->string)) - OPC->_float = true; - else - OPC->_float = false; - } - else if (!OPB->string) - { - if (!OPA->string || !*PR_StringToNative(progfuncs, OPA->string)) - OPC->_float = true; - else - OPC->_float = false; - } - else - OPC->_float = (float)(!strcmp(PR_StringToNative(progfuncs, OPA->string),PR_StringToNative(progfuncs, OPB->string))); - break; - case OP_EQ_E: - OPC->_float = (float)(OPA->_int == OPB->_int); - break; - case OP_EQ_FNC: - OPC->_float = (float)(OPA->function == OPB->function); - break; - - - case OP_NE_F: - OPC->_float = (float)(OPA->_float != OPB->_float); - break; - case OP_NE_V: - OPC->_float = (float)((OPA->_vector[0] != OPB->_vector[0]) || - (OPA->_vector[1] != OPB->_vector[1]) || - (OPA->_vector[2] != OPB->_vector[2])); - break; - case OP_NE_S: - if (OPA->string==OPB->string) - OPC->_float = false; - else if (!OPA->string) - { - if (!OPB->string || !*(PR_StringToNative(progfuncs, OPB->string))) - OPC->_float = false; - else - OPC->_float = true; - } - else if (!OPB->string) - { - if (!OPA->string || !*PR_StringToNative(progfuncs, OPA->string)) - OPC->_float = false; - else - OPC->_float = true; - } - else - OPC->_float = (float)(strcmp(PR_StringToNative(progfuncs, OPA->string),PR_StringToNative(progfuncs, OPB->string))); - break; - case OP_NE_E: - OPC->_float = (float)(OPA->_int != OPB->_int); - break; - case OP_NE_FNC: - OPC->_float = (float)(OPA->function != OPB->function); - break; - -//================== - case OP_STORE_IF: - OPB->_float = (float)OPA->_int; - break; - case OP_STORE_FI: - OPB->_int = (int)OPA->_float; - break; - - case OP_STORE_F: - case OP_STORE_ENT: - case OP_STORE_FLD: // integers - case OP_STORE_S: - case OP_STORE_I: - case OP_STORE_FNC: // pointers - case OP_STORE_P: - OPB->_int = OPA->_int; - break; - case OP_STORE_V: - OPB->_vector[0] = OPA->_vector[0]; - OPB->_vector[1] = OPA->_vector[1]; - OPB->_vector[2] = OPA->_vector[2]; - break; - - //store a value to a pointer - case OP_STOREP_IF: - if ((unsigned int)OPB->_int >= prinst->addressableused) - { - pr_xstatement = st-pr_statements; - PR_RunError (progfuncs, "bad pointer write in %s", PR_StringToNative(progfuncs, pr_xfunction->s_name)); - } - ptr = QCPOINTER(OPB); - ptr->_float = (float)OPA->_int; - break; - case OP_STOREP_FI: - if ((unsigned int)OPB->_int >= prinst->addressableused) - { - pr_xstatement = st-pr_statements; - PR_RunError (progfuncs, "bad pointer write in %s", PR_StringToNative(progfuncs, pr_xfunction->s_name)); - } - ptr = QCPOINTER(OPB); - ptr->_int = (int)OPA->_float; - break; - case OP_STOREP_I: - case OP_GSTOREP_I: - if ((unsigned int)OPB->_int >= prinst->addressableused) - { - pr_xstatement = st-pr_statements; - PR_RunError (progfuncs, "bad pointer write in %s", PR_StringToNative(progfuncs, pr_xfunction->s_name)); - } - ptr = QCPOINTER(OPB); - ptr->_int = OPA->_int; - break; - case OP_STOREP_F: - case OP_GSTOREP_F: - case OP_STOREP_ENT: - case OP_GSTOREP_ENT: - case OP_STOREP_FLD: // integers - case OP_GSTOREP_FLD: - case OP_STOREP_S: - case OP_GSTOREP_S: - case OP_STOREP_FNC: // pointers - case OP_GSTOREP_FNC: - if ((unsigned int)OPB->_int >= prinst->addressableused) - { - pr_xstatement = st-pr_statements; - PR_RunError (progfuncs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(progfuncs, pr_xfunction->s_name), OPB->_int, prinst->addressableused); - } - ptr = QCPOINTER(OPB); - ptr->_int = OPA->_int; - break; - case OP_STOREP_V: - case OP_GSTOREP_V: - if ((unsigned int)OPB->_int >= prinst->addressableused) - { - pr_xstatement = st-pr_statements; - PR_RunError (progfuncs, "bad pointer write in %s", PR_StringToNative(progfuncs, pr_xfunction->s_name)); - } - ptr = QCPOINTER(OPB); - ptr->_vector[0] = OPA->_vector[0]; - ptr->_vector[1] = OPA->_vector[1]; - ptr->_vector[2] = OPA->_vector[2]; - break; - - case OP_STOREP_C: //store character in a string - if ((unsigned int)OPB->_int >= prinst->addressableused) - { - pr_xstatement = st-pr_statements; - PR_RunError (progfuncs, "bad pointer write in %s", PR_StringToNative(progfuncs, pr_xfunction->s_name)); - } - ptr = QCPOINTER(OPB); - *(unsigned char *)ptr = (char)OPA->_float; - break; - - case OP_MULSTORE_F: // f *= f - OPB->_float *= OPA->_float; - break; - case OP_MULSTORE_VF: // v *= f - OPB->_vector[0] *= OPA->_float; - OPB->_vector[1] *= OPA->_float; - OPB->_vector[2] *= OPA->_float; - break; - case OP_MULSTOREP_F: // e.f *= f - if ((unsigned int)OPB->_int >= prinst->addressableused) - { - pr_xstatement = st-pr_statements; - PR_RunError (progfuncs, "bad pointer write in %s", PR_StringToNative(progfuncs, pr_xfunction->s_name)); - } - ptr = QCPOINTER(OPB); - OPC->_float = (ptr->_float *= OPA->_float); - break; - case OP_MULSTOREP_VF: // e.v *= f - if ((unsigned int)OPB->_int >= prinst->addressableused) - { - pr_xstatement = st-pr_statements; - PR_RunError (progfuncs, "bad pointer write in %s", PR_StringToNative(progfuncs, pr_xfunction->s_name)); - } - ptr = QCPOINTER(OPB); - OPC->_vector[0] = (ptr->_vector[0] *= OPA->_float); - OPC->_vector[0] = (ptr->_vector[1] *= OPA->_float); - OPC->_vector[0] = (ptr->_vector[2] *= OPA->_float); - break; - - case OP_DIVSTORE_F: // f /= f - OPB->_float /= OPA->_float; - break; - case OP_DIVSTOREP_F: // e.f /= f - if ((unsigned int)OPB->_int >= prinst->addressableused) - { - pr_xstatement = st-pr_statements; - PR_RunError (progfuncs, "bad pointer write in %s", PR_StringToNative(progfuncs, pr_xfunction->s_name)); - } - ptr = QCPOINTER(OPB); - OPC->_float = (ptr->_float /= OPA->_float); - break; - - case OP_ADDSTORE_F: // f += f - OPB->_float += OPA->_float; - break; - case OP_ADDSTORE_V: // v += v - OPB->_vector[0] += OPA->_vector[0]; - OPB->_vector[1] += OPA->_vector[1]; - OPB->_vector[2] += OPA->_vector[2]; - break; - case OP_ADDSTOREP_F: // e.f += f - if ((unsigned int)OPB->_int >= prinst->addressableused) - { - pr_xstatement = st-pr_statements; - PR_RunError (progfuncs, "bad pointer write in %s", PR_StringToNative(progfuncs, pr_xfunction->s_name)); - } - ptr = QCPOINTER(OPB); - OPC->_float = (ptr->_float += OPA->_float); - break; - case OP_ADDSTOREP_V: // e.v += v - if ((unsigned int)OPB->_int >= prinst->addressableused) - { - pr_xstatement = st-pr_statements; - PR_RunError (progfuncs, "bad pointer write in %s", PR_StringToNative(progfuncs, pr_xfunction->s_name)); - } - ptr = QCPOINTER(OPB); - OPC->_vector[0] = (ptr->_vector[0] += OPA->_vector[0]); - OPC->_vector[1] = (ptr->_vector[1] += OPA->_vector[1]); - OPC->_vector[2] = (ptr->_vector[2] += OPA->_vector[2]); - break; - - case OP_SUBSTORE_F: // f -= f - OPB->_float -= OPA->_float; - break; - case OP_SUBSTORE_V: // v -= v - OPB->_vector[0] -= OPA->_vector[0]; - OPB->_vector[1] -= OPA->_vector[1]; - OPB->_vector[2] -= OPA->_vector[2]; - break; - case OP_SUBSTOREP_F: // e.f -= f - if ((unsigned int)OPB->_int >= prinst->addressableused) - { - pr_xstatement = st-pr_statements; - PR_RunError (progfuncs, "bad pointer write in %s", PR_StringToNative(progfuncs, pr_xfunction->s_name)); - } - ptr = QCPOINTER(OPB); - OPC->_float = (ptr->_float -= OPA->_float); - break; - case OP_SUBSTOREP_V: // e.v -= v - if ((unsigned int)OPB->_int >= prinst->addressableused) - { - pr_xstatement = st-pr_statements; - PR_RunError (progfuncs, "bad pointer write in %s", PR_StringToNative(progfuncs, pr_xfunction->s_name)); - } - ptr = QCPOINTER(OPB); - OPC->_vector[0] = (ptr->_vector[0] -= OPA->_vector[0]); - OPC->_vector[1] = (ptr->_vector[1] -= OPA->_vector[1]); - OPC->_vector[2] = (ptr->_vector[2] -= OPA->_vector[2]); - break; - - - //get a pointer to a field var - case OP_ADDRESS: - if ((unsigned)OPA->edict >= (unsigned)maxedicts) - { -#ifndef DEBUGABLE - pr_trace++; - printf("OP_ADDRESS references invalid entity in %s", PR_StringToNative(progfuncs, pr_xfunction->s_name)); - st--; - goto cont; -#else - PR_RunError (progfuncs, "OP_ADDRESS references invalid entity in %s", PR_StringToNative(progfuncs, pr_xfunction->s_name)); -#endif - } - ed = PROG_TO_EDICT(progfuncs, OPA->edict); -#ifdef PARANOID - NUM_FOR_EDICT(ed); // make sure it's in range -#endif - if (!ed || ed->readonly) - { - pr_xstatement = st-pr_statements; -#ifndef DEBUGABLE - //boot it over to the debugger - pr_trace++; - printf("assignment to read-only entity in %s", PR_StringToNative(progfuncs, pr_xfunction->s_name)); - st--; - goto cont; -#else - { - ddef16_t *d16; - fdef_t *f; - d16 = ED_GlobalAtOfs16(progfuncs, st->a); - f = ED_FieldAtOfs(progfuncs, OPB->_int + progfuncs->fieldadjust); - printf ("assignment to read-only entity in %s (%s.%s)\n", PR_StringToNative(progfuncs, pr_xfunction->s_name), d16?PR_StringToNative(progfuncs, d16->s_name):NULL, f?f->name:NULL); - } -#endif - } - -//Whilst the next block would technically be correct, we don't use it as it breaks too many quake mods. -// if (ed->isfree) -// { -// pr_xstatement = st-pr_statements; -// PR_RunError (progfuncs, "assignment to free entitiy in %s", PR_StringToNative(progfuncs, pr_xfunction->s_name)); -// } - OPC->_int = ENGINEPOINTER((((int *)edvars(ed)) + OPB->_int + progfuncs->fieldadjust)); - break; - - //load a field to a value - case OP_LOAD_I: - case OP_LOAD_F: - case OP_LOAD_FLD: - case OP_LOAD_ENT: - case OP_LOAD_S: - case OP_LOAD_FNC: - if ((unsigned)OPA->edict >= (unsigned)maxedicts) - PR_RunError (progfuncs, "OP_LOAD references invalid entity in %s", PR_StringToNative(progfuncs, pr_xfunction->s_name)); - ed = PROG_TO_EDICT(progfuncs, OPA->edict); -#ifdef PARANOID - NUM_FOR_EDICT(ed); // make sure it's in range -#endif - ptr = (eval_t *)(((int *)edvars(ed)) + OPB->_int + progfuncs->fieldadjust); - OPC->_int = ptr->_int; - break; - - case OP_LOAD_V: - if ((unsigned)OPA->edict >= (unsigned)maxedicts) - PR_RunError (progfuncs, "OP_LOAD_V references invalid entity in %s", PR_StringToNative(progfuncs, pr_xfunction->s_name)); - ed = PROG_TO_EDICT(progfuncs, OPA->edict); -#ifdef PARANOID - NUM_FOR_EDICT(ed); // make sure it's in range -#endif - ptr = (eval_t *)(((int *)edvars(ed)) + OPB->_int + progfuncs->fieldadjust); - OPC->_vector[0] = ptr->_vector[0]; - OPC->_vector[1] = ptr->_vector[1]; - OPC->_vector[2] = ptr->_vector[2]; - break; - -//================== - - case OP_IFNOT_S: - RUNAWAYCHECK(); - if (!OPA->string || !PR_StringToNative(progfuncs, OPA->string)) - st += (sofs)st->b - 1; // offset the s++ - break; - - case OP_IFNOT_F: - RUNAWAYCHECK(); - if (!OPA->_float) - st += (sofs)st->b - 1; // offset the s++ - break; - - case OP_IFNOT_I: - RUNAWAYCHECK(); - if (!OPA->_int) - st += (sofs)st->b - 1; // offset the s++ - break; - - case OP_IF_S: - RUNAWAYCHECK(); - if (OPA->string && PR_StringToNative(progfuncs, OPA->string)) - st += (sofs)st->b - 1; // offset the s++ - break; - - case OP_IF_F: - RUNAWAYCHECK(); - if (OPA->_int) - st += (sofs)st->b - 1; // offset the s++ - break; - - case OP_IF_I: - RUNAWAYCHECK(); - if (OPA->_int) - st += (sofs)st->b - 1; // offset the s++ - break; - - case OP_GOTO: - RUNAWAYCHECK(); - st += (sofs)st->a - 1; // offset the s++ - break; - - case OP_CALL8H: - case OP_CALL7H: - case OP_CALL6H: - case OP_CALL5H: - case OP_CALL4H: - case OP_CALL3H: - case OP_CALL2H: - G_VECTOR(OFS_PARM1)[0] = OPC->_vector[0]; - G_VECTOR(OFS_PARM1)[1] = OPC->_vector[1]; - G_VECTOR(OFS_PARM1)[2] = OPC->_vector[2]; - case OP_CALL1H: - G_VECTOR(OFS_PARM0)[0] = OPB->_vector[0]; - G_VECTOR(OFS_PARM0)[1] = OPB->_vector[1]; - G_VECTOR(OFS_PARM0)[2] = OPB->_vector[2]; - - case OP_CALL8: - case OP_CALL7: - case OP_CALL6: - case OP_CALL5: - case OP_CALL4: - case OP_CALL3: - case OP_CALL2: - case OP_CALL1: - case OP_CALL0: - RUNAWAYCHECK(); - pr_xstatement = st-pr_statements; - - if (OPCODE > OP_CALL8) - pr_argc = OPCODE - (OP_CALL1H-1); - else - pr_argc = OPCODE - OP_CALL0; - fnum = OPA->function; - if ((fnum & ~0xff000000)==0) - { - PR_RunError(progfuncs, "NULL function from qc (%s).\n", PR_StringToNative(progfuncs, pr_xfunction->s_name)); -#ifndef DEBUGABLE - goto cont; -#endif - break; - } -/* -{ - static char buffer[1024*1024*8]; - int size = sizeof buffer; - progfuncs->save_ents(progfuncs, buffer, &size, 0); -}*/ - - { - int callerprogs=pr_typecurrent; -//about to switch. needs caching. - - //if it's an external call, switch now (before any function pointers are used) - PR_MoveParms(progfuncs, (fnum & 0xff000000)>>24, callerprogs); - PR_SwitchProgs(progfuncs, (fnum & 0xff000000)>>24); - - newf = &pr_functions[fnum & ~0xff000000]; - - if (newf->first_statement < 0) - { // negative statements are built in functions - /*calling a builtin in another progs may affect that other progs' globals instead, is the theory anyway, so args and stuff need to move over*/ - if (pr_typecurrent != 0) - { - PR_MoveParms(progfuncs, 0, pr_typecurrent); - PR_SwitchProgs(progfuncs, 0); - } - i = -newf->first_statement; -// p = pr_typecurrent; - progfuncs->lastcalledbuiltinnumber = i; - if (i < externs->numglobalbuiltins) - { - prinst->numtempstringsstack = prinst->numtempstrings; - (*externs->globalbuiltins[i]) (progfuncs, (struct globalvars_s *)current_progstate->globals); - if (prinst->continuestatement!=-1) - { - st=&pr_statements[prinst->continuestatement]; - prinst->continuestatement=-1; - break; - } - } - else - { - i -= externs->numglobalbuiltins; - if (i >= current_progstate->numbuiltins) - { -// if (newf->first_statement == -0x7fffffff) -// ((builtin_t)newf->profile) (progfuncs, (struct globalvars_s *)current_progstate->globals); -// else - PR_RunError (progfuncs, "Bad builtin call number - %i", -newf->first_statement); - } - else - current_progstate->builtins [i] (progfuncs, (struct globalvars_s *)current_progstate->globals); - } - PR_MoveParms(progfuncs, callerprogs, pr_typecurrent); -// memcpy(&pr_progstate[p].globals[OFS_RETURN], ¤t_progstate->globals[OFS_RETURN], sizeof(vec3_t)); - PR_SwitchProgs(progfuncs, (progsnum_t)callerprogs); - -//#ifndef DEBUGABLE //decide weather non debugger wants to start debugging. - s = st-pr_statements; - goto restart; -//#endif -// break; - } -// PR_MoveParms((OPA->function & 0xff000000)>>24, pr_typecurrent); -// PR_SwitchProgs((OPA->function & 0xff000000)>>24); - s = PR_EnterFunction (progfuncs, newf, callerprogs); - st = &pr_statements[s]; - } - - goto restart; -// break; - - case OP_DONE: - case OP_RETURN: - - RUNAWAYCHECK(); - - pr_globals[OFS_RETURN] = pr_globals[st->a]; - pr_globals[OFS_RETURN+1] = pr_globals[st->a+1]; - pr_globals[OFS_RETURN+2] = pr_globals[st->a+2]; -/* -{ - static char buffer[1024*1024*8]; - int size = sizeof buffer; - progfuncs->save_ents(progfuncs, buffer, &size, 0); -} -*/ - s = PR_LeaveFunction (progfuncs); - st = &pr_statements[s]; - if (pr_depth == prinst->exitdepth) - { - return; // all done - } - goto restart; -// break; - - case OP_STATE: - externs->stateop(progfuncs, OPA->_float, OPB->function); - break; - - case OP_ADD_I: - OPC->_int = OPA->_int + OPB->_int; - break; - case OP_ADD_FI: - OPC->_float = OPA->_float + (float)OPB->_int; - break; - case OP_ADD_IF: - OPC->_float = (float)OPA->_int + OPB->_float; - break; - - case OP_SUB_I: - OPC->_int = OPA->_int - OPB->_int; - break; - case OP_SUB_FI: - OPC->_float = OPA->_float - (float)OPB->_int; - break; - case OP_SUB_IF: - OPC->_float = (float)OPA->_int - OPB->_float; - break; - - case OP_CONV_ITOF: - OPC->_float = (float)OPA->_int; - break; - case OP_CONV_FTOI: - OPC->_int = (int)OPA->_float; - break; - - case OP_CP_ITOF: - ptr = (eval_t *)(((qbyte *)sv_edicts) + OPA->_int); - OPC->_float = (float)ptr->_int; - break; - - case OP_CP_FTOI: - ptr = (eval_t *)(((qbyte *)sv_edicts) + OPA->_int); - OPC->_int = (int)ptr->_float; - break; - - case OP_BITAND_I: - OPC->_int = (OPA->_int & OPB->_int); - break; - - case OP_BITOR_I: - OPC->_int = (OPA->_int | OPB->_int); - break; - - case OP_MUL_I: - OPC->_int = OPA->_int * OPB->_int; - break; - case OP_DIV_I: - if (OPB->_int == 0) //no division by zero allowed... - OPC->_int = 0; - else - OPC->_int = OPA->_int / OPB->_int; - break; - case OP_EQ_I: - OPC->_int = (OPA->_int == OPB->_int); - break; - case OP_NE_I: - OPC->_int = (OPA->_int != OPB->_int); - break; - - - //array/structure reading/writing. - case OP_GLOBALADDRESS: - OPC->_int = ENGINEPOINTER(&OPA->_int + OPB->_int); /*pointer arithmatic*/ - break; - case OP_POINTER_ADD: //pointer to 32 bit (remember to *3 for vectors) - OPC->_int = OPA->_int + OPB->_int*4; - break; - - case OP_LOADA_I: - case OP_LOADA_F: - case OP_LOADA_FLD: - case OP_LOADA_ENT: - case OP_LOADA_S: - case OP_LOADA_FNC: - ptr = (eval_t *)(&OPA->_int + OPB->_int); /*pointer arithmatic*/ - OPC->_int = ptr->_int; - break; - - case OP_LOADA_V: - ptr = (eval_t *)(&OPA->_int + OPB->_int); - OPC->_vector[0] = ptr->_vector[0]; - OPC->_vector[1] = ptr->_vector[1]; - OPC->_vector[2] = ptr->_vector[2]; - break; - - - - case OP_ADD_SF: //(char*)c = (char*)a + (float)b - OPC->_int = OPA->_int + (int)OPB->_float; - break; - case OP_SUB_S: //(float)c = (char*)a - (char*)b - OPC->_int = OPA->_int - OPB->_int; - break; - case OP_LOADP_C: //load character from a string - i = (unsigned int)OPA->_int + (unsigned int)OPB->_float; - if ((unsigned int)i >= prinst->addressableused) - { - i = (unsigned int)OPB->_float; - ptr = (eval_t*)PR_StringToNative(progfuncs, OPA->_int); - if ((size_t)i > strlen((char*)ptr)) - { - pr_xstatement = st-pr_statements; - PR_RunError (progfuncs, "bad pointer read in %s (%i bytes into %s)", PR_StringToNative(progfuncs, pr_xfunction->s_name), i, ptr); - } - ptr = (eval_t*)((char*)ptr + i); - } - else - ptr = QCPOINTERM(i); - OPC->_float = *(unsigned char *)ptr; - break; - case OP_LOADP_I: - case OP_LOADP_F: - case OP_LOADP_FLD: - case OP_LOADP_ENT: - case OP_LOADP_S: - case OP_LOADP_FNC: - i = OPA->_int + OPB->_int*4; - if ((unsigned int)i >= prinst->addressableused) - { - pr_xstatement = st-pr_statements; - PR_RunError (progfuncs, "bad pointer read in %s", PR_StringToNative(progfuncs, pr_xfunction->s_name)); - } - ptr = QCPOINTERM(OPA->_int + OPB->_int*4); - OPC->_int = ptr->_int; - break; - - case OP_LOADP_V: - i = OPA->_int + OPB->_int*4; - if ((unsigned int)i >= prinst->addressableused) - { - pr_xstatement = st-pr_statements; - PR_RunError (progfuncs, "bad pointer read in %s", PR_StringToNative(progfuncs, pr_xfunction->s_name)); - } - ptr = QCPOINTERM(i); - OPC->_vector[0] = ptr->_vector[0]; - OPC->_vector[1] = ptr->_vector[1]; - OPC->_vector[2] = ptr->_vector[2]; - break; - - case OP_XOR_I: - OPC->_int = OPA->_int ^ OPB->_int; - break; - case OP_RSHIFT_I: - OPC->_int = OPA->_int >> OPB->_int; - break; - case OP_LSHIFT_I: - OPC->_int = OPA->_int << OPB->_int; - break; - - - case OP_FETCH_GBL_F: - case OP_FETCH_GBL_S: - case OP_FETCH_GBL_E: - case OP_FETCH_GBL_FNC: - i = (int)OPB->_float; - if(i < 0 || i > ((eval_t *)&glob[st->a-1])->_int) - { - PR_RunError(progfuncs, "array index out of bounds: %s[%d] (max %d)", PR_GlobalStringNoContents(progfuncs, st->a), i, ((eval_t *)&glob[st->a-1])->_int); - } - t = (eval_t *)&pr_globals[(uofs)st->a + i]; - OPC->_int = t->_int; - break; - case OP_FETCH_GBL_V: - i = (int)OPB->_float; - if(i < 0 || i > ((eval_t *)&glob[st->a-1])->_int) - { - PR_RunError(progfuncs, "array index out of bounds: %s[%d]", PR_GlobalStringNoContents(progfuncs, st->a), i); - } - t = (eval_t *)&pr_globals[(uofs)st->a + i*3]; - OPC->_vector[0] = t->_vector[0]; - OPC->_vector[1] = t->_vector[1]; - OPC->_vector[2] = t->_vector[2]; - break; - - case OP_CSTATE: - externs->cstateop(progfuncs, OPA->_float, OPB->_float, fnum); - break; - - case OP_CWSTATE: - externs->cwstateop(progfuncs, OPA->_float, OPB->_float, fnum); - break; - - case OP_THINKTIME: - externs->thinktimeop(progfuncs, (struct edict_s *)PROG_TO_EDICT(progfuncs, OPA->edict), OPB->_float); - break; - - - case OP_BITSET: // b (+) a - OPB->_float = (float)((int)OPB->_float | (int)OPA->_float); - break; - case OP_BITSETP: // .b (+) a - if ((unsigned int)OPB->_int >= prinst->addressableused) - { - pr_xstatement = st-pr_statements; - PR_RunError (progfuncs, "bad pointer write in %s", PR_StringToNative(progfuncs, pr_xfunction->s_name)); - } - ptr = QCPOINTER(OPB); - ptr->_float = (float)((int)ptr->_float | (int)OPA->_float); - break; - case OP_BITCLR: // b (-) a - OPB->_float = (float)((int)OPB->_float & ~((int)OPA->_float)); - break; - case OP_BITCLRP: // .b (-) a - if ((unsigned int)OPB->_int >= prinst->addressableused) - { - pr_xstatement = st-pr_statements; - PR_RunError (progfuncs, "bad pointer write in %s", PR_StringToNative(progfuncs, pr_xfunction->s_name)); - } - ptr = QCPOINTER(OPB); - ptr->_float = (float)((int)ptr->_float & ~((int)OPA->_float)); - break; - - case OP_RAND0: - OPC->_float = (rand()&0x7fff)/((float)0x7fff); - break; - case OP_RAND1: - OPC->_float = (rand()&0x7fff)/((float)0x7fff)*OPA->_float; - break; - case OP_RAND2: - if(OPA->_float < OPB->_float) - { - OPC->_float = OPA->_float+((rand()&0x7fff)/((float)0x7fff) - *(OPB->_float-OPA->_float)); - } - else - { - OPC->_float = OPB->_float+((rand()&0x7fff)/((float)0x7fff) - *(OPA->_float-OPB->_float)); - } - break; - case OP_RANDV0: - OPC->_vector[0] = (rand()&0x7fff)/((float)0x7fff); - OPC->_vector[1] = (rand()&0x7fff)/((float)0x7fff); - OPC->_vector[2] = (rand()&0x7fff)/((float)0x7fff); - break; - case OP_RANDV1: - OPC->_vector[0] = (rand()&0x7fff)/((float)0x7fff)*OPA->_vector[0]; - OPC->_vector[1] = (rand()&0x7fff)/((float)0x7fff)*OPA->_vector[1]; - OPC->_vector[2] = (rand()&0x7fff)/((float)0x7fff)*OPA->_vector[2]; - break; - case OP_RANDV2: - for(i = 0; i < 3; i++) - { - if(OPA->_vector[i] < OPB->_vector[i]) - { - OPC->_vector[i] = OPA->_vector[i]+((rand()&0x7fff)/((float)0x7fff) - *(OPB->_vector[i]-OPA->_vector[i])); - } - else - { - OPC->_vector[i] = OPB->_vector[i]+(rand()*(1.0f/RAND_MAX) - *(OPA->_vector[i]-OPB->_vector[i])); - } - } - break; - - - case OP_SWITCH_F: - case OP_SWITCH_V: - case OP_SWITCH_S: - case OP_SWITCH_E: - case OP_SWITCH_FNC: - swtch = OPA; - swtchtype = OPCODE; - RUNAWAYCHECK(); - st += (sofs)st->b - 1; // offset the st++ - break; - case OP_CASE: - switch(swtchtype) - { - case OP_SWITCH_F: - if (swtch->_float == OPA->_float) - { - RUNAWAYCHECK(); - st += (sofs)st->b-1; // -1 to offset the s++ - } - break; - case OP_SWITCH_E: - case OP_SWITCH_FNC: - if (swtch->_int == OPA->_int) - { - RUNAWAYCHECK(); - st += (sofs)st->b-1; // -1 to offset the s++ - } - break; - case OP_SWITCH_S: - if (swtch->_int == OPA->_int) - { - RUNAWAYCHECK(); - st += (sofs)st->b-1; // -1 to offset the s++ - } - if ((!swtch->_int && PR_StringToNative(progfuncs, OPA->string)) || (!OPA->_int && PR_StringToNative(progfuncs, swtch->string))) //one is null (cannot be not both). - break; - if (!strcmp(PR_StringToNative(progfuncs, swtch->string), PR_StringToNative(progfuncs, OPA->string))) - { - RUNAWAYCHECK(); - st += (sofs)st->b-1; // -1 to offset the s++ - } - break; - case OP_SWITCH_V: - if (swtch->_vector[0] == OPA->_vector[0] && swtch->_vector[1] == OPA->_vector[1] && swtch->_vector[2] == OPA->_vector[2]) - { - RUNAWAYCHECK(); - st += (sofs)st->b-1; // -1 to offset the s++ - } - break; - default: - PR_RunError (progfuncs, "OP_CASE with bad/missing OP_SWITCH %i", swtchtype); - break; - } - break; - case OP_CASERANGE: - switch(swtchtype) - { - case OP_SWITCH_F: - if (swtch->_float >= OPA->_float && swtch->_float <= OPB->_float) - { - RUNAWAYCHECK(); - st += (sofs)st->c-1; // -1 to offset the s++ - } - break; - default: - PR_RunError (progfuncs, "OP_CASERANGE with bad/missing OP_SWITCH %i", swtchtype); - } - break; - - - - - - - - - - case OP_BITAND_IF: - OPC->_int = (OPA->_int & (int)OPB->_float); - break; - case OP_BITOR_IF: - OPC->_int = (OPA->_int | (int)OPB->_float); - break; - case OP_BITAND_FI: - OPC->_int = ((int)OPA->_float & OPB->_int); - break; - case OP_BITOR_FI: - OPC->_int = ((int)OPA->_float | OPB->_int); - break; - - case OP_MUL_IF: - OPC->_float = (OPA->_int * OPB->_float); - break; - case OP_MUL_FI: - OPC->_float = (OPA->_float * OPB->_int); - break; - - case OP_MUL_VI: - OPC->_vector[0] = OPA->_vector[0] * OPB->_int; - OPC->_vector[1] = OPA->_vector[0] * OPB->_int; - OPC->_vector[2] = OPA->_vector[0] * OPB->_int; - break; - case OP_MUL_IV: - OPC->_vector[0] = OPB->_int * OPA->_vector[0]; - OPC->_vector[1] = OPB->_int * OPA->_vector[1]; - OPC->_vector[2] = OPB->_int * OPA->_vector[2]; - break; - - case OP_DIV_IF: - OPC->_float = (OPA->_int / OPB->_float); - break; - case OP_DIV_FI: - OPC->_float = (OPA->_float / OPB->_int); - break; - - case OP_AND_I: - OPC->_int = (OPA->_int && OPB->_int); - break; - case OP_OR_I: - OPC->_int = (OPA->_int || OPB->_int); - break; - - case OP_AND_IF: - OPC->_int = (OPA->_int && OPB->_float); - break; - case OP_OR_IF: - OPC->_int = (OPA->_int || OPB->_float); - break; - - case OP_AND_FI: - OPC->_int = (OPA->_float && OPB->_int); - break; - case OP_OR_FI: - OPC->_int = (OPA->_float || OPB->_int); - break; - - case OP_NOT_I: - OPC->_int = !OPA->_int; - break; - - case OP_NE_IF: - OPC->_int = (OPA->_int != OPB->_float); - break; - case OP_NE_FI: - OPC->_int = (OPA->_float != OPB->_int); - break; - - case OP_GADDRESS: - case OP_GLOAD_I: - case OP_GLOAD_F: - case OP_GLOAD_FLD: - case OP_GLOAD_ENT: - case OP_GLOAD_S: - case OP_GLOAD_FNC: - pr_xstatement = st-pr_statements; - PR_RunError(progfuncs, "Extra opcode not implemented\n"); - break; - - case OP_BOUNDCHECK: - if ((unsigned int)OPA->_int < (unsigned int)st->c || (unsigned int)OPA->_int >= (unsigned int)st->b) - { - pr_xstatement = st-pr_statements; - PR_RunError(progfuncs, "Progs boundcheck failed. Value is %i. Must be between %u and %u", OPA->_int, st->c, st->b); - } - break; -/* case OP_PUSH: - OPC->_int = ENGINEPOINTER(&localstack[localstack_used+pr_spushed]); - pr_spushed += OPA->_int; - if (pr_spushed + localstack_used >= LOCALSTACK_SIZE) - { - pr_spushed = 0; - pr_xstatement = st-pr_statements; - PR_RunError(progfuncs, "Progs pushed too much"); - } - break; - case OP_POP: - pr_spushed -= OPA->_int; - if (pr_spushed < 0) - { - pr_spushed = 0; - pr_xstatement = st-pr_statements; - PR_RunError(progfuncs, "Progs poped more than it pushed"); - } - break; -*/ - default: - if (st->op & 0x8000) //break point! - { - pr_xstatement = s = st-pr_statements; - - printf("Break point hit in %s.\n", PR_StringToNative(progfuncs, pr_xfunction->s_name)); - if (pr_trace<1) - pr_trace=1; //this is what it's for - - s = ShowStep(progfuncs, s); - st = &pr_statements[s]; //let the user move execution - pr_xstatement = s = st-pr_statements; - - goto reeval; //reexecute - } - pr_xstatement = st-pr_statements; - PR_RunError (progfuncs, "Bad opcode %i", st->op); - } -} - - -#undef cont -#undef reeval -#undef st -#undef pr_statements -#undef fakeop -#undef dstatement_t -#undef sofs -#undef uofs -#undef OPCODE - -#undef ENGINEPOINTER -#undef QCPOINTER -#undef QCPOINTERM - diff --git a/misc/source/fteqcc-src/gui.h b/misc/source/fteqcc-src/gui.h deleted file mode 100644 index 341aedb4..00000000 --- a/misc/source/fteqcc-src/gui.h +++ /dev/null @@ -1,21 +0,0 @@ -void GoToDefinition(char *name); -void EditFile(char *name, int line); - -void GUI_SetDefaultOpts(void); -int GUI_BuildParms(char *args, char **argv); - -unsigned char *QCC_ReadFile (const char *fname, void *buffer, int len); -int QCC_FileSize (const char *fname); -pbool QCC_WriteFile (const char *name, void *data, int len); -void GUI_DialogPrint(char *title, char *text); - -extern char parameters[16384]; - -extern char progssrcname[256]; -extern char progssrcdir[256]; - -extern pbool fl_hexen2; -extern pbool fl_autohighlight; -extern pbool fl_compileonstart; -extern pbool fl_showall; -extern pbool fl_log; diff --git a/misc/source/fteqcc-src/hash.c b/misc/source/fteqcc-src/hash.c deleted file mode 100644 index 44ac211d..00000000 --- a/misc/source/fteqcc-src/hash.c +++ /dev/null @@ -1,292 +0,0 @@ -#include "hash.h" -#include -#include - -#ifndef _WIN32 -#ifndef stricmp -#define stricmp strcasecmp -#endif -#endif - -// hash init assumes we get clean memory -void Hash_InitTable(hashtable_t *table, unsigned int numbucks, void *mem) -{ - table->numbuckets = numbucks; - table->bucket = (bucket_t **)mem; -} - -unsigned int Hash_Key(const char *name, unsigned int modulus) -{ //fixme: optimize. - unsigned int key; - for (key=0;*name; name++) - key += ((key<<3) + (key>>28) + *name); - - return (key%modulus); -} -unsigned int Hash_KeyInsensative(const char *name, unsigned int modulus) -{ //fixme: optimize. - unsigned int key; - for (key=0;*name; name++) - { - if (*name >= 'A' && *name <= 'Z') - key += ((key<<3) + (key>>28) + (*name-'A'+'a')); - else - key += ((key<<3) + (key>>28) + *name); - } - - return (key%modulus); -} - -void *Hash_Get(hashtable_t *table, const char *name) -{ - unsigned int bucknum = Hash_Key(name, table->numbuckets); - bucket_t *buck; - - buck = table->bucket[bucknum]; - - while(buck) - { - if (!STRCMP(name, buck->key.string)) - return buck->data; - - buck = buck->next; - } - return NULL; -} -void *Hash_GetInsensative(hashtable_t *table, const char *name) -{ - unsigned int bucknum = Hash_KeyInsensative(name, table->numbuckets); - bucket_t *buck; - - buck = table->bucket[bucknum]; - - while(buck) - { - if (!stricmp(name, buck->key.string)) - return buck->data; - - buck = buck->next; - } - return NULL; -} -void *Hash_GetKey(hashtable_t *table, unsigned int key) -{ - unsigned int bucknum = key%table->numbuckets; - bucket_t *buck; - - buck = table->bucket[bucknum]; - - while(buck) - { - if (buck->key.value == key) - return buck->data; - - buck = buck->next; - } - return NULL; -} -/*Does _NOT_ support items that are added with two names*/ -void *Hash_GetNextKey(hashtable_t *table, unsigned int key, void *old) -{ - unsigned int bucknum = key%table->numbuckets; - bucket_t *buck; - - buck = table->bucket[bucknum]; - - while(buck) - { - if (buck->data == old) //found the old one - break; - buck = buck->next; - } - if (!buck) - return NULL; - - buck = buck->next;//don't return old - while(buck) - { - if (buck->key.value == key) - return buck->data; - - buck = buck->next; - } - return NULL; -} -/*Does _NOT_ support items that are added with two names*/ -void *Hash_GetNext(hashtable_t *table, const char *name, void *old) -{ - unsigned int bucknum = Hash_Key(name, table->numbuckets); - bucket_t *buck; - - buck = table->bucket[bucknum]; - - while(buck) - { - if (buck->data == old) //found the old one -// if (!STRCMP(name, buck->key.string)) - break; - buck = buck->next; - } - if (!buck) - return NULL; - - buck = buck->next;//don't return old - while(buck) - { - if (!STRCMP(name, buck->key.string)) - return buck->data; - - buck = buck->next; - } - return NULL; -} -/*Does _NOT_ support items that are added with two names*/ -void *Hash_GetNextInsensative(hashtable_t *table, const char *name, void *old) -{ - unsigned int bucknum = Hash_KeyInsensative(name, table->numbuckets); - bucket_t *buck; - - buck = table->bucket[bucknum]; - - while(buck) - { - if (buck->data == old) //found the old one - { -// if (!stricmp(name, buck->key.string)) - break; - } - - buck = buck->next; - } - if (!buck) - return NULL; - - buck = buck->next;//don't return old - while(buck) - { - if (!stricmp(name, buck->key.string)) - return buck->data; - - buck = buck->next; - } - return NULL; -} - - -void *Hash_Add(hashtable_t *table, const char *name, void *data, bucket_t *buck) -{ - unsigned int bucknum = Hash_Key(name, table->numbuckets); - - buck->data = data; - buck->key.string = name; - buck->next = table->bucket[bucknum]; - table->bucket[bucknum] = buck; - - return buck; -} -void *Hash_AddInsensative(hashtable_t *table, const char *name, void *data, bucket_t *buck) -{ - unsigned int bucknum = Hash_KeyInsensative(name, table->numbuckets); - - buck->data = data; - buck->key.string = name; - buck->next = table->bucket[bucknum]; - table->bucket[bucknum] = buck; - - return buck; -} -void *Hash_AddKey(hashtable_t *table, unsigned int key, void *data, bucket_t *buck) -{ - unsigned int bucknum = key%table->numbuckets; - - buck->data = data; - buck->key.value = key; - buck->next = table->bucket[bucknum]; - table->bucket[bucknum] = buck; - - return buck; -} - -void Hash_Remove(hashtable_t *table, const char *name) -{ - unsigned int bucknum = Hash_Key(name, table->numbuckets); - bucket_t *buck; - - buck = table->bucket[bucknum]; - - if (!STRCMP(name, buck->key.string)) - { - table->bucket[bucknum] = buck->next; - return; - } - - - while(buck->next) - { - if (!STRCMP(name, buck->next->key.string)) - { - buck->next = buck->next->next; - return; - } - - buck = buck->next; - } - return; -} - -void Hash_RemoveData(hashtable_t *table, const char *name, void *data) -{ - unsigned int bucknum = Hash_Key(name, table->numbuckets); - bucket_t *buck; - - buck = table->bucket[bucknum]; - - if (buck->data == data) - if (!STRCMP(name, buck->key.string)) - { - table->bucket[bucknum] = buck->next; - return; - } - - - while(buck->next) - { - if (buck->next->data == data) - if (!STRCMP(name, buck->next->key.string)) - { - buck->next = buck->next->next; - return; - } - - buck = buck->next; - } - return; -} - - -void Hash_RemoveKey(hashtable_t *table, unsigned int key) -{ - unsigned int bucknum = key%table->numbuckets; - bucket_t *buck; - - buck = table->bucket[bucknum]; - - if (buck->key.value == key) - { - table->bucket[bucknum] = buck->next; - return; - } - - - while(buck->next) - { - if (buck->next->key.value == key) - { - buck->next = buck->next->next; - return; - } - - buck = buck->next; - } - return; -} diff --git a/misc/source/fteqcc-src/hash.h b/misc/source/fteqcc-src/hash.h deleted file mode 100644 index e07c17d2..00000000 --- a/misc/source/fteqcc-src/hash.h +++ /dev/null @@ -1,39 +0,0 @@ -//============================= -//David's hash tables -//string based. - -#ifndef HASH_H__ -#define HASH_H__ - -#define Hash_BytesForBuckets(b) (sizeof(bucket_t*)*(b)) - -#define STRCMP(s1,s2) (((*s1)!=(*s2)) || strcmp(s1+1,s2+1)) //saves about 2-6 out of 120 - expansion of idea from fastqcc -typedef struct bucket_s { - void *data; - union { - const char *string; - unsigned int value; - } key; - struct bucket_s *next; -} bucket_t; -typedef struct hashtable_s { - unsigned int numbuckets; - bucket_t **bucket; -} hashtable_t; - -void Hash_InitTable(hashtable_t *table, unsigned int numbucks, void *mem); //mem must be 0 filled. (memset(mem, 0, size)) -unsigned int Hash_Key(const char *name, unsigned int modulus); -void *Hash_Get(hashtable_t *table, const char *name); -void *Hash_GetInsensative(hashtable_t *table, const char *name); -void *Hash_GetKey(hashtable_t *table, unsigned int key); -void *Hash_GetNext(hashtable_t *table, const char *name, void *old); -void *Hash_GetNextInsensative(hashtable_t *table, const char *name, void *old); -void *Hash_GetNextKey(hashtable_t *table, unsigned int key, void *old); -void *Hash_Add(hashtable_t *table, const char *name, void *data, bucket_t *buck); -void *Hash_AddInsensative(hashtable_t *table, const char *name, void *data, bucket_t *buck); -void Hash_Remove(hashtable_t *table, const char *name); -void Hash_RemoveData(hashtable_t *table, const char *name, void *data); -void Hash_RemoveKey(hashtable_t *table, unsigned int key); -void *Hash_AddKey(hashtable_t *table, unsigned int key, void *data, bucket_t *buck); - -#endif diff --git a/misc/source/fteqcc-src/initlib.c b/misc/source/fteqcc-src/initlib.c deleted file mode 100644 index 22602574..00000000 --- a/misc/source/fteqcc-src/initlib.c +++ /dev/null @@ -1,1207 +0,0 @@ -#define PROGSUSED -#include "progsint.h" -#include - -typedef struct prmemb_s { - struct prmemb_s *prev; - int level; -} prmemb_t; -void *PRHunkAlloc(progfuncs_t *progfuncs, int ammount) -{ - prmemb_t *mem; - ammount = sizeof(prmemb_t)+((ammount + 3)&~3); - mem = memalloc(ammount); - memset(mem, 0, ammount); - mem->prev = memb; - if (!memb) - mem->level = 1; - else - mem->level = ((prmemb_t *)memb)->level+1; - memb = mem; - - return ((char *)mem)+sizeof(prmemb_t); -} - -int PRHunkMark(progfuncs_t *progfuncs) -{ - return ((prmemb_t *)memb)->level; -} -void PRHunkFree(progfuncs_t *progfuncs, int mark) -{ - prmemb_t *omem; - while(memb) - { - if (memb->level <= mark) - return; - - omem = memb; - memb = memb->prev; - memfree(omem); - } - return; -} - -/*if we ran out of memory, the vm can allocate a new block, but doing so requires fixing up all sorts of pointers*/ -void PRAddressableRelocate(progfuncs_t *progfuncs, char *oldb, char *newb, int oldlen) -{ - unsigned int i; - edictrun_t *e; - for (i=0 ; iedicttable[i]); - if (e && (char*)e->fields >= oldb && (char*)e->fields < oldb+oldlen) - e->fields = ((char*)e->fields - oldb) + newb; - } - - if (progfuncs->stringtable >= oldb && progfuncs->stringtable < oldb+oldlen) - progfuncs->stringtable = (progfuncs->stringtable - oldb) + newb; - - for (i=0; i < maxprogs; i++) - { - if ((char*)prinst->progstate[i].globals >= oldb && (char*)prinst->progstate[i].globals < oldb+oldlen) - prinst->progstate[i].globals = (float*)(((char*)prinst->progstate[i].globals - oldb) + newb); - if (prinst->progstate[i].strings >= oldb && prinst->progstate[i].strings < oldb+oldlen) - prinst->progstate[i].strings = (prinst->progstate[i].strings - oldb) + newb; - } - - for (i = 0; i < numfields; i++) - { - if (field[i].name >= oldb && field[i].name < oldb+oldlen) - field[i].name = (field[i].name - oldb) + newb; - } - - externs->addressablerelocated(progfuncs, oldb, newb, oldlen); -} - -//for 64bit systems. :) -//addressable memory is memory available to the vm itself for writing. -//once allocated, it cannot be freed for the lifetime of the VM. -void *PRAddressableExtend(progfuncs_t *progfuncs, int ammount) -{ - ammount = (ammount + 4)&~3; //round up to 4 - if (prinst->addressableused + ammount > prinst->addressablesize) - { - /*only do this if the caller states that it can cope with addressable-block relocations/resizes*/ - if (externs->addressablerelocated) - { -#ifdef _WIN32 - char *newblock; - #if 0//def _DEBUG - int oldtot = addressablesize; - #endif - int newsize = (prinst->addressableused + ammount + 4096) & ~(4096-1); - newblock = VirtualAlloc (NULL, prinst->addressablesize, MEM_RESERVE, PAGE_NOACCESS); - if (newblock) - { - VirtualAlloc (newblock, prinst->addressableused, MEM_COMMIT, PAGE_READWRITE); - memcpy(newblock, prinst->addressablehunk, prinst->addressableused); - #if 0//def _DEBUG - VirtualAlloc (prinst->addressablehunk, oldtot, MEM_RESERVE, PAGE_NOACCESS); - #else - VirtualFree (prinst->addressablehunk, 0, MEM_RELEASE); - #endif - PRAddressableRelocate(progfuncs, prinst->addressablehunk, newblock, prinst->addressableused); - prinst->addressablehunk = newblock; - prinst->addressablesize = newsize; - } -#else - char *newblock; - int newsize = (prinst->addressableused + ammount + 1024*1024) & ~(1024*1024-1); - newblock = realloc(newblock, prinst->addressablesize); - if (newblock) - { - PRAddressableRelocate(progfuncs, prinst->addressablehunk, newblock, prinst->addressableused); - prinst->addressablehunk = newblock; - prinst->addressablesize = newsize; - } -#endif - } - - if (prinst->addressableused + ammount > prinst->addressablesize) - Sys_Error("Not enough addressable memory for progs VM"); - } - - prinst->addressableused += ammount; - -#ifdef _WIN32 - if (!VirtualAlloc (prinst->addressablehunk, prinst->addressableused, MEM_COMMIT, PAGE_READWRITE)) - Sys_Error("VirtualAlloc failed. Blame windows."); -#endif - - return &prinst->addressablehunk[prinst->addressableused-ammount]; -} - - -#define MARKER 0xF1E3E3E7u -typedef struct -{ - unsigned int next; - unsigned int prev; - unsigned int size; -} qcmemfreeblock_t; -typedef struct -{ - unsigned int marker; - unsigned int size; -} qcmemusedblock_t; -static void PF_fmem_unlink(progfuncs_t *pr, qcmemfreeblock_t *p) -{ - qcmemfreeblock_t *np; - if (p->prev) - { - np = (qcmemfreeblock_t*)(pr->stringtable + p->prev); - np->next = p->next; - } - else - pr->inst->mfreelist = p->next; - if (p->next) - { - np = (qcmemfreeblock_t*)(pr->stringtable + p->next); - np->prev = p->prev; - } -} -static void *PR_memalloc (progfuncs_t *progfuncs, unsigned int size) -{ - qcmemfreeblock_t *p, *np; - qcmemusedblock_t *ub = NULL; - unsigned int b,n; - /*round size up*/ - size = (size+sizeof(qcmemusedblock_t) + 63) & ~63; - - b = prinst->mfreelist; - while (b) - { - if (b < 0 || b >= prinst->addressableused) - { - printf("PF_memalloc: memory corruption\n"); - PR_StackTrace(progfuncs); - return NULL; - } - p = (qcmemfreeblock_t*)(progfuncs->stringtable + b); - if (p->size >= size) - { - if (p->next && p->next < b + p->size || - p->next >= prinst->addressableused || - b + p->size >= prinst->addressableused || - p->prev >= b) - { - printf("PF_memalloc: memory corruption\n"); - PR_StackTrace(progfuncs); - return NULL; - } - - ub = (qcmemusedblock_t*)p; - if (p->size > size + 63) - { - /*make a new header just after it, with basically the same properties, and shift the important fields over*/ - n = b + size; - np = (qcmemfreeblock_t*)(progfuncs->stringtable + b + size); - np->prev = p->prev; - np->next = p->next; - np->size = p->size - size; - if (np->prev) - { - p = (qcmemfreeblock_t*)(progfuncs->stringtable + np->prev); - p->next = n; - } - else - prinst->mfreelist = n; - if (p->next) - { - p = (qcmemfreeblock_t*)(progfuncs->stringtable + np->next); - p->prev = n; - } - } - else - { - size = p->size; /*alloc the entire block*/ - /*unlink this entry*/ - PF_fmem_unlink(progfuncs, p); - } - break; - } - } - - /*assign more space*/ - if (!ub) - { - ub = PRAddressableExtend(progfuncs, size); - if (!ub) - { - printf("PF_memalloc: memory exausted\n"); - PR_StackTrace(progfuncs); - return NULL; - } - } - memset(ub, 0, size); - ub->marker = MARKER; - ub->size = size; - return ub+1; -} -static void PR_memfree (progfuncs_t *progfuncs, void *memptr) -{ - qcmemusedblock_t *ub; - qcmemfreeblock_t *p, *np; - unsigned int l, ln; - unsigned int size; - unsigned int ptr = memptr?((char*)memptr - progfuncs->stringtable):0; - - /*freeing NULL is ignored*/ - if (!ptr) - return; - if (ptr < sizeof(qcmemusedblock_t) || ptr >= prinst->addressableused) - { - printf("PF_memfree: pointer invalid - out of range (%u >= %u)\n", ptr, prinst->addressableused); - PR_StackTrace(progfuncs); - return; - } - - ub = (qcmemusedblock_t*)(progfuncs->stringtable + ptr); - ub--; - ptr = (char*)ub - progfuncs->stringtable; - if (ub->marker != MARKER || ub->size <= sizeof(*ub) || ptr + ub->size > (unsigned int)prinst->addressableused) - { - printf("PF_memfree: memory corruption or double free\n"); - PR_StackTrace(progfuncs); - return; - } - ub->marker = 0; - size = ub->size; - - for (ln = prinst->mfreelist, l = 0; ;) - { - if (ln < 0 || ln >= prinst->addressableused) - { - printf("PF_memfree: memory corruption\n"); - PR_StackTrace(progfuncs); - return; - } - if (!ln || ln >= ptr) - { - np = (qcmemfreeblock_t*)(progfuncs->stringtable + l); - if (l && l+np->size>ptr) - { - printf("PF_memfree: double free\n"); - PR_StackTrace(progfuncs); - return; - } - - /*generate the free block, now we know its proper values*/ - p = (qcmemfreeblock_t*)(progfuncs->stringtable + ptr); - p->prev = l; - p->next = ln; - p->size = size; - - /*update the next's previous*/ - if (p->next) - { - np = (qcmemfreeblock_t*)(progfuncs->stringtable + p->next); - np->prev = p->prev; - - /*extend this block and kill the next if they are adjacent*/ - if (p->next == ptr + size) - { - p->size += np->size; - PF_fmem_unlink(progfuncs, np); - } - } - - /*update the link to get here*/ - if (!l) - prinst->mfreelist = ptr; - else - { - np = (qcmemfreeblock_t*)(progfuncs->stringtable + l); - np->next = ptr; - - /*we're adjacent to the previous block, so merge them by killing the newly freed region*/ - if (l + np->size == ptr) - { - np->size += size; - PF_fmem_unlink(progfuncs, p); - } - } - break; - } - - l = ln; - p = (qcmemfreeblock_t*)(progfuncs->stringtable + l); - ln = p->next; - } -} - -void PRAddressableFlush(progfuncs_t *progfuncs, int totalammount) -{ - prinst->addressableused = 0; - if (totalammount < 0) //flush - { - totalammount = prinst->addressablesize; -// return; - } - -#ifdef _WIN32 - if (prinst->addressablehunk && prinst->addressablesize != totalammount) - { - VirtualFree(prinst->addressablehunk, 0, MEM_RELEASE); //doesn't this look complicated? :p - prinst->addressablehunk = NULL; - } - if (!prinst->addressablehunk) - prinst->addressablehunk = VirtualAlloc (prinst->addressablehunk, totalammount, MEM_RESERVE, PAGE_NOACCESS); -#else - if (prinst->addressablehunk && prinst->addressablesize != totalammount) - { - free(prinst->addressablehunk); - prinst->addressablehunk = NULL; - } - if (!prinst->addressablehunk) - prinst->addressablehunk = malloc(totalammount); //linux will allocate-on-use anyway, which is handy. -// memset(prinst->addressablehunk, 0xff, totalammount); -#endif - if (!prinst->addressablehunk) - Sys_Error("Out of memory\n"); - prinst->addressablesize = totalammount; -} - -int PR_InitEnts(progfuncs_t *progfuncs, int max_ents) -{ - maxedicts = max_ents; - - sv_num_edicts = 0; - - max_fields_size = fields_size; - - prinst->edicttable = PRHunkAlloc(progfuncs, maxedicts*sizeof(struct edicts_s *)); - sv_edicts = PRHunkAlloc(progfuncs, externs->edictsize); - prinst->edicttable[0] = sv_edicts; - ((edictrun_t*)prinst->edicttable[0])->fields = PRAddressableExtend(progfuncs, max_fields_size); - QC_ClearEdict(progfuncs, sv_edicts); - sv_num_edicts = 1; - - if (externs->entspawn) - externs->entspawn((struct edict_s *)sv_edicts, false); - - return max_fields_size; -} -edictrun_t tempedict; //used as a safty buffer -float tempedictfields[2048]; - -void PR_Configure (progfuncs_t *progfuncs, int addressable_size, int max_progs) //can be used to wipe all memory -{ - unsigned int i; - edictrun_t *e; - - max_fields_size=0; - fields_size = 0; - progfuncs->stringtable = 0; - QC_StartShares(progfuncs); - QC_InitShares(progfuncs); - - for ( i=1 ; iedicttable[i]); - prinst->edicttable[i] = NULL; -// e->entnum = i; - if (e) - memfree(e); - } - - PRHunkFree(progfuncs, 0); //clear mem - our hunk may not be a real hunk. - if (addressable_size<0) - addressable_size = 8*1024*1024; - PRAddressableFlush(progfuncs, addressable_size); - - pr_progstate = PRHunkAlloc(progfuncs, sizeof(progstate_t) * max_progs); - -/* for(a = 0; a < max_progs; a++) - { - pr_progstate[a].progs = NULL; - } -*/ - - maxprogs = max_progs; - pr_typecurrent=-1; - - prinst->reorganisefields = false; - - maxedicts = 1; - prinst->edicttable = &sv_edicts; - sv_num_edicts = 1; //set up a safty buffer so things won't go horribly wrong too often - sv_edicts=(struct edict_s *)&tempedict; - tempedict.readonly = true; - tempedict.fields = tempedictfields; - tempedict.isfree = false; -} - - - -struct globalvars_s *PR_globals (progfuncs_t *progfuncs, progsnum_t pnum) -{ - if (pnum < 0) - { - if (!current_progstate) - { - static float fallback[RESERVED_OFS]; - return (struct globalvars_s *)fallback; //err.. you've not loaded one yet. - } - return (struct globalvars_s *)current_progstate->globals; - } - return (struct globalvars_s *)pr_progstate[pnum].globals; -} - -struct entvars_s *PR_entvars (progfuncs_t *progfuncs, struct edict_s *ed) -{ - if (((edictrun_t *)ed)->isfree) - return NULL; - - return (struct entvars_s *)edvars(ed); -} - -int PR_GetFuncArgCount(progfuncs_t *progfuncs, func_t func) -{ - unsigned int pnum; - unsigned int fnum; - dfunction_t *f; - - pnum = (func & 0xff000000)>>24; - fnum = (func & 0x00ffffff); - - if (pnum >= (unsigned)maxprogs || !pr_progstate[pnum].functions) - return -1; - else if (fnum >= pr_progstate[pnum].progs->numfunctions) - return -1; - else - { - f = pr_progstate[pnum].functions + fnum; - return f->numparms; - } -} - -func_t PR_FindFunc(progfuncs_t *progfuncs, char *funcname, progsnum_t pnum) -{ - dfunction_t *f=NULL; - if (pnum == PR_ANY) - { - for (pnum = 0; (unsigned)pnum < maxprogs; pnum++) - { - if (!pr_progstate[pnum].progs) - continue; - f = ED_FindFunction(progfuncs, funcname, &pnum, pnum); - if (f) - break; - } - } - else if (pnum == PR_ANYBACK) //run backwards - { - for (pnum = maxprogs-1; pnum >= 0; pnum--) - { - if (!pr_progstate[pnum].progs) - continue; - f = ED_FindFunction(progfuncs, funcname, &pnum, pnum); - if (f) - break; - } - } - else - f = ED_FindFunction(progfuncs, funcname, &pnum, pnum); - if (!f) - return 0; - - { - ddef16_t *var16; - ddef32_t *var32; - switch(pr_progstate[pnum].structtype) - { - case PST_KKQWSV: - case PST_DEFAULT: - var16 = ED_FindTypeGlobalFromProgs16(progfuncs, funcname, pnum, ev_function); //we must make sure we actually have a function def - 'light' is defined as a field before it is defined as a function. - if (!var16) - return (f - pr_progstate[pnum].functions) | (pnum << 24); - return *(int *)&pr_progstate[pnum].globals[var16->ofs]; - case PST_QTEST: - case PST_FTE32: - var32 = ED_FindTypeGlobalFromProgs32(progfuncs, funcname, pnum, ev_function); //we must make sure we actually have a function def - 'light' is defined as a field before it is defined as a function. - if (!var32) - return (f - pr_progstate[pnum].functions) | (pnum << 24); - return *(int *)&pr_progstate[pnum].globals[var32->ofs]; - } - Sys_Error("Error with def size (PR_FindFunc)"); - } - return 0; -} - -void QC_FindPrefixedGlobals(progfuncs_t *progfuncs, char *prefix, void (*found) (progfuncs_t *progfuncs, char *name, union eval_s *val, etype_t type) ) -{ - unsigned int i; - ddef16_t *def16; - ddef32_t *def32; - int len = strlen(prefix); - unsigned int pnum; - - for (pnum = 0; pnum < maxprogs; pnum++) - { - if (!pr_progstate[pnum].progs) - continue; - - switch(pr_progstate[pnum].structtype) - { - case PST_DEFAULT: - case PST_KKQWSV: - for (i=1 ; inumglobaldefs ; i++) - { - def16 = &pr_progstate[pnum].globaldefs16[i]; - if (!strncmp(def16->s_name+progfuncs->stringtable,prefix, len)) - found(progfuncs, def16->s_name+progfuncs->stringtable, (eval_t *)&pr_progstate[pnum].globals[def16->ofs], def16->type); - } - break; - case PST_QTEST: - case PST_FTE32: - for (i=1 ; inumglobaldefs ; i++) - { - def32 = &pr_progstate[pnum].globaldefs32[i]; - if (!strncmp(def32->s_name+progfuncs->stringtable,prefix, len)) - found(progfuncs, def32->s_name+progfuncs->stringtable, (eval_t *)&pr_progstate[pnum].globals[def32->ofs], def32->type); - } - break; - } - } -} - -eval_t *PR_FindGlobal(progfuncs_t *progfuncs, char *globname, progsnum_t pnum, etype_t *type) -{ - unsigned int i; - ddef16_t *var16; - ddef32_t *var32; - if (pnum == PR_CURRENT) - pnum = pr_typecurrent; - if (pnum == PR_ANY) - { - eval_t *ev; - for (i = 0; i < maxprogs; i++) - { - if (!pr_progstate[i].progs) - continue; - ev = PR_FindGlobal(progfuncs, globname, i, type); - if (ev) - return ev; - } - return NULL; - } - if (pnum < 0 || (unsigned)pnum >= maxprogs || !pr_progstate[pnum].progs) - return NULL; - switch(pr_progstate[pnum].structtype) - { - case PST_DEFAULT: - case PST_KKQWSV: - if (!(var16 = ED_FindGlobalFromProgs16(progfuncs, globname, pnum))) - return NULL; - - if (type) - *type = var16->type; - return (eval_t *)&pr_progstate[pnum].globals[var16->ofs]; - case PST_QTEST: - case PST_FTE32: - if (!(var32 = ED_FindGlobalFromProgs32(progfuncs, globname, pnum))) - return NULL; - - if (type) - *type = var32->type; - return (eval_t *)&pr_progstate[pnum].globals[var32->ofs]; - } - Sys_Error("Error with def size (PR_FindGlobal)"); - return NULL; -} - -void SetGlobalEdict(progfuncs_t *progfuncs, struct edict_s *ed, int ofs) -{ - ((int*)pr_globals)[ofs] = EDICT_TO_PROG(progfuncs, ed); -} - -char *PR_VarString (progfuncs_t *progfuncs, int first) -{ - int i; - static char out[1024]; - char *s; - - out[0] = 0; - for (i=first ; istringtable; - if (strlen(out) + strlen(s) + 1 >= sizeof(out)) - return out; - strcat (out, s); - } - } - return out; -} - -int PR_QueryField (progfuncs_t *progfuncs, unsigned int fieldoffset, etype_t *type, char **name, evalc_t *fieldcache) -{ - fdef_t *var; - var = ED_FieldAtOfs(progfuncs, fieldoffset); - if (!var) - return false; - - if (type) - *type = var->type & ~(DEF_SAVEGLOBAL|DEF_SHARED); - if (name) - *name = var->name; - if (fieldcache) - { - fieldcache->ofs32 = var; - fieldcache->varname = var->name; - } - - return true; -} - -eval_t *GetEdictFieldValue(progfuncs_t *progfuncs, struct edict_s *ed, char *name, evalc_t *cache) -{ - fdef_t *var; - if (!cache) - { - var = ED_FindField(progfuncs, name); - if (!var) - return NULL; - return (eval_t *) &(((int*)(((edictrun_t*)ed)->fields))[var->ofs]); - } - if (!cache->varname) - { - cache->varname = name; - var = ED_FindField(progfuncs, name); - if (!var) - { - cache->ofs32 = NULL; - return NULL; - } - cache->ofs32 = var; - cache->varname = var->name; - if (!ed) - return (void*)~0; //something not null - return (eval_t *) &(((int*)(((edictrun_t*)ed)->fields))[var->ofs]); - } - if (cache->ofs32 == NULL) - return NULL; - return (eval_t *) &(((int*)(((edictrun_t*)ed)->fields))[cache->ofs32->ofs]); -} - -struct edict_s *ProgsToEdict (progfuncs_t *progfuncs, int progs) -{ - if ((unsigned)progs >= (unsigned)maxedicts) - { - printf("Bad entity index %i\n", progs); - progs = 0; - } - return (struct edict_s *)PROG_TO_EDICT(progfuncs, progs); -} -int EdictToProgs (progfuncs_t *progfuncs, struct edict_s *ed) -{ - return EDICT_TO_PROG(progfuncs, ed); -} - -string_t PR_StringToProgs (progfuncs_t *progfuncs, char *str) -{ - char **ntable; - int i, free=-1; - - if (!str) - return 0; - - if (str >= progfuncs->stringtable && str < progfuncs->stringtable + prinst->addressableused) - return str - progfuncs->stringtable; - - for (i = prinst->numallocedstrings-1; i >= 0; i--) - { - if (prinst->allocedstrings[i] == str) - return (string_t)((unsigned int)i | 0x80000000); - if (!prinst->allocedstrings[i]) - free = i; - } - - if (free != -1) - { - i = free; - prinst->allocedstrings[i] = str; - return (string_t)((unsigned int)i | 0x80000000); - } - - prinst->maxallocedstrings += 1024; - ntable = memalloc(sizeof(char*) * prinst->maxallocedstrings); - memcpy(ntable, prinst->allocedstrings, sizeof(char*) * prinst->numallocedstrings); - memset(ntable + prinst->numallocedstrings, 0, sizeof(char*) * (prinst->maxallocedstrings - prinst->numallocedstrings)); - prinst->numallocedstrings = prinst->maxallocedstrings; - if (prinst->allocedstrings) - memfree(prinst->allocedstrings); - prinst->allocedstrings = ntable; - - for (i = prinst->numallocedstrings-1; i >= 0; i--) - { - if (!prinst->allocedstrings[i]) - { - prinst->allocedstrings[i] = str; - return (string_t)((unsigned int)i | 0x80000000); - } - } - - return 0; -} - -char *PR_RemoveProgsString (progfuncs_t *progfuncs, string_t str) -{ - char *ret; - - //input string is expected to be an allocated string - //if its a temp, or a constant, just return NULL. - if ((unsigned int)str & 0xc0000000) - { - if ((unsigned int)str & 0x80000000) - { - int i = str & ~0x80000000; - if (i >= prinst->numallocedstrings) - { - pr_trace = 1; - return NULL; - } - if (prinst->allocedstrings[i]) - { - ret = prinst->allocedstrings[i]; - prinst->allocedstrings[i] = NULL; //remove it - return ret; - } - else - { - pr_trace = 1; - return NULL; //urm, was freed... - } - } - } - pr_trace = 1; - return NULL; -} - -char *ASMCALL PR_StringToNative (progfuncs_t *progfuncs, string_t str) -{ - if ((unsigned int)str & 0xc0000000) - { - if ((unsigned int)str & 0x80000000) - { - int i = str & ~0x80000000; - if (i >= prinst->numallocedstrings) - { - printf("invalid string %x\n", str); - PR_StackTrace(progfuncs); - pr_trace = 1; - return ""; - } - if (prinst->allocedstrings[i]) - return prinst->allocedstrings[i]; - else - { - printf("invalid string %x\n", str); - PR_StackTrace(progfuncs); - pr_trace = 1; - return ""; //urm, was freed... - } - } - if ((unsigned int)str & 0x40000000) - { - int i = str & ~0x40000000; - if (i >= prinst->numtempstrings) - { - printf("invalid temp string %x\n", str); - PR_StackTrace(progfuncs); - pr_trace = 1; - return ""; - } - return prinst->tempstrings[i]; - } - } - - if ((unsigned int)str >= (unsigned int)prinst->addressableused) - { - printf("invalid string offset %x\n", str); - PR_StackTrace(progfuncs); - pr_trace = 1; - return ""; - } - return progfuncs->stringtable + str; -} - - -string_t PR_AllocTempString (progfuncs_t *progfuncs, char *str) -{ - char **ntable; - int newmax; - int i; - - if (!str) - return 0; - - if (prinst->numtempstrings == prinst->maxtempstrings) - { - newmax = prinst->maxtempstrings += 1024; - prinst->maxtempstrings += 1024; - ntable = memalloc(sizeof(char*) * newmax); - memcpy(ntable, prinst->tempstrings, sizeof(char*) * prinst->numtempstrings); - prinst->maxtempstrings = newmax; - if (prinst->tempstrings) - memfree(prinst->tempstrings); - prinst->tempstrings = ntable; - } - - i = prinst->numtempstrings; - if (i == 0x10000000) - return 0; - - prinst->numtempstrings++; - - prinst->tempstrings[i] = memalloc(strlen(str)+1); - strcpy(prinst->tempstrings[i], str); - - return (string_t)((unsigned int)i | 0x40000000); -} -string_t PR_AllocTempStringLen (progfuncs_t *progfuncs, char **str, unsigned int len) -{ - char **ntable; - int newmax; - int i; - - if (!str) - return 0; - - if (prinst->numtempstrings == prinst->maxtempstrings) - { - newmax = prinst->maxtempstrings += 1024; - prinst->maxtempstrings += 1024; - ntable = memalloc(sizeof(char*) * newmax); - memcpy(ntable, prinst->tempstrings, sizeof(char*) * prinst->numtempstrings); - prinst->maxtempstrings = newmax; - if (prinst->tempstrings) - memfree(prinst->tempstrings); - prinst->tempstrings = ntable; - } - - i = prinst->numtempstrings; - if (i == 0x10000000) - return 0; - - prinst->numtempstrings++; - - prinst->tempstrings[i] = memalloc(len); - *str = prinst->tempstrings[i]; - - return (string_t)((unsigned int)i | 0x40000000); -} - -void PR_FreeTemps (progfuncs_t *progfuncs, int depth) -{ - int i; - if (depth > prinst->numtempstrings) - { - Sys_Error("QC Temp stack inverted\n"); - return; - } - for (i = depth; i < prinst->numtempstrings; i++) - { - memfree(prinst->tempstrings[i]); - } - - prinst->numtempstrings = depth; -} - - -struct qcthread_s *PR_ForkStack (progfuncs_t *progfuncs); -void PR_ResumeThread (progfuncs_t *progfuncs, struct qcthread_s *thread); -void PR_AbortStack (progfuncs_t *progfuncs); - - -void RegisterBuiltin(progfuncs_t *progfncs, char *name, builtin_t func); - -progfuncs_t deffuncs = { - PROGSTRUCT_VERSION, - PR_Configure, - PR_LoadProgs, - PR_InitEnts, - PR_ExecuteProgram, - PR_SwitchProgs, - PR_globals, - PR_entvars, - PR_RunError, - ED_Print, - ED_Alloc, - ED_Free, - - EDICT_NUM, - NUM_FOR_EDICT, - - - SetGlobalEdict, - - PR_VarString, - - NULL, - PR_FindFunc, -#ifdef MINIMAL - NULL, - NULL, -#else - Comp_Begin, - Comp_Continue, -#endif - - filefromprogs, - NULL,//filefromnewprogs, - - SaveEnts, - LoadEnts, - - SaveEnt, - RestoreEnt, - - PR_FindGlobal, - ED_NewString, - (void*)PRHunkAlloc, - - GetEdictFieldValue, - ProgsToEdict, - EdictToProgs, - - EvaluateDebugString, - - NULL, - PR_StackTrace, - - PR_ToggleBreakpoint, - 0, - NULL, -#ifdef MINIMAL - NULL, -#else - Decompile, -#endif - NULL, - NULL, - RegisterBuiltin, - - 0, - 0, - - PR_ForkStack, - PR_ResumeThread, - PR_AbortStack, - - 0, - - QC_RegisterFieldVar, - - 0, - 0, - - PR_AllocTempString, - - PR_StringToProgs, - PR_StringToNative, - 0, - PR_QueryField, - QC_ClearEdict, - QC_FindPrefixedGlobals, - PR_memalloc, - PR_AllocTempStringLen, - PR_memfree, -}; -#undef printf - -//defs incase following structure is not passed. -struct edict_s *safesv_edicts; -int safesv_num_edicts; -double safetime=0; - -progexterns_t defexterns = { - PROGSTRUCT_VERSION, - - NULL, //char *(*ReadFile) (char *fname, void *buffer, int len); - NULL, //int (*FileSize) (char *fname); //-1 if file does not exist - NULL, //bool (*WriteFile) (char *name, void *data, int len); - printf, //void (*printf) (char *, ...); - (void*)exit, //void (*Sys_Error) (char *, ...); - NULL, //void (*Abort) (char *, ...); - sizeof(edictrun_t), //int edictsize; //size of edict_t - - NULL, //void (*entspawn) (struct edict_s *ent); //ent has been spawned, but may not have all the extra variables (that may need to be set) set - NULL, //bool (*entcanfree) (struct edict_s *ent); //return true to stop ent from being freed - NULL, //void (*stateop) (float var, func_t func); - NULL, - NULL, - NULL, - - //used when loading a game - NULL, //builtin_t *(*builtinsfor) (int num); //must return a pointer to the builtins that were used before the state was saved. - NULL, //void (*loadcompleate) (int edictsize); //notification to reset any pointers. - NULL, - - (void*)malloc, //void *(*memalloc) (int size); //small string allocation malloced and freed randomly by the executor. (use memalloc if you want) - free, //void (*memfree) (void * mem); - - - NULL, //builtin_t *globalbuiltins; //these are available to all progs - 0, //int numglobalbuiltins; - - PR_NOCOMPILE, - - &safetime, //double *gametime; - - &safesv_edicts, //struct edict_s **sv_edicts; - &safesv_num_edicts, //int *sv_num_edicts; - - NULL, //int (*useeditor) (char *filename, int line, int nump, char **parms); -}; - -//progfuncs_t *progfuncs = NULL; -#undef memfree -#undef prinst -#undef extensionbuiltin -#undef field -#undef shares -#undef maxedicts -#undef sv_num_edicts - - -#ifdef QCLIBDLL_EXPORTS -__declspec(dllexport) -#endif -void CloseProgs(progfuncs_t *inst) -{ -// extensionbuiltin_t *eb; - void (VARGS *f) (void *); - - unsigned int i; - edictrun_t *e; - - f = inst->parms->memfree; - - for ( i=1 ; iinst->maxedicts; i++) - { - e = (edictrun_t *)(inst->inst->edicttable[i]); - inst->inst->edicttable[i] = NULL; - if (e) - { -// e->entnum = i; - f(e); - } - } - - PRHunkFree(inst, 0); - -#ifdef _WIN32 - VirtualFree(inst->inst->addressablehunk, 0, MEM_RELEASE); //doesn't this look complicated? :p -#else - free(inst->inst->addressablehunk); -#endif - - if (inst->inst->allocedstrings) - f(inst->inst->allocedstrings); - inst->inst->allocedstrings = NULL; - if (inst->inst->tempstrings) - f(inst->inst->tempstrings); - inst->inst->tempstrings = NULL; - - -/* - while(inst->prinst->extensionbuiltin) - { - eb = inst->prinst->extensionbuiltin->prev; - f(inst->prinst->extensionbuiltin); - inst->prinst->extensionbuiltin = eb; - } -*/ - if (inst->inst->field) - f(inst->inst->field); - if (inst->inst->shares) - f(inst->inst->shares); //free memory - f(inst->inst); - f(inst); -} - -void RegisterBuiltin(progfuncs_t *progfuncs, char *name, builtin_t func) -{ -/* - extensionbuiltin_t *eb; - eb = memalloc(sizeof(extensionbuiltin_t)); - eb->prev = progfuncs->prinst->extensionbuiltin; - progfuncs->prinst->extensionbuiltin = eb; - eb->name = name; - eb->func = func; -*/ -} - -#ifndef WIN32 -#define QCLIBINT //don't use dllspecifications -#endif - -#if defined(QCLIBDLL_EXPORTS) -__declspec(dllexport) -#endif -progfuncs_t * InitProgs(progexterns_t *ext) -{ - progfuncs_t *funcs; - - if (!ext) - ext = &defexterns; - else - { - int i; - if (ext->progsversion > PROGSTRUCT_VERSION) - return NULL; - - for (i=0;imemalloc(sizeof(progfuncs_t)); - memcpy(funcs, &deffuncs, sizeof(progfuncs_t)); - - funcs->inst = ext->memalloc(sizeof(prinst_t)); - memset(funcs->inst,0, sizeof(prinst_t)); - - funcs->pr_trace = &funcs->inst->pr_trace; - funcs->progstate = &funcs->inst->progstate; - funcs->callargc = &funcs->inst->pr_argc; - - funcs->parms = ext; - - SetEndian(); - - return funcs; -} - - - - - - - - - - - - - - - - -#ifdef QCC -void main (int argc, char **argv) -{ - progexterns_t ext; - - progfuncs_t *funcs; - funcs = InitProgs(&ext); - if (funcs->PR_StartCompile(argc, argv)) - while(funcs->PR_ContinueCompile()); -} -#endif diff --git a/misc/source/fteqcc-src/pr_comp.h b/misc/source/fteqcc-src/pr_comp.h deleted file mode 100644 index 35590863..00000000 --- a/misc/source/fteqcc-src/pr_comp.h +++ /dev/null @@ -1,575 +0,0 @@ -// this file is shared by the execution and compiler - -/*i'm part way through making this work -I've given up now that I can't work out a way to load pointers. -Setting them should be fine. -*/ -#ifndef __PR_COMP_H__ -#define __PR_COMP_H__ - - -/*this distinction is made as the execution uses c pointers while compiler uses pointers from the start of the string table of the current progs*/ -#ifdef COMPILER -typedef int QCC_string_t; -#else -//typedef char *string_t; -#endif - -//typedef enum {ev_void, ev_string, ev_float, ev_vector, ev_entity, ev_field, ev_function, ev_pointer, ev_integer, ev_struct, ev_union} etype_t; -// 0 1 2 3 4 5 6 7 8 9 10 - -#define OFS_NULL 0 -#define OFS_RETURN 1 -#define OFS_PARM0 4 // leave 3 ofs for each parm to hold vectors -#define OFS_PARM1 7 -#define OFS_PARM2 10 -#define OFS_PARM3 13 -#define OFS_PARM4 16 -#define OFS_PARM5 19 -#define OFS_PARM6 22 -#define OFS_PARM7 25 -#define RESERVED_OFS 28 - - -enum qcop_e { - OP_DONE, //0 - OP_MUL_F, - OP_MUL_V, - OP_MUL_FV, - OP_MUL_VF, - OP_DIV_F, - OP_ADD_F, - OP_ADD_V, - OP_SUB_F, - OP_SUB_V, - - OP_EQ_F, //10 - OP_EQ_V, - OP_EQ_S, - OP_EQ_E, - OP_EQ_FNC, - - OP_NE_F, - OP_NE_V, - OP_NE_S, - OP_NE_E, - OP_NE_FNC, - - OP_LE_F, //20 - OP_GE_F, - OP_LT_F, - OP_GT_F, - - OP_LOAD_F, - OP_LOAD_V, - OP_LOAD_S, - OP_LOAD_ENT, - OP_LOAD_FLD, - OP_LOAD_FNC, - - OP_ADDRESS, //30 - - OP_STORE_F, - OP_STORE_V, - OP_STORE_S, - OP_STORE_ENT, - OP_STORE_FLD, - OP_STORE_FNC, - - OP_STOREP_F, - OP_STOREP_V, - OP_STOREP_S, - OP_STOREP_ENT, //40 - OP_STOREP_FLD, - OP_STOREP_FNC, - - OP_RETURN, - OP_NOT_F, - OP_NOT_V, - OP_NOT_S, - OP_NOT_ENT, - OP_NOT_FNC, - OP_IF_I, - OP_IFNOT_I, //50 - OP_CALL0, //careful... hexen2 and q1 have different calling conventions - OP_CALL1, //remap hexen2 calls to OP_CALL2H - OP_CALL2, - OP_CALL3, - OP_CALL4, - OP_CALL5, - OP_CALL6, - OP_CALL7, - OP_CALL8, - OP_STATE, //60 - OP_GOTO, - OP_AND_F, - OP_OR_F, - - OP_BITAND_F, - OP_BITOR_F, - - - //these following ones are Hexen 2 constants. - - OP_MULSTORE_F, - OP_MULSTORE_VF, - OP_MULSTOREP_F, - OP_MULSTOREP_VF, - - OP_DIVSTORE_F, //70 - OP_DIVSTOREP_F, - - OP_ADDSTORE_F, - OP_ADDSTORE_V, - OP_ADDSTOREP_F, - OP_ADDSTOREP_V, - - OP_SUBSTORE_F, - OP_SUBSTORE_V, - OP_SUBSTOREP_F, - OP_SUBSTOREP_V, - - OP_FETCH_GBL_F, //80 - OP_FETCH_GBL_V, - OP_FETCH_GBL_S, - OP_FETCH_GBL_E, - OP_FETCH_GBL_FNC, - - OP_CSTATE, - OP_CWSTATE, - - OP_THINKTIME, - - OP_BITSET, - OP_BITSETP, - OP_BITCLR, //90 - OP_BITCLRP, - - OP_RAND0, - OP_RAND1, - OP_RAND2, - OP_RANDV0, - OP_RANDV1, - OP_RANDV2, - - OP_SWITCH_F, - OP_SWITCH_V, - OP_SWITCH_S, //100 - OP_SWITCH_E, - OP_SWITCH_FNC, - - OP_CASE, - OP_CASERANGE, - - - - - - //the rest are added - //mostly they are various different ways of adding two vars with conversions. - - OP_CALL1H, - OP_CALL2H, - OP_CALL3H, - OP_CALL4H, - OP_CALL5H, - OP_CALL6H, //110 - OP_CALL7H, - OP_CALL8H, - - - OP_STORE_I, - OP_STORE_IF, - OP_STORE_FI, - - OP_ADD_I, - OP_ADD_FI, - OP_ADD_IF, - - OP_SUB_I, - OP_SUB_FI, //120 - OP_SUB_IF, - - OP_CONV_ITOF, - OP_CONV_FTOI, - OP_CP_ITOF, - OP_CP_FTOI, - OP_LOAD_I, - OP_STOREP_I, - OP_STOREP_IF, - OP_STOREP_FI, - - OP_BITAND_I, //130 - OP_BITOR_I, - - OP_MUL_I, - OP_DIV_I, - OP_EQ_I, - OP_NE_I, - - OP_IFNOT_S, - OP_IF_S, - - OP_NOT_I, - - OP_DIV_VF, - - OP_XOR_I, //140 - OP_RSHIFT_I, - OP_LSHIFT_I, - - OP_GLOBALADDRESS, - OP_POINTER_ADD, //32 bit pointers - - OP_LOADA_F, - OP_LOADA_V, - OP_LOADA_S, - OP_LOADA_ENT, - OP_LOADA_FLD, - OP_LOADA_FNC, //150 - OP_LOADA_I, - - OP_STORE_P, //152... erm.. wait... - OP_LOAD_P, - - OP_LOADP_F, - OP_LOADP_V, - OP_LOADP_S, - OP_LOADP_ENT, - OP_LOADP_FLD, - OP_LOADP_FNC, - OP_LOADP_I, //160 - - OP_LE_I, - OP_GE_I, - OP_LT_I, - OP_GT_I, - - OP_LE_IF, - OP_GE_IF, - OP_LT_IF, - OP_GT_IF, - - OP_LE_FI, - OP_GE_FI, //170 - OP_LT_FI, - OP_GT_FI, - - OP_EQ_IF, - OP_EQ_FI, - - //------------------------------------- - //string manipulation. - OP_ADD_SF, //(char*)c = (char*)a + (float)b add_fi->i - OP_SUB_S, //(float)c = (char*)a - (char*)b sub_ii->f - OP_STOREP_C,//(float)c = *(char*)b = (float)a - OP_LOADP_C, //(float)c = *(char*) - //------------------------------------- - - - OP_MUL_IF, - OP_MUL_FI, //180 - OP_MUL_VI, - OP_MUL_IV, - OP_DIV_IF, - OP_DIV_FI, - OP_BITAND_IF, - OP_BITOR_IF, - OP_BITAND_FI, - OP_BITOR_FI, - OP_AND_I, - OP_OR_I, //190 - OP_AND_IF, - OP_OR_IF, - OP_AND_FI, - OP_OR_FI, - OP_NE_IF, - OP_NE_FI, - -//erm... FTEQCC doesn't make use of these... These are for DP. - OP_GSTOREP_I, - OP_GSTOREP_F, - OP_GSTOREP_ENT, - OP_GSTOREP_FLD, // integers //200 - OP_GSTOREP_S, - OP_GSTOREP_FNC, // pointers - OP_GSTOREP_V, - OP_GADDRESS, - OP_GLOAD_I, - OP_GLOAD_F, - OP_GLOAD_FLD, - OP_GLOAD_ENT, - OP_GLOAD_S, - OP_GLOAD_FNC, //210 - OP_BOUNDCHECK, - -//back to ones that we do use. - OP_UNUSED, //used to be OP_STOREP_P, which is now emulated with OP_STOREP_I, fteqcc nor fte generated it - OP_PUSH, //push 4octets onto the local-stack (which is ALWAYS poped on function return). Returns a pointer. - OP_POP, //pop those ones that were pushed (don't over do it). Needs assembler. - - OP_SWITCH_I,//hmm. - OP_GLOAD_V, - - OP_IF_F, - OP_IFNOT_F, - - OP_NUMREALOPS, - - /* - These ops are emulated out, always, and are only present in the compiler. - */ - - OP_BITSET_I, //220 - OP_BITSETP_I, - - OP_MULSTORE_I, - OP_DIVSTORE_I, - OP_ADDSTORE_I, - OP_SUBSTORE_I, - OP_MULSTOREP_I, - OP_DIVSTOREP_I, - OP_ADDSTOREP_I, - OP_SUBSTOREP_I, - - OP_MULSTORE_IF, //230 - OP_MULSTOREP_IF, - OP_DIVSTORE_IF, - OP_DIVSTOREP_IF, - OP_ADDSTORE_IF, - OP_ADDSTOREP_IF, - OP_SUBSTORE_IF, - OP_SUBSTOREP_IF, - - OP_MULSTORE_FI, - OP_MULSTOREP_FI, - OP_DIVSTORE_FI, //240 - OP_DIVSTOREP_FI, - OP_ADDSTORE_FI, - OP_ADDSTOREP_FI, - OP_SUBSTORE_FI, - OP_SUBSTOREP_FI, - - OP_MULSTORE_VI, - OP_MULSTOREP_VI, - - OP_LOADA_STRUCT, - OP_STOREP_P, - - OP_NUMOPS -}; - -#define MAX_PARMS 8 - -// qtest structs (used for reordering and not execution) -typedef struct qtest_statement_s -{ - unsigned int line; // line number in source code file - unsigned short op; - unsigned short a,b,c; -} qtest_statement_t; - -typedef struct qtest_def_s -{ - unsigned int type; // no DEFGLOBAL found in qtest progs - unsigned int s_name; // different order! - unsigned int ofs; -} qtest_def_t; - -typedef struct qtest_function_s -{ - int first_statement; - int unused1; - int locals; // assumed! (always 0 in real qtest progs) - int profile; // assumed! (always 0 in real qtest progs) - - int s_name; - int s_file; - - int numparms; - int parm_start; // different order - int parm_size[MAX_PARMS]; // ints instead of bytes... -} qtest_function_t; - -#ifndef COMPILER -typedef struct statement16_s -{ - unsigned short op; - unsigned short a,b,c; -} dstatement16_t; -typedef struct statement32_s -{ - unsigned int op; - unsigned int a,b,c; -} dstatement32_t; -#else -typedef struct QCC_statement16_s -{ - unsigned short op; - unsigned short a,b,c; -} QCC_dstatement16_t; -typedef struct QCC_statement32_s -{ - unsigned int op; - unsigned int a,b,c; -} QCC_dstatement32_t; -#define QCC_dstatement_t QCC_dstatement32_t -#endif - -//these should be the same except the string type -#ifndef COMPILER -typedef struct ddef16_s -{ - unsigned short type; // if DEF_SAVEGLOBAL bit is set - // the variable needs to be saved in savegames - unsigned short ofs; - string_t s_name; -} ddef16_t; - -typedef struct ddef32_s -{ - unsigned int type; // if DEF_SAVEGLOBAL bit is set - // the variable needs to be saved in savegames - unsigned int ofs; - string_t s_name; -} ddef32_t; - -typedef struct fdef_s -{ - unsigned int type; // if DEF_SAVEGLOBAL bit is set - // the variable needs to be saved in savegames - unsigned int ofs; - unsigned int progsofs; //used at loading time, so maching field offsets (unions/members) are positioned at the same runtime offset. - char * name; -} fdef_t; - -typedef void *ddefXX_t; -#else -typedef struct QCC_ddef16_s -{ - unsigned short type; // if DEF_SAVEGLOBAL bit is set - // the variable needs to be saved in savegames - unsigned short ofs; - QCC_string_t s_name; -} QCC_ddef16_t; - -typedef struct QCC_ddef32_s -{ - unsigned int type; // if DEF_SAVEGLOBAL bit is set - // the variable needs to be saved in savegames - unsigned int ofs; - QCC_string_t s_name; -} QCC_ddef32_t; - -#define QCC_ddef_t QCC_ddef32_t -#endif - -#define DEF_SAVEGLOBAL (1<<15) -#define DEF_SHARED (1<<14) - -#ifndef COMPILER -typedef struct -{ - int first_statement; // negative numbers are builtins - int parm_start; - int locals; // total ints of parms + locals - - int profile; // runtime - - string_t s_name; - string_t s_file; // source file defined in - - int numparms; - qbyte parm_size[MAX_PARMS]; -} dfunction_t; -#else -typedef struct -{ - unsigned int first_statement; // negative numbers are builtins - unsigned int parm_start; - int locals; // total ints of parms + locals - - int profile; // runtime - - QCC_string_t s_name; - QCC_string_t s_file; // source file defined in - - int numparms; - qbyte parm_size[MAX_PARMS]; -} QCC_dfunction_t; -#endif - -#define PROG_QTESTVERSION 3 -#define PROG_VERSION 6 -#define PROG_KKQWSVVERSION 7 -#define PROG_EXTENDEDVERSION 7 -#define PROG_SECONDARYVERSION16 (*(int*)"1FTE" ^ *(int*)"PROG") //something unlikly and still meaningful (to me) -#define PROG_SECONDARYVERSION32 (*(int*)"1FTE" ^ *(int*)"32B ") //something unlikly and still meaningful (to me) -typedef struct -{ - int version; - int crc; // check of header file - - unsigned int ofs_statements; //comp 1 - unsigned int numstatements; // statement 0 is an error - - unsigned int ofs_globaldefs; //comp 2 - unsigned int numglobaldefs; - - unsigned int ofs_fielddefs; //comp 4 - unsigned int numfielddefs; - - unsigned int ofs_functions; //comp 8 - unsigned int numfunctions; // function 0 is an empty - - unsigned int ofs_strings; //comp 16 - unsigned int numstrings; // first string is a null string - - unsigned int ofs_globals; //comp 32 - unsigned int numglobals; - - unsigned int entityfields; - - //debug / version 7 extensions - unsigned int ofsfiles; //non list format. no comp - unsigned int ofslinenums; //numstatements big //comp 64 - unsigned int ofsbodylessfuncs; //no comp - unsigned int numbodylessfuncs; - - unsigned int ofs_types; //comp 128 - unsigned int numtypes; - unsigned int blockscompressed; - - int secondaryversion; //Constant - to say that any version 7 progs are actually ours, not someone else's alterations. -} dprograms_t; -#define standard_dprograms_t_size ((size_t)&((dprograms_t*)NULL)->ofsfiles) - -#endif - - - - - -typedef struct { - char filename[128]; - int size; - int compsize; - int compmethod; - int ofs; -} includeddatafile_t; - - - - -typedef struct typeinfo_s -{ - etype_t type; - - int next; - int aux_type; - int num_parms; - - int ofs; //inside a structure. - int size; - string_t name; -} typeinfo_t; diff --git a/misc/source/fteqcc-src/pr_edict.c b/misc/source/fteqcc-src/pr_edict.c deleted file mode 100644 index 292e764b..00000000 --- a/misc/source/fteqcc-src/pr_edict.c +++ /dev/null @@ -1,3172 +0,0 @@ - - -#define PROGSUSED -struct edict_s; -#include "progsint.h" -//#include "crc.h" - -#ifdef _WIN32 -//this is windows all files are written with this endian standard. we do this to try to get a little more speed. -#define NOENDIAN -#endif - - -vec3_t vec3_origin; - -fdef_t *ED_FieldAtOfs (progfuncs_t *progfuncs, unsigned int ofs); -pbool ED_ParseEpair (progfuncs_t *progfuncs, int qcptr, unsigned int fldofs, int fldtype, char *s); - -/* -================= -QC_ClearEdict - -Sets everything to NULL -================= -*/ -void QC_ClearEdict (progfuncs_t *progfuncs, struct edict_s *ed) -{ - edictrun_t *e = (edictrun_t *)ed; - int num = e->entnum; - memset (e->fields, 0, fields_size); - e->isfree = false; - e->entnum = num; -} - -edictrun_t *ED_AllocIntoTable (progfuncs_t *progfuncs, int num) -{ - edictrun_t *e; - - prinst->edicttable[num] = *(struct edict_s **)&e = (void*)memalloc(externs->edictsize); - memset(e, 0, externs->edictsize); - e->fields = PRAddressableExtend(progfuncs, fields_size); - e->entnum = num; - QC_ClearEdict(progfuncs, (struct edict_s*)e); - - return e; -} - -/* -================= -ED_Alloc - -Either finds a free edict, or allocates a new one. -Try to avoid reusing an entity that was recently freed, because it -can cause the client to think the entity morphed into something else -instead of being removed and recreated, which can cause interpolated -angles and bad trails. -================= -*/ -struct edict_s *ED_Alloc (progfuncs_t *progfuncs) -{ - unsigned int i; - edictrun_t *e; - - for ( i=0 ; iisfree && ( e->freetime < 2 || *externs->gametime - e->freetime > 0.5 ) )) - { - if (!e) - e = ED_AllocIntoTable(progfuncs, i); - else - QC_ClearEdict (progfuncs, (struct edict_s*)e); - - if (externs->entspawn) - externs->entspawn((struct edict_s *) e, false); - return (struct edict_s *)e; - } - } - - if (i >= maxedicts-1) //try again, but use timed out ents. - { - for ( i=0 ; iisfree)) - { - if (!e) - e = ED_AllocIntoTable(progfuncs, i); - else - QC_ClearEdict (progfuncs, (struct edict_s*)e); - - if (externs->entspawn) - externs->entspawn((struct edict_s *) e, false); - return (struct edict_s *)e; - } - } - - if (i >= maxedicts-2) - { - printf("Running out of edicts\n"); - pr_trace = 1; //trip the debugger whilst it's still valid - } - if (i >= maxedicts-1) - { - int size; - char *buf; - buf = progfuncs->save_ents(progfuncs, NULL, &size, 0); - progfuncs->parms->WriteFile("edalloc.dump", buf, size); - Sys_Error ("ED_Alloc: no free edicts"); - } - } - - sv_num_edicts++; - e = (edictrun_t*)EDICT_NUM(progfuncs, i); - - if (!e) - e = ED_AllocIntoTable(progfuncs, i); - else - QC_ClearEdict (progfuncs, (struct edict_s*)e); - - if (externs->entspawn) - externs->entspawn((struct edict_s *) e, false); - - return (struct edict_s *)e; -} - -/* -================= -ED_Free - -Marks the edict as free -FIXME: walk all entities and NULL out references to this entity -================= -*/ -void ED_Free (progfuncs_t *progfuncs, struct edict_s *ed) -{ - edictrun_t *e = (edictrun_t *)ed; -// SV_UnlinkEdict (ed); // unlink from world bsp - - if (e->isfree) //this happens on start.bsp where an onlyregistered trigger killtargets itself (when all of this sort die after 1 trigger anyway). - { - if (pr_depth) - printf("Tried to free free entity within %s\n", pr_xfunction->s_name+progfuncs->stringtable); - else - printf("Engine tried to free free entity\n"); -// if (developer.value == 1) -// pr_trace = true; - return; - } - - if (externs->entcanfree) - if (!externs->entcanfree(ed)) //can stop an ent from being freed. - return; - - e->isfree = true; - e->freetime = (float)*externs->gametime; - -/* - ed->v.model = 0; - ed->v.takedamage = 0; - ed->v.modelindex = 0; - ed->v.colormap = 0; - ed->v.skin = 0; - ed->v.frame = 0; - VectorCopy (vec3_origin, ed->v.origin); - VectorCopy (vec3_origin, ed->v.angles); - ed->v.nextthink = -1; - ed->v.solid = 0; -*/ -} - -//=========================================================================== - -/* -============ -ED_GlobalAtOfs -============ -*/ -ddef16_t *ED_GlobalAtOfs16 (progfuncs_t *progfuncs, int ofs) -{ - ddef16_t *def; - unsigned int i; - - for (i=0 ; inumglobaldefs ; i++) - { - def = &pr_globaldefs16[i]; - if (def->ofs == ofs) - return def; - } - return NULL; -} -ddef32_t *ED_GlobalAtOfs32 (progfuncs_t *progfuncs, unsigned int ofs) -{ - ddef32_t *def; - unsigned int i; - - for (i=0 ; inumglobaldefs ; i++) - { - def = &pr_globaldefs32[i]; - if (def->ofs == ofs) - return def; - } - return NULL; -} - -/* -============ -ED_FieldAtOfs -============ -*/ -fdef_t *ED_FieldAtOfs (progfuncs_t *progfuncs, unsigned int ofs) -{ -// ddef_t *def; - unsigned int i; - - for (i=0 ; inumglobaldefs ; i++) - { - def = &pr_globaldefs16[i]; - if (!strcmp(def->s_name+progfuncs->stringtable,name) ) - return def; - } - return NULL; -} -ddef32_t *ED_FindGlobal32 (progfuncs_t *progfuncs, char *name) -{ - ddef32_t *def; - unsigned int i; - - for (i=1 ; inumglobaldefs ; i++) - { - def = &pr_globaldefs32[i]; - if (!strcmp(def->s_name+progfuncs->stringtable,name) ) - return def; - } - return NULL; -} - -unsigned int ED_FindGlobalOfs (progfuncs_t *progfuncs, char *name) -{ - ddef16_t *d16; - ddef32_t *d32; - switch(current_progstate->structtype) - { - case PST_KKQWSV: - case PST_DEFAULT: - d16 = ED_FindGlobal16(progfuncs, name); - return d16?d16->ofs:0; - case PST_QTEST: - case PST_FTE32: - d32 = ED_FindGlobal32(progfuncs, name); - return d32?d32->ofs:0; - } - Sys_Error("ED_FindGlobalOfs - bad struct type"); - return 0; -} - -ddef16_t *ED_FindGlobalFromProgs16 (progfuncs_t *progfuncs, char *name, progsnum_t prnum) -{ - ddef16_t *def; - unsigned int i; - - for (i=1 ; inumglobaldefs ; i++) - { - def = &pr_progstate[prnum].globaldefs16[i]; - if (!strcmp(def->s_name+progfuncs->stringtable,name) ) - return def; - } - return NULL; -} -ddef32_t *ED_FindGlobalFromProgs32 (progfuncs_t *progfuncs, char *name, progsnum_t prnum) -{ - ddef32_t *def; - unsigned int i; - - for (i=1 ; inumglobaldefs ; i++) - { - def = &pr_progstate[prnum].globaldefs32[i]; - if (!strcmp(def->s_name+progfuncs->stringtable,name) ) - return def; - } - return NULL; -} - -ddef16_t *ED_FindTypeGlobalFromProgs16 (progfuncs_t *progfuncs, char *name, progsnum_t prnum, int type) -{ - ddef16_t *def; - unsigned int i; - - for (i=1 ; inumglobaldefs ; i++) - { - def = &pr_progstate[prnum].globaldefs16[i]; - if (!strcmp(def->s_name+progfuncs->stringtable,name) ) - { - if (pr_progstate[prnum].types) - { - if (pr_progstate[prnum].types[def->type&~DEF_SAVEGLOBAL].type != type) - continue; - } - else if ((def->type&(~DEF_SAVEGLOBAL)) != type) - continue; - return def; - } - } - return NULL; -} - - -ddef32_t *ED_FindTypeGlobalFromProgs32 (progfuncs_t *progfuncs, char *name, progsnum_t prnum, int type) -{ - ddef32_t *def; - unsigned int i; - - for (i=1 ; inumglobaldefs ; i++) - { - def = &pr_progstate[prnum].globaldefs32[i]; - if (!strcmp(def->s_name+progfuncs->stringtable,name) ) - { - if (pr_progstate[prnum].types) - { - if (pr_progstate[prnum].types[def->type&~DEF_SAVEGLOBAL].type != type) - continue; - } - else if ((def->type&(~DEF_SAVEGLOBAL)) != (unsigned)type) - continue; - return def; - } - } - return NULL; -} - -unsigned int *ED_FindGlobalOfsFromProgs (progfuncs_t *progfuncs, char *name, progsnum_t prnum, int type) -{ - ddef16_t *def16; - ddef32_t *def32; - static unsigned int pos; - switch(pr_progstate[prnum].structtype) - { - case PST_DEFAULT: - case PST_KKQWSV: - def16 = ED_FindTypeGlobalFromProgs16(progfuncs, name, prnum, type); - if (!def16) - return NULL; - pos = def16->ofs; - return &pos; - case PST_QTEST: - case PST_FTE32: - def32 = ED_FindTypeGlobalFromProgs32(progfuncs, name, prnum, type); - if (!def32) - return NULL; - return &def32->ofs; - } - Sys_Error("ED_FindGlobalOfsFromProgs - bad struct type"); - return 0; -} - -/* -============ -ED_FindFunction -============ -*/ -dfunction_t *ED_FindFunction (progfuncs_t *progfuncs, char *name, progsnum_t *prnum, progsnum_t fromprogs) -{ - dfunction_t *func; - unsigned int i; - char *sep; - - progsnum_t pnum; - - if (prnum) - { - sep = strchr(name, ':'); - if (sep) - { - pnum = atoi(name); - name = sep+1; - } - else - { - if (fromprogs>=0) - pnum = fromprogs; - else - pnum = pr_typecurrent; - } - *prnum = pnum; - } - else - pnum = pr_typecurrent; - - if ((unsigned)pnum > (unsigned)maxprogs) - { - printf("Progsnum %i out of bounds\n", pnum); - return NULL; - } - - if (!pr_progstate[pnum].progs) - return NULL; - - for (i=1 ; inumfunctions ; i++) - { - func = &pr_progstate[pnum].functions[i]; - if (!strcmp(func->s_name+progfuncs->stringtable,name) ) - return func; - } - return NULL; -} - -/* -============ -PR_ValueString - -Returns a string describing *data in a type specific manner -============= -*/ -char *PR_ValueString (progfuncs_t *progfuncs, etype_t type, eval_t *val) -{ - static char line[256]; - fdef_t *fielddef; - dfunction_t *f; - -#ifdef DEF_SAVEGLOBAL - type &= ~DEF_SAVEGLOBAL; -#endif - - if (current_progstate && pr_types) - type = pr_types[type].type; - - switch (type) - { - case ev_struct: - sprintf (line, "struct"); - break; - case ev_union: - sprintf (line, "union"); - break; - case ev_string: - sprintf (line, "%s", PR_StringToNative(progfuncs, val->string)); - break; - case ev_entity: - fielddef = ED_FindField(progfuncs, "classname"); - if (fielddef && val->edict < sv_num_edicts) - { - edictrun_t *ed; - string_t *v; - ed = (edictrun_t *)EDICT_NUM(progfuncs, val->edict); - v = (string_t *)((char *)edvars(ed) + fielddef->ofs*4); - sprintf (line, "entity %i(%s)", val->edict, PR_StringToNative(progfuncs, *v)); - } - else - sprintf (line, "entity %i", val->edict); - break; - case ev_function: - if (!val->function) - sprintf (line, "NULL function"); - else - { - if ((val->function & 0xff000000)>>24 >= (unsigned)maxprogs || !pr_progstate[(val->function & 0xff000000)>>24].functions) - sprintf (line, "Bad function"); - else - { - if ((val->function &~0xff000000) >= pr_progs->numfunctions) - sprintf(line, "bad function %i:%i\n", (val->function & 0xff000000)>>24, val->function & ~0xff000000); - else - { - f = pr_progstate[(val->function & 0xff000000)>>24].functions + (val->function & ~0xff000000); - sprintf (line, "%i:%s()", (val->function & 0xff000000)>>24, f->s_name+progfuncs->stringtable); - } - } - } - break; - case ev_field: - fielddef = ED_FieldAtOfs (progfuncs, val->_int ); - if (!fielddef) - sprintf (line, ".??? (%i)", val->_int); - else - sprintf (line, ".%s (%i)", fielddef->name, val->_int); - break; - case ev_void: - sprintf (line, "void type"); - break; - case ev_float: - sprintf (line, "%5.1f", val->_float); - break; - case ev_integer: - sprintf (line, "%i", val->_int); - break; - case ev_vector: - sprintf (line, "'%5.1f %5.1f %5.1f'", val->_vector[0], val->_vector[1], val->_vector[2]); - break; - case ev_pointer: - sprintf (line, "pointer"); - { -// int entnum; -// int valofs; - if (val->_int == 0) - { - sprintf (line, "NULL pointer"); - break; - } - //FIXME: :/ - sprintf(line, "UNKNOWN"); -// entnum = ((qbyte *)val->edict - (qbyte *)sv_edicts) / pr_edict_size; -// valofs = (int *)val->edict - (int *)edvars(EDICT_NUM(progfuncs, entnum)); -// fielddef = ED_FieldAtOfs (progfuncs, valofs ); -// if (!fielddef) -// sprintf(line, "ent%i.%s", entnum, "UNKNOWN"); -// else -// sprintf(line, "ent%i.%s", entnum, fielddef->s_name); - } - break; - default: - sprintf (line, "bad type %i", type); - break; - } - - return line; -} - -/* -============ -PR_UglyValueString - -Returns a string describing *data in a type specific manner -Easier to parse than PR_ValueString -============= -*/ -char *PR_UglyValueString (progfuncs_t *progfuncs, etype_t type, eval_t *val) -{ - static char line[256]; - fdef_t *fielddef; - dfunction_t *f; - int i, j; - -#ifdef DEF_SAVEGLOBAL - type &= ~DEF_SAVEGLOBAL; -#endif - -// if (pr_types) -// type = pr_types[type].type; - - switch (type) - { - case ev_struct: - sprintf (line, "structures cannot yet be saved"); - break; - case ev_union: - sprintf (line, "unions cannot yet be saved"); - break; - case ev_string: - { - char *outs = line; - int outb = sizeof(line)-2; - char *ins = PR_StringToNative(progfuncs, val->string); - //markup the output string. - while(*ins && outb > 0) - { - switch(*ins) - { - case '\n': - *outs++ = '\\'; - *outs++ = 'n'; - ins++; - outb-=2; - break; - case '\"': - *outs++ = '\\'; - *outs++ = '"'; - ins++; - outb-=2; - break; - case '\\': - *outs++ = '\\'; - *outs++ = '\\'; - ins++; - outb-=2; - break; - default: - *outs++ = *ins++; - outb--; - break; - } - } - *outs = 0; - } - break; - case ev_entity: - sprintf (line, "%i", val->_int); - break; - case ev_function: - i = (val->function & 0xff000000)>>24; //progs number - if ((unsigned)i >= maxprogs || !pr_progstate[(unsigned)i].progs) - sprintf (line, "BAD FUNCTION INDEX: %i", val->function); - else - { - j = (val->function & ~0xff000000); //function number - if ((unsigned)j >= pr_progstate[(unsigned)i].progs->numfunctions) - sprintf(line, "%i:%s", i, "CORRUPT FUNCTION POINTER"); - else - { - f = pr_progstate[(unsigned)i].functions + j; - sprintf (line, "%i:%s", i, f->s_name+progfuncs->stringtable); - } - } - break; - case ev_field: - fielddef = ED_FieldAtOfs (progfuncs, val->_int ); - sprintf (line, "%s", fielddef->name); - break; - case ev_void: - sprintf (line, "void"); - break; - case ev_float: - if (val->_float == (int)val->_float) - sprintf (line, "%i", (int)val->_float); //an attempt to cut down on the number of .000000 vars.. - else - sprintf (line, "%f", val->_float); - break; - case ev_integer: - sprintf (line, "%i", val->_int); - break; - case ev_vector: - if (val->_vector[0] == (int)val->_vector[0] && val->_vector[1] == (int)val->_vector[1] && val->_vector[2] == (int)val->_vector[2]) - sprintf (line, "%i %i %i", (int)val->_vector[0], (int)val->_vector[1], (int)val->_vector[2]); - else - sprintf (line, "%f %f %f", val->_vector[0], val->_vector[1], val->_vector[2]); - break; - default: - sprintf (line, "bad type %i", type); - break; - } - - return line; -} - -//compatible with Q1 (for savegames) -char *PR_UglyOldValueString (progfuncs_t *progfuncs, etype_t type, eval_t *val) -{ - static char line[256]; - fdef_t *fielddef; - dfunction_t *f; - -#ifdef DEF_SAVEGLOBAL - type &= ~DEF_SAVEGLOBAL; -#endif - - if (pr_types) - type = pr_types[type].type; - - switch (type) - { - case ev_struct: - sprintf (line, "structures cannot yet be saved"); - break; - case ev_union: - sprintf (line, "unions cannot yet be saved"); - break; - case ev_string: - sprintf (line, "%s", PR_StringToNative(progfuncs, val->string)); - break; - case ev_entity: - sprintf (line, "%i", NUM_FOR_EDICT(progfuncs, (struct edict_s *)PROG_TO_EDICT(progfuncs, val->edict))); - break; - case ev_function: - f = pr_progstate[(val->function & 0xff000000)>>24].functions + (val->function & ~0xff000000); - sprintf (line, "%s", f->s_name+progfuncs->stringtable); - break; - case ev_field: - fielddef = ED_FieldAtOfs (progfuncs, val->_int ); - sprintf (line, "%s", fielddef->name); - break; - case ev_void: - sprintf (line, "void"); - break; - case ev_float: - if (val->_float == (int)val->_float) - sprintf (line, "%i", (int)val->_float); //an attempt to cut down on the number of .000000 vars.. - else - sprintf (line, "%f", val->_float); - break; - case ev_integer: - sprintf (line, "%i", val->_int); - break; - case ev_vector: - if (val->_vector[0] == (int)val->_vector[0] && val->_vector[1] == (int)val->_vector[1] && val->_vector[2] == (int)val->_vector[2]) - sprintf (line, "%i %i %i", (int)val->_vector[0], (int)val->_vector[1], (int)val->_vector[2]); - else - sprintf (line, "%f %f %f", val->_vector[0], val->_vector[1], val->_vector[2]); - break; - break; - default: - sprintf (line, "bad type %i", type); - break; - } - - return line; -} - -char *PR_TypeString(progfuncs_t *progfuncs, etype_t type) -{ -#ifdef DEF_SAVEGLOBAL - type &= ~DEF_SAVEGLOBAL; -#endif - - if (pr_types) - type = pr_types[type].type; - - switch (type) - { - case ev_struct: - return "struct"; - case ev_union: - return "union"; - case ev_string: - return "string"; - case ev_entity: - return "entity"; - case ev_function: - return "function"; - case ev_field: - return "field"; - case ev_void: - return "void"; - case ev_float: - return "float"; - case ev_vector: - return "vector"; - case ev_integer: - return "integer"; - default: - return "BAD TYPE"; - } -} - -/* -============ -PR_GlobalString - -Returns a string with a description and the contents of a global, -padded to 20 field width -============ -*/ -char *PR_GlobalString (progfuncs_t *progfuncs, int ofs) -{ - char *s; - int i; - ddef16_t *def16; - ddef32_t *def32; - void *val; - static char line[128]; - - switch (current_progstate->structtype) - { - case PST_DEFAULT: - case PST_KKQWSV: - val = (void *)&pr_globals[ofs]; - def16 = ED_GlobalAtOfs16(progfuncs, ofs); - if (!def16) - sprintf (line,"%i(?""?""?)", ofs); - else - { - s = PR_ValueString (progfuncs, def16->type, val); - sprintf (line,"%i(%s)%s", ofs, def16->s_name+progfuncs->stringtable, s); - } - - i = strlen(line); - for ( ; i<20 ; i++) - strcat (line," "); - strcat (line," "); - return line; - case PST_QTEST: - case PST_FTE32: - val = (void *)&pr_globals[ofs]; - def32 = ED_GlobalAtOfs32(progfuncs, ofs); - if (!def32) - sprintf (line,"%i(?""?""?)", ofs); - else - { - s = PR_ValueString (progfuncs, def32->type, val); - sprintf (line,"%i(%s)%s", ofs, def32->s_name+progfuncs->stringtable, s); - } - - i = strlen(line); - for ( ; i<20 ; i++) - strcat (line," "); - strcat (line," "); - return line; - } - Sys_Error("Bad struct type in PR_GlobalString"); - return ""; -} - -char *PR_GlobalStringNoContents (progfuncs_t *progfuncs, int ofs) -{ - int i; - ddef16_t *def16; - ddef32_t *def32; - static char line[128]; - - switch (current_progstate->structtype) - { - case PST_DEFAULT: - case PST_KKQWSV: - def16 = ED_GlobalAtOfs16(progfuncs, ofs); - if (!def16) - sprintf (line,"%i(?""?""?)", ofs); - else - sprintf (line,"%i(%s)", ofs, def16->s_name+progfuncs->stringtable); - break; - case PST_QTEST: - case PST_FTE32: - def32 = ED_GlobalAtOfs32(progfuncs, ofs); - if (!def32) - sprintf (line,"%i(?""?""?)", ofs); - else - sprintf (line,"%i(%s)", ofs, def32->s_name+progfuncs->stringtable); - break; - default: - Sys_Error("Bad struct type in PR_GlobalStringNoContents"); - } - - i = strlen(line); - for ( ; i<20 ; i++) - strcat (line," "); - strcat (line," "); - - return line; -} - - -/* -============= -ED_Print - -For debugging -============= -*/ -void ED_Print (progfuncs_t *progfuncs, struct edict_s *ed) -{ - int l; - fdef_t *d; - int *v; - unsigned int i;unsigned int j; - char *name; - int type; - - if (((edictrun_t *)ed)->isfree) - { - printf ("FREE\n"); - return; - } - - printf("\nEDICT %i:\n", NUM_FOR_EDICT(progfuncs, (struct edict_s *)ed)); - for (i=1 ; iname; - l = strlen(name); - if (l >= 2 && name[l-2] == '_') - continue; // skip _x, _y, _z vars - - v = (int *)((char *)edvars(ed) + d->ofs*4); - - // if the value is still all 0, skip the field -#ifdef DEF_SAVEGLOBAL - type = d->type & ~DEF_SAVEGLOBAL; -#else - type = d->type; -#endif - - for (j=0 ; jtype, (eval_t *)v)); - } -} - -void ED_PrintNum (progfuncs_t *progfuncs, int ent) -{ - ED_Print (progfuncs, EDICT_NUM(progfuncs, ent)); -} - -/* -============= -ED_PrintEdicts - -For debugging, prints all the entities in the current server -============= -*/ -void ED_PrintEdicts (progfuncs_t *progfuncs) -{ - unsigned int i; - - printf ("%i entities\n", sv_num_edicts); - for (i=0 ; iisfree) - continue; - active++; -// if (ent->v.solid) -// solid++; -// if (ent->v.model) -// models++; -// if (ent->v.movetype == MOVETYPE_STEP) -// step++; - } - - printf ("num_edicts:%3i\n", sv_num_edicts); - printf ("active :%3i\n", active); -// Con_Printf ("view :%3i\n", models); -// Con_Printf ("touch :%3i\n", solid); -// Con_Printf ("step :%3i\n", step); - -} - - - -//============================================================================ - - -/* -============= -ED_NewString -============= -*/ -char *ED_NewString (progfuncs_t *progfuncs, char *string, int minlength) -{ - char *newc, *new_p; - int i,l; - - minlength++; - - l = strlen(string) + 1; - - newc = progfuncs->AddressableAlloc (progfuncs, lstringtable; - - new_p = newc; - - for (i=0 ; i< l ; i++) - { - if (string[i] == '\\' && i < l-1 && string[i+1] != 0) - { - i++; - if (string[i] == 'n') - *new_p++ = '\n'; - else - *new_p++ = '\\'; - } - else - *new_p++ = string[i]; - } - - return newc; -} - - -/* -============= -ED_ParseEval - -Can parse either fields or globals -returns false if error -============= -*/ -pbool ED_ParseEpair (progfuncs_t *progfuncs, int qcptr, unsigned int fldofs, int fldtype, char *s) -{ - int i; - char string[128]; - fdef_t *def; - char *v, *w; - string_t st; - dfunction_t *func; - int type = fldtype & ~DEF_SAVEGLOBAL; - qcptr += fldofs*sizeof(int); - - switch (type) - { - case ev_string: - st = PR_StringToProgs(progfuncs, ED_NewString (progfuncs, s, 0)); - *(string_t *)(progfuncs->stringtable + qcptr) = st; - break; - - case ev_float: - *(float *)(progfuncs->stringtable + qcptr) = (float)atof (s); - break; - - case ev_integer: - *(int *)(progfuncs->stringtable + qcptr) = atoi (s); - break; - - case ev_vector: - strcpy (string, s); - v = string; - w = string; - for (i=0 ; i<3 ; i++) - { - while (*v && *v != ' ') - v++; - if (!*v) - { - ((float *)(progfuncs->stringtable + qcptr))[i] = (float)atof (w); - w = v; - } - else - { - *v = 0; - ((float *)(progfuncs->stringtable + qcptr))[i] = (float)atof (w); - w = v = v+1; - } - } - break; - - case ev_entity: - *(int *)(progfuncs->stringtable + qcptr) = atoi (s); - break; - - case ev_field: - def = ED_FindField (progfuncs, s); - if (!def) - { - printf ("Can't find field %s\n", s); - return false; - } - *(int *)(progfuncs->stringtable + qcptr) = def->ofs; - break; - - case ev_function: - if (s[1]==':'&&s[2]=='\0') - { - *(func_t *)(progfuncs->stringtable + qcptr) = 0; - return true; - } - func = ED_FindFunction (progfuncs, s, &i, -1); - if (!func) - { - printf ("Can't find function %s\n", s); - return false; - } - *(func_t *)(progfuncs->stringtable + qcptr) = (func - pr_progstate[i].functions) | (i<<24); - break; - - default: - break; - } - return true; -} - -/* -==================== -ED_ParseEdict - -Parses an edict out of the given string, returning the new position -ed should be a properly initialized empty edict. -Used for initial level load and for savegames. -==================== -*/ -#if 1 -char *ED_ParseEdict (progfuncs_t *progfuncs, char *data, edictrun_t *ent) -{ - fdef_t *key; - pbool init; - char keyname[256]; - int n; - int nest = 1; - -// eval_t *val; - - init = false; - -// clear it -// if (ent != (edictrun_t *)sv_edicts) // hack -// memset (ent+1, 0, pr_edict_size - sizeof(edictrun_t)); - -// go through all the dictionary pairs - while (1) - { - // parse key - data = QCC_COM_Parse (data); - if (qcc_token[0] == '}') - { - if (--nest) - continue; - break; - } - if (qcc_token[0] == '{' && !qcc_token[1]) - nest++; - if (!data) - { - printf ("ED_ParseEntity: EOF without closing brace\n"); - return NULL; - } - if (nest > 1) - continue; - - strncpy (keyname, qcc_token, sizeof(keyname)-1); - keyname[sizeof(keyname)-1] = 0; - - // another hack to fix heynames with trailing spaces - n = strlen(keyname); - while (n && keyname[n-1] == ' ') - { - keyname[n-1] = 0; - n--; - } - - // parse value - data = QCC_COM_Parse (data); - if (!data) - { - printf ("ED_ParseEntity: EOF without closing brace\n"); - return NULL; - } - - if (qcc_token[0] == '}') - { - printf ("ED_ParseEntity: closing brace without data\n"); - return NULL; - } - - init = true; - -// keynames with a leading underscore are used for utility comments, -// and are immediately discarded by quake - if (keyname[0] == '_') - continue; - - key = ED_FindField (progfuncs, keyname); - if (!key) - { - if (!strcmp(keyname, "angle")) //Quake anglehack - we've got to leave it in cos it doesn't work for quake otherwise, and this is a QuakeC lib! - { - if ((key = ED_FindField (progfuncs, "angles"))) - { - sprintf (qcc_token, "0 %f 0", atof(qcc_token)); //change it from yaw to 3d angle - goto cont; - } - } - if (!strcmp(keyname, "light")) //Quake lighthack - allows a field name and a classname to go by the same thing in the level editor - if ((key = ED_FindField (progfuncs, "light_lev"))) - goto cont; - if (externs->badfield && externs->badfield(progfuncs, (struct edict_s*)ent, keyname, qcc_token)) - continue; - printf ("'%s' is not a field\n", keyname); - continue; - } - -cont: - if (!ED_ParseEpair (progfuncs, (char*)ent->fields - progfuncs->stringtable, key->ofs, key->type, qcc_token)) - { - continue; -// Sys_Error ("ED_ParseEdict: parse error on entities"); - } - } - - if (!init) - ent->isfree = true; - - return data; -} -#endif - -/* -================ -ED_LoadFromFile - -The entities are directly placed in the array, rather than allocated with -ED_Alloc, because otherwise an error loading the map would have entity -number references out of order. - -Creates a server's entity / program execution context by -parsing textual entity definitions out of an ent file. - -Used for both fresh maps and savegame loads. A fresh map would also need -to call ED_CallSpawnFunctions () to let the objects initialize themselves. -================ -*/ - -char *ED_WriteGlobals(progfuncs_t *progfuncs, char *buffer) //switch first. -{ -#define AddS(str) strcpy(buffer, str);buffer+=strlen(str); - int *v; - ddef32_t *def32; - ddef16_t *def16; - unsigned int i; - unsigned int j; - char *name; - int type; - int curprogs = pr_typecurrent; - int len; - switch(current_progstate->structtype) - { - case PST_DEFAULT: - case PST_KKQWSV: - for (i=0 ; inumglobaldefs ; i++) - { - def16 = &pr_globaldefs16[i]; - name = def16->s_name + progfuncs->stringtable; - len = strlen(name); - if (!*name) - continue; - if (name[len-2] == '_' && (name[len-1] == 'x' || name[len-1] == 'y' || name[len-1] == 'z')) - continue; // skip _x, _y, _z vars (vector components, which are saved as one vector not 3 floats) - - type = def16->type; - -#ifdef DEF_SAVEGLOBAL - if ( !(def16->type & DEF_SAVEGLOBAL) ) - continue; - type &= ~DEF_SAVEGLOBAL; -#endif - if (current_progstate->types) - type = current_progstate->types[type].type; - if (type == ev_function) - { - v = (int *)¤t_progstate->globals[def16->ofs]; - if ((v[0]&0xff000000)>>24 == (unsigned)curprogs) //same progs - { - if (!progfuncs->stringtable[current_progstate->functions[v[0]&0x00ffffff].s_name]) - continue; - else if (!strcmp(current_progstate->functions[v[0]&0x00ffffff].s_name+ progfuncs->stringtable, name)) //names match. Assume function is at initial value. - continue; - } - - if (curprogs!=0) - if ((v[0]&0xff000000)>>24 == 0) - if (!ED_FindFunction(progfuncs, name, NULL, curprogs)) //defined as extern - { - if (!progfuncs->stringtable[pr_progstate[0].functions[v[0]&0x00ffffff].s_name]) - continue; - else if (!strcmp(pr_progstate[0].functions[v[0]&0x00ffffff].s_name + progfuncs->stringtable, name)) //same name. - continue; - } - - //else function has been redirected externally. - goto add16; - } - else if (type != ev_string //anything other than these is not saved - && type != ev_float - && type != ev_integer - && type != ev_entity - && type != ev_vector) - continue; - - v = (int *)¤t_progstate->globals[def16->ofs]; - - // make sure the value is not null, where there's no point in saving - for (j=0 ; jtype&~DEF_SAVEGLOBAL, (eval_t *)v))); - } - break; - case PST_QTEST: - case PST_FTE32: - for (i=0 ; inumglobaldefs ; i++) - { - def32 = &pr_globaldefs32[i]; - name = def32->s_name + progfuncs->stringtable; - if (name[strlen(name)-2] == '_') - continue; // skip _x, _y, _z vars (vector components, which are saved as one vector not 3 floats) - - type = def32->type; - -#ifdef DEF_SAVEGLOBAL - if ( !(def32->type & DEF_SAVEGLOBAL) ) - continue; - type &= ~DEF_SAVEGLOBAL; -#endif - if (current_progstate->types) - type = current_progstate->types[type].type; - if (type == ev_function) - { - v = (int *)¤t_progstate->globals[def32->ofs]; - if ((v[0]&0xff000000)>>24 == (unsigned)curprogs) //same progs - if (!strcmp(current_progstate->functions[v[0]&0x00ffffff].s_name+ progfuncs->stringtable, name)) //names match. Assume function is at initial value. - continue; - - if (curprogs!=0) - if ((v[0]&0xff000000)>>24 == 0) - if (!ED_FindFunction(progfuncs, name, NULL, curprogs)) //defined as extern - if (!strcmp(pr_progstate[0].functions[v[0]&0x00ffffff].s_name+ progfuncs->stringtable, name)) //same name. - continue; - - //else function has been redirected externally. - goto add32; - } - else if (type != ev_string //anything other than these is not saved - && type != ev_float - && type != ev_integer - && type != ev_entity - && type != ev_vector) - continue; - - v = (int *)¤t_progstate->globals[def32->ofs]; - - // make sure the value is not null, where there's no point in saving - for (j=0 ; jtype&~DEF_SAVEGLOBAL, (eval_t *)v))); - } - break; - default: - Sys_Error("Bad struct type in SaveEnts"); - } - - return buffer; -} - -char *ED_WriteEdict(progfuncs_t *progfuncs, edictrun_t *ed, char *buffer, pbool q1compatible) -{ - fdef_t *d; - - int *v; - unsigned int i;unsigned int j; - char *name; - int type; - int len; - - for (i=0 ; iname; - len = strlen(name); - if (len>4 && (name[len-2] == '_' && (name[len-1] == 'x' || name[len-1] == 'y' || name[len-1] == 'z'))) - continue; // skip _x, _y, _z vars - - v = (int *)((char*)ed->fields + d->ofs*4); - - // if the value is still all 0, skip the field -#ifdef DEF_SAVEGLOBAL - type = d->type & ~DEF_SAVEGLOBAL; -#else - type = d->type; -#endif - - for (j=0 ; jtype, (eval_t *)v))); - } - - return buffer; -#undef AddS -} - -char *SaveCallStack (progfuncs_t *progfuncs, char *s) -{ -#define AddS(str) strcpy(s, str);s+=strlen(str); - char buffer[8192]; - dfunction_t *f; - int i; - int progs; - - int arg; - int *globalbase; - - progs = -1; - - if (pr_depth == 0) - { - AddS ("\n"); - return s; - } - - globalbase = (int *)pr_globals + pr_xfunction->parm_start + pr_xfunction->locals; - - pr_stack[pr_depth].f = pr_xfunction; - for (i=pr_depth ; i>0 ; i--) - { - f = pr_stack[i].f; - - if (!f) - { - AddS ("\n"); - } - else - { - if (pr_stack[i].progsnum != progs) - { - progs = pr_stack[i].progsnum; - - sprintf(buffer, "//%i %s\n", progs, pr_progstate[progs].filename); - AddS (buffer); - } - if (!f->s_file) - sprintf(buffer, "\t\"%i:%s\"\n", progs, f->s_name+progfuncs->stringtable); - else - sprintf(buffer, "\t\"%i:%s\" //%s\n", progs, f->s_name+progfuncs->stringtable, f->s_file+progfuncs->stringtable); - AddS (buffer); - - AddS ("\t{\n"); - for (arg = 0; arg < f->locals; arg++) - { - ddef16_t *local; - local = ED_GlobalAtOfs16(progfuncs, f->parm_start+arg); - if (!local) - sprintf(buffer, "\t\tofs%i %i // %f\n", f->parm_start+arg, *(int *)(globalbase - f->locals+arg), *(float *)(globalbase - f->locals+arg) ); - else - { - if (local->type == ev_entity) - { - sprintf(buffer, "\t\t\"%s\" \"entity %i\"\n", local->s_name+progfuncs->stringtable, ((eval_t*)(globalbase - f->locals+arg))->edict); - } - else - sprintf(buffer, "\t\t\"%s\"\t\"%s\"\n", local->s_name+progfuncs->stringtable, PR_ValueString(progfuncs, local->type, (eval_t*)(globalbase - f->locals+arg))); - - if (local->type == ev_vector) - arg+=2; - } - AddS (buffer); - } - AddS ("\t}\n"); - - if (i == pr_depth) - globalbase = localstack + localstack_used - f->locals; - else - globalbase -= f->locals; - } - } - return s; -#undef AddS -} - -//there are two ways of saving everything. -//0 is to save just the entities. -//1 is to save the entites, and all the progs info so that all the variables are saved off, and it can be reloaded to exactly how it was (provided no files or data has been changed outside, like the progs.dat for example) -char *SaveEnts(progfuncs_t *progfuncs, char *mem, int *len, int alldata) -{ -#define AddS(str) strcpy(s, str);s+=strlen(str); - char *s, *os; - unsigned int a; - int oldprogs; - - if (mem) - { - os = s = mem; - } - else - os = s = memalloc(5*1024*1024); - - if (alldata == 2) - { //special Q1 savegame compatability mode. - //engine will need to store references to progs type and will need to preload the progs and inti the ents itself before loading. - - //Make sure there is only 1 progs loaded. - for (a = 1; a < maxprogs; a++) - { - if (pr_progstate[a].progs) - break; - } - if (!pr_progstate[0].progs || a != maxprogs) //the state of the progs wasn't Q1 compatible. - { - memfree(os); - return NULL; - } - - //write the globals - AddS ("{\n"); - - oldprogs = pr_typecurrent; - PR_SwitchProgs(progfuncs, 0); - - s = ED_WriteGlobals(progfuncs, s); - - PR_SwitchProgs(progfuncs, oldprogs); - - AddS ("}\n"); - - - //write the ents - for (a = 0; a < sv_num_edicts; a++) - { - edictrun_t *ed = (edictrun_t *)EDICT_NUM(progfuncs, a); - - AddS ("{\n"); - - if (!ed->isfree) - s = ED_WriteEdict(progfuncs, ed, s, true); - - AddS ("}\n"); - } - - *len = s - os; - return os; - } - - if (alldata) - { - AddS("general {\n"); - AddS(qcva("\"maxprogs\" \"%i\"\n", maxprogs)); -// AddS(qcva("\"maxentities\" \"%i\"\n", maxedicts)); -// AddS(qcva("\"mem\" \"%i\"\n", hunksize)); -// AddS(qcva("\"crc\" \"%i\"\n", header_crc)); - AddS(qcva("\"numentities\" \"%i\"\n", sv_num_edicts)); - AddS("}\n"); - - oldprogs = pr_typecurrent; - - for (a = 0; a < maxprogs; a++) - { - if (!pr_progstate[a].progs) - continue; - PR_SwitchProgs(progfuncs, a); - { - AddS (qcva("progs %i {\n", a)); - AddS (qcva("\"filename\" \"%s\"\n", pr_progstate[a].filename)); - AddS (qcva("\"crc\" \"%i\"\n", pr_progs->crc)); - AddS (qcva("\"numbuiltins\" \"%i\"\n", current_progstate->numbuiltins)); - AddS ("}\n"); - } - } - - if (alldata == 3) - { - //include callstack - AddS("stacktrace {\n"); - s = SaveCallStack(progfuncs, s); - AddS("}\n"); - } - - for (a = 0; a < maxprogs; a++) //I would mix, but external functions rely on other progs being loaded - { - if (!pr_progstate[a].progs) - continue; - - AddS (qcva("globals %i {\n", a)); - - PR_SwitchProgs(progfuncs, a); - - s = ED_WriteGlobals(progfuncs, s); - - AddS ("}\n"); - } - PR_SwitchProgs(progfuncs, oldprogs); - } - for (a = 0; a < sv_num_edicts; a++) - { - edictrun_t *ed = (edictrun_t *)EDICT_NUM(progfuncs, a); - - if (ed->isfree) - continue; - - AddS (qcva("entity %i{\n", a)); - - s = ED_WriteEdict(progfuncs, ed, s, false); - - AddS ("}\n"); - } - - *len = s - os; - return os; - -#undef AddS -} - -int header_crc; - -//if 'general' block is found, this is a compleate state, otherwise, we should spawn entities like -int LoadEnts(progfuncs_t *progfuncs, char *file, float killonspawnflags) -{ - eval_t *fulldata; //this is part of FTE_FULLSPAWNDATA - char *datastart; - - eval_t *selfvar = NULL; - eval_t *var; - - char filename[128]; - int num; - int numbuiltins; - edictrun_t *ed=NULL; - ddef16_t *d16; - ddef32_t *d32; - func_t CheckSpawn=0; - void *oldglobals = NULL; - int oldglobalssize = 0; - - extern edictrun_t tempedict; - - int crc = 1; - int entsize = 0; - int numents = 0; - - pbool resethunk=0; - pbool isloadgame; - if (!strncmp(file, "loadgame", 8)) - { - isloadgame = true; - numents = -1; - file+=8; - fulldata = NULL; - } - else - { - isloadgame = false; - - fulldata = PR_FindGlobal(progfuncs, "__fullspawndata", PR_ANY, NULL); - } - - while(1) - { - datastart = file; - file = QCC_COM_Parse(file); - if (file == NULL) - break; //finished reading file - else if (!strcmp(qcc_token, "Version")) - { - file = QCC_COM_Parse(file); - //qcc_token is a version number - } - else if (!strcmp(qcc_token, "entity")) - { - if (entsize == 0 && resethunk) //edicts have not yet been initialized, and this is a compleate load (memsize has been set) - { - entsize = PR_InitEnts(progfuncs, maxedicts); -// sv_num_edicts = numents; - - for (num = 0; num < numents; num++) - { - ed = (edictrun_t *)EDICT_NUM(progfuncs, num); - - if (!ed) - { - ed = ED_AllocIntoTable(progfuncs, num); - ed->isfree = true; - if (externs->entspawn) - externs->entspawn((struct edict_s *) ed, true); - } - } - } - - file = QCC_COM_Parse(file); - num = atoi(qcc_token); - file = QCC_COM_Parse(file); - if (qcc_token[0] != '{') - Sys_Error("Progs loading found %s, not '{'", qcc_token); - if (!resethunk) - ed = (edictrun_t *)ED_Alloc(progfuncs); - else - { - ed = (edictrun_t *)EDICT_NUM(progfuncs, num); - - if (!ed) - { - Sys_Error("Edict was not allocated\n"); - ed = ED_AllocIntoTable(progfuncs, num); - } - } - ed->isfree = false; - if (externs->entspawn) - externs->entspawn((struct edict_s *) ed, true); - file = ED_ParseEdict(progfuncs, file, ed); - - if (killonspawnflags) - { - var = GetEdictFieldValue (progfuncs, (struct edict_s *)&ed, "spawnflags", &spawnflagscache); - if (var) - { - if ((int)var->_float & (int)killonspawnflags) - { - ed->isfree = true; - continue; - } - } - } - - if (!resethunk) - { - dfunction_t *f; - if ((var = GetEdictFieldValue (progfuncs, (struct edict_s *)ed, "classname", NULL))) - { - f = ED_FindFunction(progfuncs, var->string + progfuncs->stringtable, NULL, -1); - if (f) - { - var = (eval_t *)((int *)pr_globals + ED_FindGlobalOfs(progfuncs, "self")); - var->edict = EDICT_TO_PROG(progfuncs, ed); - PR_ExecuteProgram(progfuncs, f-pr_functions); - } - } - } - } - else if (!strcmp(qcc_token, "progs")) - { - file = QCC_COM_Parse(file); - num = atoi(qcc_token); - file = QCC_COM_Parse(file); - if (qcc_token[0] != '{') - Sys_Error("Progs loading found %s, not '{'", qcc_token); - - - filename[0] = '\0'; - header_crc = 0; - numbuiltins = 0; - - while(1) - { - file = QCC_COM_Parse(file); //read the key - if (!file) - Sys_Error("EOF in progs block"); - - if (!strcmp("filename", qcc_token)) //check key get and save values - {file = QCC_COM_Parse(file); strcpy(filename, qcc_token);} - else if (!strcmp("crc", qcc_token)) - {file = QCC_COM_Parse(file); header_crc = atoi(qcc_token);} - else if (!strcmp("numbuiltins", qcc_token)) - {file = QCC_COM_Parse(file); numbuiltins = atoi(qcc_token);} - else if (qcc_token[0] == '}') //end of block - break; - else - Sys_Error("Bad key \"%s\" in progs block", qcc_token); - } - - PR_ReallyLoadProgs(progfuncs, filename, header_crc, &pr_progstate[num], true); - if (!externs->builtinsfor) - { - // Sys_Error("Couldn't reset the builtin functions"); - current_progstate->builtins = NULL; //these are specific, we assume the global ones were set via pr_configure - current_progstate->numbuiltins = 0; - } - else - { - current_progstate->builtins = externs->builtinsfor(num, header_crc); - current_progstate->numbuiltins = numbuiltins; - } - - if (num == 0 && oldglobals) - { - if (pr_progstate[0].globals_size == oldglobalssize) - memcpy(pr_progstate[0].globals, oldglobals, pr_progstate[0].globals_size); - free(oldglobals); - oldglobals = NULL; - } - } - else if (!strcmp(qcc_token, "globals")) - { - if (entsize == 0 && resethunk) //by the time we parse some globals, we MUST have loaded all progs - { - entsize = PR_InitEnts(progfuncs, maxedicts); -// sv_num_edicts = numents; - - for (num = 0; num < numents; num++) - { - ed = (edictrun_t *)EDICT_NUM(progfuncs, num); - - if (!ed) - { - ed = ED_AllocIntoTable(progfuncs, num); - ed->isfree = true; - } - - if (externs->entspawn) - externs->entspawn((struct edict_s *) ed, true); - } - } - - file = QCC_COM_Parse(file); - num = atoi(qcc_token); - - file = QCC_COM_Parse(file); - if (qcc_token[0] != '{') - Sys_Error("Globals loading found \'%s\', not '{'", qcc_token); - - PR_SwitchProgs(progfuncs, num); - while (1) - { - file = QCC_COM_Parse(file); - if (qcc_token[0] == '}') - break; - else if (!qcc_token[0] || !file) - Sys_Error("EOF when parsing global values"); - - switch(current_progstate->structtype) - { - case PST_DEFAULT: - case PST_KKQWSV: - if (!(d16 = ED_FindGlobal16(progfuncs, qcc_token))) - { - file = QCC_COM_Parse(file); - printf("global value %s not found", qcc_token); - } - else - { - file = QCC_COM_Parse(file); - ED_ParseEpair(progfuncs, (char*)pr_globals - progfuncs->stringtable, d16->ofs, d16->type, qcc_token); - } - break; - case PST_QTEST: - case PST_FTE32: - if (!(d32 = ED_FindGlobal32(progfuncs, qcc_token))) - { - file = QCC_COM_Parse(file); - printf("global value %s not found", qcc_token); - } - else - { - file = QCC_COM_Parse(file); - ED_ParseEpair(progfuncs, (char*)pr_globals - progfuncs->stringtable, d32->ofs, d32->type, qcc_token); - } - break; - default: - Sys_Error("Bad struct type in LoadEnts"); - } - } - -// file = QCC_COM_Parse(file); -// if (com_token[0] != '}') -// Sys_Error("Progs loading found %s, not '}'", qcc_token); - } - else if (!strcmp(qcc_token, "general")) - { - QC_StartShares(progfuncs); -// QC_InitShares(); //forget stuff -// pr_edict_size = 0; - max_fields_size=0; - - file = QCC_COM_Parse(file); - if (qcc_token[0] != '{') - Sys_Error("Progs loading found %s, not '{'", qcc_token); - - while(1) - { - file = QCC_COM_Parse(file); //read the key - if (!file) - Sys_Error("EOF in general block"); - - if (!strcmp("maxprogs", qcc_token)) //check key get and save values - {file = QCC_COM_Parse(file); maxprogs = atoi(qcc_token);} -// else if (!strcmp("maxentities", com_token)) -// {file = QCC_COM_Parse(file); maxedicts = atoi(qcc_token);} -// else if (!strcmp("mem", com_token)) -// {file = QCC_COM_Parse(file); memsize = atoi(qcc_token);} -// else if (!strcmp("crc", com_token)) -// {file = QCC_COM_Parse(file); crc = atoi(qcc_token);} - else if (!strcmp("numentities", qcc_token)) - {file = QCC_COM_Parse(file); numents = atoi(qcc_token);} - else if (qcc_token[0] == '}') //end of block - break; - else - Sys_Error("Bad key \"%s\" in general block", qcc_token); - } - - if (oldglobals) - free(oldglobals); - oldglobals = NULL; - if (pr_progstate[0].globals_size) - { - oldglobals = malloc(pr_progstate[0].globals_size); - if (oldglobals) - { - oldglobalssize = pr_progstate[0].globals_size; - memcpy(oldglobals, pr_progstate[0].globals, oldglobalssize); - } - else - printf("Unable to alloc %i bytes\n", pr_progstate[0].globals_size); - } - - PRAddressableFlush(progfuncs, -1); - resethunk=true; - - pr_progstate = PRHunkAlloc(progfuncs, sizeof(progstate_t) * maxprogs); - pr_typecurrent=0; - - sv_num_edicts = 1; //set up a safty buffer so things won't go horribly wrong too often - sv_edicts=(struct edict_s *)&tempedict; - prinst->edicttable = &sv_edicts; - - - sv_num_edicts = numents; //should be fine - -// PR_Configure(crc, NULL, memsize, maxedicts, maxprogs); - } - else if (!strcmp(qcc_token, "{")) - { - if (isloadgame) - { - if (numents == -1) //globals - { - while (1) - { - file = QCC_COM_Parse(file); - if (qcc_token[0] == '}') - break; - else if (!qcc_token[0] || !file) - Sys_Error("EOF when parsing global values"); - - switch(current_progstate->structtype) - { - case PST_DEFAULT: - case PST_KKQWSV: - if (!(d16 = ED_FindGlobal16(progfuncs, qcc_token))) - { - file = QCC_COM_Parse(file); - printf("global value %s not found", qcc_token); - } - else - { - file = QCC_COM_Parse(file); - ED_ParseEpair(progfuncs, (char*)pr_globals - progfuncs->stringtable, d16->ofs, d16->type, qcc_token); - } - break; - case PST_QTEST: - case PST_FTE32: - if (!(d32 = ED_FindGlobal32(progfuncs, qcc_token))) - { - file = QCC_COM_Parse(file); - printf("global value %s not found", qcc_token); - } - else - { - file = QCC_COM_Parse(file); - ED_ParseEpair(progfuncs, (char*)pr_globals - progfuncs->stringtable, d32->ofs, d32->type, qcc_token); - } - break; - default: - Sys_Error("Bad struct type in LoadEnts"); - } - } - } - else - { - ed = (edictrun_t *)EDICT_NUM(progfuncs, numents); - if (!ed) - ed = ED_AllocIntoTable(progfuncs, numents); - - if (externs->entspawn) - externs->entspawn((struct edict_s *) ed, true); - - sv_num_edicts = numents; - ed->isfree = false; - file = ED_ParseEdict (progfuncs, file, ed); - } - numents++; - continue; - } - - if (entsize == 0 && resethunk) //edicts have not yet been initialized, and this is a compleate load (memsize has been set) - { - entsize = PR_InitEnts(progfuncs, maxedicts); -// sv_num_edicts = numents; - - for (num = 0; num < numents; num++) - { - ed = (edictrun_t *)EDICT_NUM(progfuncs, num); - - if (!ed) - { - ed = ED_AllocIntoTable(progfuncs, num); - ed->isfree = true; - } - } - } - - if (!ed) //first entity - ed = (edictrun_t *)EDICT_NUM(progfuncs, 0); - else - ed = (edictrun_t *)ED_Alloc(progfuncs); - ed->isfree = false; - if (externs->entspawn) - externs->entspawn((struct edict_s *) ed, true); - file = ED_ParseEdict(progfuncs, file, ed); - - if (killonspawnflags) - { - var = GetEdictFieldValue (progfuncs, (struct edict_s *)ed, "spawnflags", &spawnflagscache); - if (var) - { - if ((int)var->_float & (int)killonspawnflags) - { - ed->isfree = true; - ed->freetime = 0; - continue; - } - } - } - - if (!resethunk) - { - char *eclassname; - func_t f; - if (!CheckSpawn) - CheckSpawn = PR_FindFunc(progfuncs, "CheckSpawn", -2); - - var = GetEdictFieldValue (progfuncs, (struct edict_s *)ed, "classname", NULL); - if (!var || !var->string || !*PR_StringToNative(progfuncs, var->string)) - { - printf("No classname\n"); - ED_Free(progfuncs, (struct edict_s *)ed); - } - else - { - //added by request of Mercury. - if (fulldata) //this is a vital part of HL map support!!! - { //essentually, it passes the ent's spawn info to the ent. - char *nl; //otherwise it sees only the named fields of - char *spawndata;//a standard quake ent. - spawndata = PRHunkAlloc(progfuncs, file - datastart +1); - strncpy(spawndata, datastart, file - datastart); - spawndata[file - datastart] = '\0'; - for (nl = spawndata; *nl; nl++) - if (*nl == '\n') - *nl = '\t'; - fulldata->string = PR_StringToProgs(progfuncs, spawndata); - } - - if (!selfvar) - selfvar = PR_FindGlobal(progfuncs, "self", PR_ANY, NULL); - if (selfvar) - selfvar->edict = EDICT_TO_PROG(progfuncs, ed); - - //DP_SV_SPAWNFUNC_PREFIX support - eclassname = PR_StringToNative(progfuncs, var->string); -#ifdef _WIN32 - _snprintf(filename, sizeof(filename), "spawnfunc_%s", eclassname); - filename[sizeof(filename)-1] = 0; -#else - snprintf(filename, sizeof(filename), "spawnfunc_%s", eclassname); -#endif - f = PR_FindFunc(progfuncs, filename, PR_ANYBACK); - if (!f) - f = PR_FindFunc(progfuncs, eclassname, PR_ANYBACK); - if (f) - { - if (CheckSpawn) - { - G_INT(OFS_PARM0) = f; - PR_ExecuteProgram(progfuncs, CheckSpawn); - //call the spawn func or remove. - } - else - PR_ExecuteProgram(progfuncs, f); - } - else if (CheckSpawn) - { - G_INT(OFS_PARM0) = 0; - PR_ExecuteProgram(progfuncs, CheckSpawn); - //the mod is responsible for freeing unrecognised ents. - } - else - { - printf("Couldn't find spawn function %s\n", PR_StringToNative(progfuncs, var->string)); - ED_Free(progfuncs, (struct edict_s *)ed); - } - } - } - } - else - Sys_Error("Command %s not recognised", qcc_token); - } - if (resethunk) - { - header_crc = crc; - if (externs->loadcompleate) - externs->loadcompleate(entsize); - - sv_num_edicts = numents; - } - - if (oldglobals) - free(oldglobals); - oldglobals = NULL; - - if (resethunk) - { - return entsize; - } - else - return max_fields_size; -} - -#define AddS(str) strcpy(s, str);s+=strlen(str); - -char *SaveEnt (progfuncs_t *progfuncs, char *buf, int *size, struct edict_s *ed) -{ - fdef_t *d; - int *v; - unsigned int i;unsigned int j; - char *name; - int type; - - char *s = buf; - -// if (ed->free) -// continue; - - AddS ("{\n"); - - - for (i=0 ; iname; - len = strlen(name); // should we skip vars with no name? - if (len > 2 && name[len-2] == '_') - continue; // skip _x, _y, _z vars - - v = (int*)((edictrun_t*)ed)->fields + d->ofs; - - // if the value is still all 0, skip the field - type = d->type & ~DEF_SAVEGLOBAL; - for (j=0 ; jtype, (eval_t *)v))); - } - - AddS ("}\n"); - - *size = s - buf; - - return buf; -} -struct edict_s *RestoreEnt (progfuncs_t *progfuncs, char *buf, int *size, struct edict_s *ed) -{ - edictrun_t *ent; - char *start = buf; - - buf = QCC_COM_Parse(buf); //read the key - if (!buf || !*qcc_token) - return NULL; - - if (strcmp(qcc_token, "{")) - Sys_Error("Restore Ent with no opening brace"); - - if (!ed) - ent = (edictrun_t *)ED_Alloc(progfuncs); - else - ent = (edictrun_t *)ed; - ent->isfree = false; - - if (externs->entspawn) - externs->entspawn((struct edict_s *) ent, false); - - buf = ED_ParseEdict(progfuncs, buf, ent); - - *size = buf - start; - - return (struct edict_s *)ent; -} - -#define Host_Error Sys_Error - -//return true if pr_progs needs recompiling (source files have changed) -pbool PR_TestRecompile(progfuncs_t *progfuncs) -{ - int newsize; - int num, found=0, lost=0, changed=0; - includeddatafile_t *s; - if (!pr_progs->ofsfiles) - return false; - - num = *(int*)((char *)pr_progs + pr_progs->ofsfiles); - s = (includeddatafile_t *)((char *)pr_progs + pr_progs->ofsfiles+4); - while(num>0) - { - newsize = externs->FileSize(s->filename); - if (newsize == -1) //ignore now missing files. - the referencer must have changed... - lost++; - else if (s->size != newsize) //file - changed++; - else - found++; - - s++; - num--; - } - if (lost > found+changed) - return false; - if (changed) - return true; - return false; -} -/* -#ifdef _DEBUG -//this is for debugging. -//I'm using this to detect incorrect string types while converting 32bit string pointers with bias to bound indexes. -void PR_TestForWierdness(progfuncs_t *progfuncs) -{ - unsigned int i; - int e; - edictrun_t *ed; - for (i = 0; i < pr_progs->numglobaldefs; i++) - { - if ((pr_globaldefs16[i].type&~(DEF_SHARED|DEF_SAVEGLOBAL)) == ev_string) - { - if (G_INT(pr_globaldefs16[i].ofs) < 0 || G_INT(pr_globaldefs16[i].ofs) >= addressableused) - printf("String type irregularity on \"%s\" \"%s\"\n", pr_globaldefs16[i].s_name+progfuncs->stringtable, G_INT(pr_globaldefs16[i].ofs)+progfuncs->stringtable); - } - } - - for (i = 0; i < numfields; i++) - { - if ((field[i].type&~(DEF_SHARED|DEF_SAVEGLOBAL)) == ev_string) - { - for (e = 0; e < sv_num_edicts; e++) - { - ed = (edictrun_t*)EDICT_NUM(progfuncs, e); - if (ed->isfree) - continue; - if (((int *)ed->fields)[field[i].ofs] < 0 || ((int *)ed->fields)[field[i].ofs] >= addressableused) - printf("String type irregularity \"%s\" \"%s\"\n", field[i].name, ((int *)ed->fields)[field[i].ofs]+progfuncs->stringtable); - } - } - } -} -#endif -*/ -char *decode(int complen, int len, int method, char *info, char *buffer); -/* -=============== -PR_LoadProgs -=============== -*/ -int PR_ReallyLoadProgs (progfuncs_t *progfuncs, char *filename, int headercrc, progstate_t *progstate, pbool complain) -{ - unsigned int i, type; -// extensionbuiltin_t *eb; -// float fl; - int len; -// int num; -// dfunction_t *f, *f2; - ddef16_t *d16; - ddef32_t *d32; - int *d2; - eval_t *eval; - char *s; - int progstype; - int trysleft = 2; -// bool qfhack = false; - pbool isfriked = false; //all imediate values were stripped, which causes problems with strings. - pbool hexencalling = false; //hexen style calling convention. The opcodes themselves are used as part of passing the arguments; - ddef16_t *gd16, *fld16; - float *glob; - dfunction_t *fnc; - dstatement16_t *st16; - - int hmark=0xffffffff; - - int reorg = prinst->reorganisefields || numfields; - - int stringadjust; - - current_progstate = progstate; - - strcpy(current_progstate->filename, filename); - - -// flush the non-C variable lookup cache -// for (i=0 ; iautocompile == PR_COMPILEALWAYS) //always compile before loading - { - printf("Forcing compile of progs %s\n", filename); - if (!CompileFile(progfuncs, filename)) - return false; - } - -// CRC_Init (&pr_crc); - -retry: - if ((len=externs->FileSize(filename))<=0) - { - if (externs->autocompile == PR_COMPILENEXIST || externs->autocompile == PR_COMPILECHANGED) //compile if file is not found (if 2, we have already tried, so don't bother) - { - if (hmark==0xffffffff) //first try - { - printf("couldn't open progs %s. Attempting to compile.\n", filename); - CompileFile(progfuncs, filename); - } - if ((len=externs->FileSize(filename))<0) - { - printf("Couldn't find or compile file %s\n", filename); - return false; - } - } - else if (externs->autocompile == PR_COMPILEIGNORE) - return false; - else - { - printf("Couldn't find file %s\n", filename); - return false; - } - } - - hmark = PRHunkMark(progfuncs); - pr_progs = PRHunkAlloc(progfuncs, len+1); - if (!externs->ReadFile(filename, pr_progs, len+1)) - { - if (!complain) - return false; - printf("Failed to open %s", filename); - return false; - } - -// for (i=0 ; iversion == PROG_VERSION) - { -// printf("Opening standard progs file \"%s\"\n", filename); - current_progstate->structtype = PST_DEFAULT; - } - else if (pr_progs->version == PROG_QTESTVERSION) - { - current_progstate->structtype = PST_QTEST; - } - else if (pr_progs->version == PROG_EXTENDEDVERSION) - { -#ifndef NOENDIAN - for (i = standard_dprograms_t_size/sizeof(int); i < sizeof(dprograms_t)/sizeof(int); i++) - ((int *)pr_progs)[i] = PRLittleLong ( ((int *)pr_progs)[i] ); -#endif - if (pr_progs->secondaryversion == PROG_SECONDARYVERSION16) - { -// printf("Opening 16bit fte progs file \"%s\"\n", filename); - current_progstate->structtype = PST_DEFAULT; - } - else if (pr_progs->secondaryversion == PROG_SECONDARYVERSION32) - { -// printf("Opening 32bit fte progs file \"%s\"\n", filename); - current_progstate->structtype = PST_FTE32; - } - else - { -// printf("Opening KK7 progs file \"%s\"\n", filename); - current_progstate->structtype = PST_KKQWSV; //KK progs. Yuck. Disabling saving would be a VERY good idea. - pr_progs->version = PROG_VERSION; //not fte. - } -/* else - { - printf ("Progs extensions are not compatible\nTry recompiling with the FTE compiler\n"); - HunkFree(hmark); - pr_progs=NULL; - return false; - } -*/ } - else - { - printf ("%s has wrong version number (%i should be %i)\n", filename, pr_progs->version, PROG_VERSION); - PRHunkFree(progfuncs, hmark); - pr_progs=NULL; - return false; - } - -//progs contains enough info for use to recompile it. - if (trysleft && (externs->autocompile == PR_COMPILECHANGED || externs->autocompile == PR_COMPILEEXISTANDCHANGED) && pr_progs->version == PROG_EXTENDEDVERSION) - { - if (PR_TestRecompile(progfuncs)) - { - printf("Source file has changed\nRecompiling.\n"); - if (CompileFile(progfuncs, filename)) - { - PRHunkFree(progfuncs, hmark); - pr_progs=NULL; - - trysleft--; - goto retry; - } - } - } - if (!trysleft) //the progs exists, let's just be happy about it. - printf("Progs is out of date and uncompilable\n"); - - if (headercrc != -1 && headercrc != 0) - if (pr_progs->crc != headercrc && pr_progs->crc != 0) //This shouldn't affect us. However, it does adjust expectations and usage of builtins. - { -// printf ("%s system vars have been modified, progdefs.h is out of date\n", filename); - PRHunkFree(progfuncs, hmark); - pr_progs=NULL; - return false; - } - - if (pr_progs->version == PROG_EXTENDEDVERSION && pr_progs->blockscompressed && !QC_decodeMethodSupported(2)) - { - printf ("%s uses compression\n", filename); - PRHunkFree(progfuncs, hmark); - pr_progs=NULL; - return false; - } - - fnc = pr_functions = (dfunction_t *)((qbyte *)pr_progs + pr_progs->ofs_functions); - pr_strings = ((char *)pr_progs + pr_progs->ofs_strings); - current_progstate->globaldefs = *(void**)&gd16 = (void *)((qbyte *)pr_progs + pr_progs->ofs_globaldefs); - current_progstate->fielddefs = *(void**)&fld16 = (void *)((qbyte *)pr_progs + pr_progs->ofs_fielddefs); - current_progstate->statements = (void *)((qbyte *)pr_progs + pr_progs->ofs_statements); - - glob = pr_globals = (void *)((qbyte *)pr_progs + pr_progs->ofs_globals); - current_progstate->globals_size = pr_progs->numglobals*sizeof(*pr_globals); - - pr_linenums=NULL; - pr_types=NULL; - if (pr_progs->version == PROG_EXTENDEDVERSION) - { - if (pr_progs->ofslinenums) - pr_linenums = (int *)((qbyte *)pr_progs + pr_progs->ofslinenums); - if (pr_progs->ofs_types) - pr_types = (typeinfo_t *)((qbyte *)pr_progs + pr_progs->ofs_types); - - //start decompressing stuff... - if (pr_progs->blockscompressed & 1) //statements - { - switch(current_progstate->structtype) - { - case PST_DEFAULT: - len=sizeof(dstatement16_t)*pr_progs->numstatements; - break; - case PST_FTE32: - len=sizeof(dstatement32_t)*pr_progs->numstatements; - break; - default: - Sys_Error("Bad struct type"); - } - s = PRHunkAlloc(progfuncs, len); - QC_decode(progfuncs, PRLittleLong(*(int *)pr_statements16), len, 2, (char *)(((int *)pr_statements16)+1), s); - - current_progstate->statements = (dstatement16_t *)s; - } - if (pr_progs->blockscompressed & 2) //global defs - { - switch(current_progstate->structtype) - { - case PST_DEFAULT: - len=sizeof(ddef16_t)*pr_progs->numglobaldefs; - break; - case PST_FTE32: - len=sizeof(ddef32_t)*pr_progs->numglobaldefs; - break; - default: - Sys_Error("Bad struct type"); - } - s = PRHunkAlloc(progfuncs, len); - QC_decode(progfuncs, PRLittleLong(*(int *)pr_globaldefs16), len, 2, (char *)(((int *)pr_globaldefs16)+1), s); - - gd16 = *(ddef16_t**)¤t_progstate->globaldefs = (ddef16_t *)s; - } - if (pr_progs->blockscompressed & 4) //fields - { - switch(current_progstate->structtype) - { - case PST_DEFAULT: - len=sizeof(ddef16_t)*pr_progs->numglobaldefs; - break; - case PST_FTE32: - len=sizeof(ddef32_t)*pr_progs->numglobaldefs; - break; - default: - Sys_Error("Bad struct type"); - } - s = PRHunkAlloc(progfuncs, len); - QC_decode(progfuncs, PRLittleLong(*(int *)pr_fielddefs16), len, 2, (char *)(((int *)pr_fielddefs16)+1), s); - - *(ddef16_t**)¤t_progstate->fielddefs = (ddef16_t *)s; - } - if (pr_progs->blockscompressed & 8) //functions - { - len=sizeof(dfunction_t)*pr_progs->numfunctions; - s = PRHunkAlloc(progfuncs, len); - QC_decode(progfuncs, PRLittleLong(*(int *)pr_functions), len, 2, (char *)(((int *)pr_functions)+1), s); - - fnc = pr_functions = (dfunction_t *)s; - } - if (pr_progs->blockscompressed & 16) //string table - { - len=sizeof(char)*pr_progs->numstrings; - s = PRHunkAlloc(progfuncs, len); - QC_decode(progfuncs, PRLittleLong(*(int *)pr_strings), len, 2, (char *)(((int *)pr_strings)+1), s); - - pr_strings = (char *)s; - } - if (pr_progs->blockscompressed & 32) //globals - { - len=sizeof(float)*pr_progs->numglobals; - s = PRHunkAlloc(progfuncs, len); - QC_decode(progfuncs, PRLittleLong(*(int *)pr_globals), len, 2, (char *)(((int *)pr_globals)+1), s); - - glob = pr_globals = (float *)s; - } - if (pr_linenums && pr_progs->blockscompressed & 64) //line numbers - { - len=sizeof(int)*pr_progs->numstatements; - s = PRHunkAlloc(progfuncs, len); - QC_decode(progfuncs, PRLittleLong(*(int *)pr_linenums), len, 2, (char *)(((int *)pr_linenums)+1), s); - - pr_linenums = (int *)s; - } - if (pr_types && pr_progs->blockscompressed & 128) //types - { - len=sizeof(typeinfo_t)*pr_progs->numtypes; - s = PRHunkAlloc(progfuncs, len); - QC_decode(progfuncs, PRLittleLong(*(int *)pr_types), len, 2, (char *)(((int *)pr_types)+1), s); - - pr_types = (typeinfo_t *)s; - } - } - - len=sizeof(char)*pr_progs->numstrings; - s = PRAddressableExtend(progfuncs, len); - memcpy(s, pr_strings, len); - pr_strings = (char *)s; - - len=sizeof(float)*pr_progs->numglobals; - s = PRAddressableExtend(progfuncs, len); - memcpy(s, pr_globals, len); - glob = pr_globals = (float *)s; - - if (progfuncs->stringtable) - stringadjust = pr_strings - progfuncs->stringtable; - else - stringadjust = 0; - - if (!pr_linenums) - { - unsigned int lnotype = *(unsigned int*)"LNOF"; - unsigned int version = 1; - int ohm; - unsigned int *file; - char lnoname[128]; - ohm = PRHunkMark(progfuncs); - - strcpy(lnoname, filename); - StripExtension(lnoname); - strcat(lnoname, ".lno"); - if ((len=externs->FileSize(lnoname))>0) - { - file = PRHunkAlloc(progfuncs, len+1); - if (externs->ReadFile(lnoname, file, len+1)) - { - if ( file[0] != lnotype - || file[1] != version - || file[2] != pr_progs->numglobaldefs - || file[3] != pr_progs->numglobals - || file[4] != pr_progs->numfielddefs - || file[5] != pr_progs->numstatements - ) - { - PRHunkFree(progfuncs, ohm); //whoops: old progs or incompatible - } - else - pr_linenums = file + 6; - } - } - } - - pr_functions = fnc; -// pr_strings = ((char *)pr_progs + pr_progs->ofs_strings); - gd16 = *(ddef16_t**)¤t_progstate->globaldefs = (ddef16_t *)((qbyte *)pr_progs + pr_progs->ofs_globaldefs); - fld16 = (ddef16_t *)((qbyte *)pr_progs + pr_progs->ofs_fielddefs); -// pr_statements16 = (dstatement16_t *)((qbyte *)pr_progs + pr_progs->ofs_statements); - pr_globals = glob; - st16 = pr_statements16; -#undef pr_globals -#undef pr_globaldefs16 -#undef pr_functions -#undef pr_statements16 -#undef pr_fielddefs16 - - - current_progstate->edict_size = pr_progs->entityfields * 4 + externs->edictsize; - -// byte swap the lumps - switch(current_progstate->structtype) - { - case PST_QTEST: - // qtest needs a struct remap - for (i=0 ; inumfunctions; i++) - { - int j; - qtest_function_t qtfunc = ((qtest_function_t*)fnc)[i]; - - fnc[i].first_statement = PRLittleLong (qtfunc.first_statement); - fnc[i].parm_start = PRLittleLong (qtfunc.parm_start); - fnc[i].s_name = (string_t)PRLittleLong (qtfunc.s_name); - fnc[i].s_file = (string_t)PRLittleLong (qtfunc.s_file); - fnc[i].numparms = PRLittleLong (qtfunc.numparms); - fnc[i].locals = PRLittleLong (qtfunc.locals); - - for (j=0; jnumfunctions; i++) - { -#ifndef NOENDIAN - fnc[i].first_statement = PRLittleLong (fnc[i].first_statement); - fnc[i].parm_start = PRLittleLong (fnc[i].parm_start); - fnc[i].s_name = (string_t)PRLittleLong ((long)fnc[i].s_name); - fnc[i].s_file = (string_t)PRLittleLong ((long)fnc[i].s_file); - fnc[i].numparms = PRLittleLong (fnc[i].numparms); - fnc[i].locals = PRLittleLong (fnc[i].locals); -#endif - fnc[i].s_name += stringadjust; - fnc[i].s_file += stringadjust; - } - break; - default: - Sys_Error("Bad struct type"); - } - - //actual global values -#ifndef NOENDIAN - for (i=0 ; inumglobals ; i++) - ((int *)glob)[i] = PRLittleLong (((int *)glob)[i]); -#endif - - if (pr_types) - { - for (i=0 ; inumtypes ; i++) - { -#ifndef NOENDIAN - pr_types[i].type = PRLittleLong(current_progstate->types[i].type); - pr_types[i].next = PRLittleLong(current_progstate->types[i].next); - pr_types[i].aux_type = PRLittleLong(current_progstate->types[i].aux_type); - pr_types[i].num_parms = PRLittleLong(current_progstate->types[i].num_parms); - pr_types[i].ofs = PRLittleLong(current_progstate->types[i].ofs); - pr_types[i].size = PRLittleLong(current_progstate->types[i].size); - pr_types[i].name = PRLittleLong(current_progstate->types[i].name); -#endif - pr_types[i].name += stringadjust; - } - } - - if (reorg) - reorg = (headercrc != -1); - - QC_FlushProgsOffsets(progfuncs); - switch(current_progstate->structtype) - { - case PST_KKQWSV: - case PST_DEFAULT: - //byteswap the globals and fix name offsets - for (i=0 ; inumglobaldefs ; i++) - { -#ifndef NOENDIAN - gd16[i].type = PRLittleShort (gd16[i].type); - gd16[i].ofs = PRLittleShort (gd16[i].ofs); - gd16[i].s_name = (string_t)PRLittleLong ((long)gd16[i].s_name); -#endif - gd16[i].s_name += stringadjust; - } - - //byteswap fields and fix name offets. Also register the fields (which will result in some offset adjustments in the globals segment). - for (i=0 ; inumfielddefs ; i++) - { -#ifndef NOENDIAN - fld16[i].type = PRLittleShort (fld16[i].type); - fld16[i].ofs = PRLittleShort (fld16[i].ofs); - fld16[i].s_name = (string_t)PRLittleLong ((long)fld16[i].s_name); -#endif - if (reorg) - { - if (pr_types) - type = pr_types[fld16[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].type; - else - type = fld16[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL); - - if (progfuncs->fieldadjust && !pr_typecurrent) //we need to make sure all fields appear in their original place. - QC_RegisterFieldVar(progfuncs, type, fld16[i].s_name+pr_strings, 4*(fld16[i].ofs+progfuncs->fieldadjust), -1); - else if (type == ev_vector) //emit vector vars early, so their fields cannot be alocated before the vector itself. (useful against scramblers) - { - QC_RegisterFieldVar(progfuncs, type, fld16[i].s_name+pr_strings, -1, fld16[i].ofs); - } - } - else - { - fdef_t *nf; - if (numfields+1>maxfields) - { - i = maxfields; - maxfields += 32; - nf = memalloc(sizeof(fdef_t) * maxfields); - memcpy(nf, field, sizeof(fdef_t) * i); - memfree(field); - field = nf; - } - nf = &field[numfields]; - nf->name = fld16[i].s_name+pr_strings; - nf->type = fld16[i].type; - nf->progsofs = fld16[i].ofs; - nf->ofs = fld16[i].ofs; - - if (fields_size < (nf->ofs+type_size[nf->type])*4) - fields_size = (nf->ofs+type_size[nf->type])*4; - - numfields++; - } - fld16[i].s_name += stringadjust; - } - if (reorg && !(progfuncs->fieldadjust && !pr_typecurrent)) - for (i=0 ; inumfielddefs ; i++) - { - if (pr_types) - type = pr_types[fld16[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].type; - else - type = fld16[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL); - if (type != ev_vector) - QC_RegisterFieldVar(progfuncs, type, fld16[i].s_name+pr_strings-stringadjust, -1, fld16[i].ofs); - } - - break; - case PST_QTEST: - // qtest needs a struct remap - for (i=0 ; inumglobaldefs ; i++) - { - qtest_def_t qtdef = ((qtest_def_t *)pr_globaldefs32)[i]; - - pr_globaldefs32[i].type = qtdef.type; - pr_globaldefs32[i].s_name = qtdef.s_name; - pr_globaldefs32[i].ofs = qtdef.ofs; - } - for (i=0 ; inumfielddefs ; i++) - { - qtest_def_t qtdef = ((qtest_def_t *)pr_fielddefs32)[i]; - - pr_fielddefs32[i].type = qtdef.type; - pr_fielddefs32[i].s_name = qtdef.s_name; - pr_fielddefs32[i].ofs = qtdef.ofs; - } - // passthrough - case PST_FTE32: - for (i=0 ; inumglobaldefs ; i++) - { -#ifndef NOENDIAN - pr_globaldefs32[i].type = PRLittleLong (pr_globaldefs32[i].type); - pr_globaldefs32[i].ofs = PRLittleLong (pr_globaldefs32[i].ofs); - pr_globaldefs32[i].s_name = (string_t)PRLittleLong ((long)pr_globaldefs32[i].s_name); -#endif - pr_globaldefs32[i].s_name += stringadjust; - } - - for (i=0 ; inumfielddefs ; i++) - { - #ifndef NOENDIAN - pr_fielddefs32[i].type = PRLittleLong (pr_fielddefs32[i].type); - pr_fielddefs32[i].ofs = PRLittleLong (pr_fielddefs32[i].ofs); - pr_fielddefs32[i].s_name = (string_t)PRLittleLong ((long)pr_fielddefs32[i].s_name); - #endif - - if (reorg) - { - if (pr_types) - type = pr_types[pr_fielddefs32[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].type; - else - type = pr_fielddefs32[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL); - if (progfuncs->fieldadjust && !pr_typecurrent) //we need to make sure all fields appear in their original place. - QC_RegisterFieldVar(progfuncs, type, pr_fielddefs32[i].s_name+pr_strings, 4*(pr_fielddefs32[i].ofs+progfuncs->fieldadjust), -1); - else if (type == ev_vector) - QC_RegisterFieldVar(progfuncs, type, pr_fielddefs32[i].s_name+pr_strings, -1, pr_fielddefs32[i].ofs); - } - pr_fielddefs32[i].s_name += stringadjust; - } - if (reorg && !(progfuncs->fieldadjust && !pr_typecurrent)) - for (i=0 ; inumfielddefs ; i++) - { - if (pr_types) - type = pr_types[pr_fielddefs32[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].type; - else - type = pr_fielddefs32[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL); - if (type != ev_vector) - QC_RegisterFieldVar(progfuncs, type, pr_fielddefs32[i].s_name+pr_strings-stringadjust, -1, pr_fielddefs32[i].ofs); - } - break; - default: - Sys_Error("Bad struct type"); - } - -//ifstring fixes arn't performed anymore. -//the following switch just fixes endian and hexen2 calling conventions (by using different opcodes). - switch(current_progstate->structtype) - { - case PST_QTEST: - for (i=0 ; inumstatements ; i++) - { - qtest_statement_t qtst = ((qtest_statement_t*)st16)[i]; - - st16[i].op = PRLittleShort(qtst.op); - st16[i].a = PRLittleShort(qtst.a); - st16[i].b = PRLittleShort(qtst.b); - st16[i].c = PRLittleShort(qtst.c); - // could use the line info as lno information maybe? is it really worth it? - // also never assuming h2 calling mechanism - } - break; - case PST_DEFAULT: - for (i=0 ; inumstatements ; i++) - { -#ifndef NOENDIAN - st16[i].op = PRLittleShort(st16[i].op); - st16[i].a = PRLittleShort(st16[i].a); - st16[i].b = PRLittleShort(st16[i].b); - st16[i].c = PRLittleShort(st16[i].c); -#endif - if (st16[i].op >= OP_CALL1 && st16[i].op <= OP_CALL8) - { - if (st16[i].b) - { - hexencalling = true; - break; - } - } - } - if (hexencalling) - { - for (i=0 ; inumstatements ; i++) - { - if (st16[i].op >= OP_CALL1 && st16[i].op <= OP_CALL8) - st16[i].op += OP_CALL1H - OP_CALL1; - if (st16[i].op >= OP_RAND0 && st16[i].op <= OP_RANDV2) - if (!st16[i].c) - st16[i].c = OFS_RETURN; - } - } - break; - - case PST_KKQWSV: //24 sucks. Guess why. - case PST_FTE32: - for (i=0 ; inumstatements ; i++) - { -#ifndef NOENDIAN - pr_statements32[i].op = PRLittleLong(pr_statements32[i].op); - pr_statements32[i].a = PRLittleLong(pr_statements32[i].a); - pr_statements32[i].b = PRLittleLong(pr_statements32[i].b); - pr_statements32[i].c = PRLittleLong(pr_statements32[i].c); -#endif - if (pr_statements32[i].op >= OP_CALL1 && pr_statements32[i].op <= OP_CALL8) - { - if (pr_statements32[i].b) - { - hexencalling = true; - break; - } - } - } - if (hexencalling) - { - for (i=0 ; inumstatements ; i++) - { - if (pr_statements32[i].op >= OP_CALL1 && pr_statements32[i].op <= OP_CALL8) - pr_statements32[i].op += OP_CALL1H - OP_CALL1; - if (pr_statements32[i].op >= OP_RAND0 && pr_statements32[i].op <= OP_RANDV2) - if (!pr_statements32[i].c) - pr_statements32[i].c = OFS_RETURN; - } - } - break; - } - - - if (headercrc == -1) - { - isfriked = true; - if (current_progstate->structtype != PST_DEFAULT) - Sys_Error("Decompiling a bigprogs"); - return true; - } - - progstype = current_progstate-pr_progstate; - -// QC_StartShares(progfuncs); - - isfriked = true; - if (!pr_typecurrent) //progs 0 always acts as string stripped. - isfriked = -1; //partly to avoid some bad progs. - -// len = 0; - switch(current_progstate->structtype) - { - case PST_DEFAULT: - case PST_KKQWSV: - for (i=0 ; inumglobaldefs ; i++) - { - if (pr_types) - type = pr_types[gd16[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].type; - else - type = gd16[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL); - - if (gd16[i].type & DEF_SHARED) - { - gd16[i].type &= ~DEF_SHARED; - if (pr_types) - QC_AddSharedVar(progfuncs, gd16[i].ofs, pr_types[gd16[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].size); - else - QC_AddSharedVar(progfuncs, gd16[i].ofs, type_size[type]); - } - switch(type) - { - case ev_field: - if (reorg) - QC_AddSharedFieldVar(progfuncs, i, pr_strings - stringadjust); - break; - case ev_string: - if (((unsigned int *)glob)[gd16[i].ofs]>=progstate->progs->numstrings) - printf("Insane value\n"); - else if (isfriked != -1) - { - if (pr_strings[((int *)glob)[gd16[i].ofs]]) //quakec uses string tables. 0 must remain null, or 'if (s)' can break. - { - ((int *)glob)[gd16[i].ofs] += stringadjust; - isfriked = false; - } - else - ((int *)glob)[gd16[i].ofs] = 0; - } - break; - case ev_function: - if (((int *)glob)[gd16[i].ofs]) //don't change null funcs - { -// if (fnc[((int *)glob)[gd16[i].ofs]].first_statement>=0) //this is a hack. Make all builtins switch to the main progs first. Allows builtin funcs to cache vars from just the main progs. - ((int *)glob)[gd16[i].ofs] |= progstype << 24; - } - break; - } - } - break; - case PST_QTEST: - case PST_FTE32: - for (i=0 ; inumglobaldefs ; i++) - { - if (pr_types) - type = pr_types[pr_globaldefs32[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].type; - else - type = pr_globaldefs32[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL); - - if (pr_globaldefs32[i].type & DEF_SHARED) - { - pr_globaldefs32[i].type &= ~DEF_SHARED; - if (pr_types) - QC_AddSharedVar(progfuncs, pr_globaldefs32[i].ofs, pr_types[pr_globaldefs32[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].size); - else - QC_AddSharedVar(progfuncs, pr_globaldefs32[i].ofs, type_size[type]); - } - switch(type) - { - case ev_field: - QC_AddSharedFieldVar(progfuncs, i, pr_strings - stringadjust); - break; - case ev_string: - if (pr_strings[((int *)glob)[pr_globaldefs32[i].ofs]]) //quakec uses string tables. 0 must remain null, or 'if (s)' can break. - { - ((int *)glob)[pr_globaldefs32[i].ofs] += stringadjust; - isfriked = false; - } - break; - case ev_function: - if (((int *)glob)[pr_globaldefs32[i].ofs]) //don't change null funcs - ((int *)glob)[pr_globaldefs32[i].ofs] |= progstype << 24; - break; - } - } - - if (pr_progs->version == PROG_EXTENDEDVERSION && pr_progs->numbodylessfuncs) - { - s = &((char *)pr_progs)[pr_progs->ofsbodylessfuncs]; - for (i = 0; i < pr_progs->numbodylessfuncs; i++) - { - d32 = ED_FindGlobal32(progfuncs, s); - d2 = ED_FindGlobalOfsFromProgs(progfuncs, s, 0, ev_function); - if (!d2) - Sys_Error("Runtime-linked function %s was not found in existing progs", s); - if (!d32) - Sys_Error("Couldn't find def for \"%s\"", s); - ((int *)glob)[d32->ofs] = (*(func_t *)&pr_progstate[0].globals[*d2]); - - s+=strlen(s)+1; - } - } - break; - default: - Sys_Error("Bad struct type"); - } - - if ((isfriked && pr_typecurrent)) //friked progs only allow one file. - { - printf("You are trying to load a string-stripped progs as an addon.\nThis behaviour is not supported. Try removing some optimizations."); - PRHunkFree(progfuncs, hmark); - pr_progs=NULL; - return false; - } - - pr_strings+=stringadjust; - if (!progfuncs->stringtable) - progfuncs->stringtable = pr_strings; - - if (progfuncs->stringtablesize + progfuncs->stringtable < pr_strings + pr_progs->numstrings) - progfuncs->stringtablesize = (pr_strings + pr_progs->numstrings) - progfuncs->stringtable; - - eval = PR_FindGlobal(progfuncs, "thisprogs", progstype, NULL); - if (eval) - eval->prog = progstype; - - switch(current_progstate->structtype) - { - case PST_DEFAULT: - if (pr_progs->version == PROG_EXTENDEDVERSION && pr_progs->numbodylessfuncs) - { - s = &((char *)pr_progs)[pr_progs->ofsbodylessfuncs]; - for (i = 0; i < pr_progs->numbodylessfuncs; i++) - { - d16 = ED_FindGlobal16(progfuncs, s); - if (!d16) - { - printf("Progs requires \"%s\" the external function \"%s\", but the definition was stripped\n", filename, s); - PRHunkFree(progfuncs, hmark); - pr_progs=NULL; - return false; - } - - ((int *)glob)[d16->ofs] = PR_FindFunc(progfuncs, s, PR_ANY); - if (!((int *)glob)[d16->ofs]) - printf("Warning: Runtime-linked function %s could not be found (loading %s)\n", s, filename); - /* - d2 = ED_FindGlobalOfsFromProgs(progfuncs, s, 0, ev_function); - if (!d2) - Sys_Error("Runtime-linked function %s was not found in primary progs (loading %s)", s, filename); - ((int *)glob)[d16->ofs] = (*(func_t *)&pr_progstate[0].globals[*d2]); - */ - s+=strlen(s)+1; - } - } - break; - case PST_QTEST: - case PST_KKQWSV: - break; //cannot happen anyway. - case PST_FTE32: - if (pr_progs->version == PROG_EXTENDEDVERSION && pr_progs->numbodylessfuncs) - { - s = &((char *)pr_progs)[pr_progs->ofsbodylessfuncs]; - for (i = 0; i < pr_progs->numbodylessfuncs; i++) - { - d32 = ED_FindGlobal32(progfuncs, s); - d2 = ED_FindGlobalOfsFromProgs(progfuncs, s, 0, ev_function); - if (!d2) - printf("Warning: Runtime-linked function %s was not found in existing progs", s); - if (!d32) - { - printf("Couldn't find def for \"%s\"", s); - PRHunkFree(progfuncs, hmark); - pr_progs=NULL; - return false; - } - ((int *)glob)[d32->ofs] = (*(func_t *)&pr_progstate[0].globals[*d2]); - - s+=strlen(s)+1; - } - } - break; - } - - eval = PR_FindGlobal(progfuncs, "__ext__fasttrackarrays", PR_CURRENT, NULL); - if (eval) //we support these opcodes - eval->_float = true; - - return true; -} - - - -struct edict_s *EDICT_NUM(progfuncs_t *progfuncs, unsigned int n) -{ - if (n >= maxedicts) - Sys_Error ("QCLIB: EDICT_NUM: bad number %i", n); - - return prinst->edicttable[n]; -} - -unsigned int NUM_FOR_EDICT(progfuncs_t *progfuncs, struct edict_s *e) -{ - edictrun_t *er = (edictrun_t*)e; - if (er->entnum >= maxedicts) - Sys_Error ("QCLIB: NUM_FOR_EDICT: bad pointer (%p)", e); - return er->entnum; -} diff --git a/misc/source/fteqcc-src/pr_exec.c b/misc/source/fteqcc-src/pr_exec.c deleted file mode 100644 index 323c5660..00000000 --- a/misc/source/fteqcc-src/pr_exec.c +++ /dev/null @@ -1,1233 +0,0 @@ -#define PROGSUSED -#include "progsint.h" -//#include "editor.h" - -#define HunkAlloc BADGDFG sdfhhsf FHS - - -#define Host_Error Sys_Error - -// I put the following here to resolve "undefined reference to `__imp__vsnprintf'" with MinGW64 ~ Moodles -#ifdef _WIN32 - #if (_MSC_VER >= 1400) - //with MSVC 8, use MS extensions - #define snprintf linuxlike_snprintf_vc8 - int VARGS linuxlike_snprintf_vc8(char *buffer, int size, const char *format, ...) LIKEPRINTF(3); - #define vsnprintf(a, b, c, d) vsnprintf_s(a, b, _TRUNCATE, c, d) - #else - //msvc crap - #define snprintf linuxlike_snprintf - int VARGS linuxlike_snprintf(char *buffer, int size, const char *format, ...) LIKEPRINTF(3); - #define vsnprintf linuxlike_vsnprintf - int VARGS linuxlike_vsnprintf(char *buffer, int size, const char *format, va_list argptr); - #endif -#endif - - -//============================================================================= - -/* -================= -PR_PrintStatement -================= -*/ -/* -void PR_PrintStatement (progfuncs_t *progfuncs, dstatement16_t *s) -{ - int i; -printf("PR_PrintStatement is unsupported\n"); -return; - if ( (unsigned)s->op < OP_NUMOPS) - { - printf ("%s ", pr_opcodes[s->op].name); - i = strlen(pr_opcodes[s->op].name); - for ( ; i<10 ; i++) - printf (" "); - } - - if (s->op == OP_IF || s->op == OP_IFNOT) - printf ("%sbranch %i",PR_GlobalString(progfuncs, s->a),s->b); - else if (s->op == OP_GOTO) - { - printf ("branch %i",s->a); - } - else if ( (unsigned)(s->op - OP_STORE_F) < 6) - { - printf ("%s",PR_GlobalString(progfuncs, s->a)); - printf ("%s", PR_GlobalStringNoContents(progfuncs, s->b)); - } - else - { - if (s->a) - printf ("%s",PR_GlobalString(progfuncs, s->a)); - if (s->b) - printf ("%s",PR_GlobalString(progfuncs, s->b)); - if (s->c) - printf ("%s", PR_GlobalStringNoContents(progfuncs, s->c)); - } - printf ("\n"); -} -*/ - -/* -============ -PR_StackTrace -============ -*/ -char *QC_ucase(char *str) -{ - static char s[1024]; - strcpy(s, str); - str = s; - - while(*str) - { - if (*str >= 'a' && *str <= 'z') - *str = *str - 'a' + 'A'; - str++; - } - return s; -} - -void PR_StackTrace (progfuncs_t *progfuncs) -{ - dfunction_t *f; - int i; - int progs; - -#ifdef STACKTRACE - int arg; - int *globalbase; -#endif - progs = -1; - - if (pr_depth == 0) - { - printf ("\n"); - return; - } - -#ifdef STACKTRACE - globalbase = (int *)pr_globals + pr_xfunction->parm_start - pr_xfunction->locals; -#endif - - pr_stack[pr_depth].f = pr_xfunction; - pr_stack[pr_depth].s = pr_xstatement; - for (i=pr_depth ; i>0 ; i--) - { - f = pr_stack[i].f; - - if (!f) - { - printf ("\n"); - } - else - { - if (pr_stack[i].progsnum != progs) - { - progs = pr_stack[i].progsnum; - - printf ("<%s>\n", pr_progstate[progs].filename); - } - if (!f->s_file) - printf ("stripped : %s\n", f->s_name+progfuncs->stringtable); - else - { - if (pr_progstate[progs].linenums) - printf ("%12s %i : %s\n", f->s_file+progfuncs->stringtable, pr_progstate[progs].linenums[pr_stack[i].s], f->s_name+progfuncs->stringtable); - else - printf ("%12s : %s\n", f->s_file+progfuncs->stringtable, f->s_name+progfuncs->stringtable); - } - -#ifdef STACKTRACE - - for (arg = 0; arg < f->locals; arg++) - { - ddef16_t *local; - local = ED_GlobalAtOfs16(progfuncs, f->parm_start+arg); - if (!local) - { - printf(" ofs %i: %f : %i\n", f->parm_start+arg, *(float *)(globalbase - f->locals+arg), *(int *)(globalbase - f->locals+arg) ); - } - else - { - printf(" %s: %s\n", local->s_name+progfuncs->stringtable, PR_ValueString(progfuncs, local->type, (eval_t*)(globalbase - f->locals+arg))); - if (local->type == ev_vector) - arg+=2; - } - } - - if (i == pr_depth) - globalbase = localstack + localstack_used; - else - globalbase -= f->locals; -#endif - } - } -} - -/* -============ -PR_Profile_f - -============ -*/ -/* -void PR_Profile_f (void) -{ - dfunction_t *f, *best; - int max; - int num; - unsigned int i; - - num = 0; - do - { - max = 0; - best = NULL; - for (i=0 ; inumfunctions ; i++) - { - f = &pr_functions[i]; - if (f->profile > max && f->first_statement >=0) - { - max = f->profile; - best = f; - } - } - if (best) - { - if (num < 10) - printf ("%7i %s\n", best->profile, best->s_name); - num++; - best->profile = 0; - } - } while (best); -} -*/ - - -/* -============ -PR_RunError - -Aborts the currently executing function -============ -*/ -void VARGS PR_RunError (progfuncs_t *progfuncs, char *error, ...) -{ - va_list argptr; - char string[1024]; - - va_start (argptr,error); - Q_vsnprintf (string,sizeof(string)-1, error,argptr); - va_end (argptr); - -// { -// void SV_EndRedirect (void); -// SV_EndRedirect(); -// } - -// PR_PrintStatement (pr_statements + pr_xstatement); - PR_StackTrace (progfuncs); - printf ("\n"); - -//editbadfile(pr_strings + pr_xfunction->s_file, -1); - -// pr_depth = 0; // dump the stack so host_error can shutdown functions -// prinst->exitdepth = 0; - - Abort ("%s", string); -} - -/* -============================================================================ -PR_ExecuteProgram - -The interpretation main loop -============================================================================ -*/ - -/* -==================== -PR_EnterFunction - -Returns the new program statement counter -==================== -*/ -void PR_AbortStack (progfuncs_t *progfuncs); -int ASMCALL PR_EnterFunction (progfuncs_t *progfuncs, dfunction_t *f, int progsnum) -{ - int i, j, c, o; - - pr_stack[pr_depth].s = pr_xstatement; - pr_stack[pr_depth].f = pr_xfunction; - pr_stack[pr_depth].progsnum = progsnum; - pr_stack[pr_depth].pushed = pr_spushed; - pr_depth++; - if (pr_depth == MAX_STACK_DEPTH) - { - pr_depth--; - PR_StackTrace (progfuncs); - - printf ("stack overflow on call to %s\n", progfuncs->stringtable+f->s_name); - - //comment this out if you want the progs to try to continue anyway (could cause infinate loops) - PR_AbortStack(progfuncs); - Abort("Stack Overflow in %s\n", progfuncs->stringtable+f->s_name); - return pr_xstatement; - } - - localstack_used += pr_spushed; //make sure the call doesn't hurt pushed pointers - -// save off any locals that the new function steps on (to a side place, fromwhere they are restored on exit) - c = f->locals; - if (localstack_used + c > LOCALSTACK_SIZE) - { - localstack_used -= pr_spushed; - pr_depth--; - PR_RunError (progfuncs, "PR_ExecuteProgram: locals stack overflow\n"); - } - - for (i=0 ; i < c ; i++) - localstack[localstack_used+i] = ((int *)pr_globals)[f->parm_start + i]; - localstack_used += c; - -// copy parameters (set initial values) - o = f->parm_start; - for (i=0 ; inumparms ; i++) - { - for (j=0 ; jparm_size[i] ; j++) - { - ((int *)pr_globals)[o] = ((int *)pr_globals)[OFS_PARM0+i*3+j]; - o++; - } - } - - pr_xfunction = f; - return f->first_statement - 1; // offset the s++ -} - -/* -==================== -PR_LeaveFunction -==================== -*/ -int ASMCALL PR_LeaveFunction (progfuncs_t *progfuncs) -{ - int i, c; - - if (pr_depth <= 0) - Sys_Error ("prog stack underflow"); - -// restore locals from the stack - c = pr_xfunction->locals; - localstack_used -= c; - if (localstack_used < 0) - PR_RunError (progfuncs, "PR_ExecuteProgram: locals stack underflow\n"); - - for (i=0 ; i < c ; i++) - ((int *)pr_globals)[pr_xfunction->parm_start + i] = localstack[localstack_used+i]; - -// up stack - pr_depth--; - PR_MoveParms(progfuncs, pr_stack[pr_depth].progsnum, pr_typecurrent); - PR_SwitchProgs(progfuncs, pr_stack[pr_depth].progsnum); - pr_xfunction = pr_stack[pr_depth].f; - pr_spushed = pr_stack[pr_depth].pushed; - - localstack_used -= pr_spushed; - return pr_stack[pr_depth].s; -} - -ddef32_t *ED_FindLocalOrGlobal(progfuncs_t *progfuncs, char *name, eval_t **val) -{ - static ddef32_t def; - ddef32_t *def32; - ddef16_t *def16; - int i; - - if (pr_typecurrent < 0) - return NULL; - - switch (pr_progstate[pr_typecurrent].structtype) - { - case PST_DEFAULT: - case PST_KKQWSV: - //this gets parms fine, but not locals - if (pr_xfunction) - for (i = 0; i < pr_xfunction->numparms; i++) - { - def16 = ED_GlobalAtOfs16(progfuncs, pr_xfunction->parm_start+i); - if (!def16) - continue; - if (!strcmp(def16->s_name+progfuncs->stringtable, name)) - { - *val = (eval_t *)&pr_progstate[pr_typecurrent].globals[pr_xfunction->parm_start+i]; - - //we need something like this for functions that are not the top layer - // *val = (eval_t *)&localstack[localstack_used-pr_xfunction->numparms*4]; - def.ofs = def16->ofs; - def.s_name = def16->s_name; - def.type = def16->type; - return &def; - } - } - def16 = ED_FindGlobal16(progfuncs, name); - if (!def16) - return NULL; - def.ofs = def16->ofs; - def.type = def16->type; - def.s_name = def16->s_name; - def32 = &def; - break; - case PST_QTEST: - case PST_FTE32: - //this gets parms fine, but not locals - if (pr_xfunction) - for (i = 0; i < pr_xfunction->numparms; i++) - { - def32 = ED_GlobalAtOfs32(progfuncs, pr_xfunction->parm_start+i); - if (!def32) - continue; - if (!strcmp(def32->s_name+progfuncs->stringtable, name)) - { - *val = (eval_t *)&pr_progstate[pr_typecurrent].globals[pr_xfunction->parm_start+i]; - - //we need something like this for functions that are not the top layer - // *val = (eval_t *)&localstack[localstack_used-pr_xfunction->numparms*4]; - return def32; - } - } - def32 = ED_FindGlobal32(progfuncs, name); - if (!def32) - return NULL; - break; - default: - Sys_Error("Bad struct type in ED_FindLocalOrGlobal"); - def32 = NULL; - } - - *val = (eval_t *)&pr_progstate[pr_typecurrent].globals[def32->ofs]; - return &def; -} - -char *COM_TrimString(char *str) -{ - int i; - static char buffer[256]; - while (*str <= ' ' && *str>'\0') - str++; - - for (i = 0; i < 255; i++) - { - if (*str <= ' ') - break; - buffer[i] = *str++; - } - buffer[i] = '\0'; - return buffer; -} - -char *EvaluateDebugString(progfuncs_t *progfuncs, char *key) -{ - static char buf[256]; - char *c, *c2; - ddef32_t *def; - fdef_t *fdef; - eval_t *val; - char *assignment; - int type; - ddef32_t fakedef; - eval_t fakeval; - edictrun_t *ed; - - assignment = strchr(key, '='); - if (assignment) - *assignment = '\0'; - - c = strchr(key, '.'); - if (c) *c = '\0'; - def = ED_FindLocalOrGlobal(progfuncs, key, &val); - if (!def) - { - if (atoi(key)) - { - def = &fakedef; - def->ofs = 0; - def->type = ev_entity; - val = &fakeval; - val->edict = atoi(key); - } - } - if (c) *c = '.'; - if (!def) - { - return "(Bad string)"; - } - type = def->type; - - //go through ent vars - c = strchr(key, '.'); - while(c) - { - c2 = c+1; - c = strchr(c2, '.'); - type = type &~DEF_SAVEGLOBAL; - if (current_progstate && current_progstate->types) - type = current_progstate->types[type].type; - if (type != ev_entity) - return "'.' without entity"; - if (c)*c = '\0'; - fdef = ED_FindField(progfuncs, COM_TrimString(c2)); - if (c)*c = '.'; - if (!fdef) - return "(Bad string)"; - ed = PROG_TO_EDICT(progfuncs, val->_int); - if (!ed) - return "(Invalid Entity)"; - val = (eval_t *) (((char *)ed->fields) + fdef->ofs*4); - type = fdef->type; - } - - if (assignment) - { - assignment++; - while(*assignment == ' ') - assignment++; - switch (type&~DEF_SAVEGLOBAL) - { - case ev_string: - *(string_t *)val = PR_StringToProgs(progfuncs, ED_NewString (progfuncs, assignment, 0)); - break; - - case ev_float: - if (assignment[0] == '0' && (assignment[1] == 'x' || assignment[1] == 'X')) - *(float*)val = strtoul(assignment, NULL, 0); - else - *(float *)val = (float)atof (assignment); - break; - - case ev_integer: - *(int *)val = atoi (assignment); - break; - -/* case ev_vector: - strcpy (string, assignment); - v = string; - w = string; - for (i=0 ; i<3 ; i++) - { - while (*v && *v != ' ') - v++; - *v = 0; - ((float *)d)[i] = (float)atof (w); - w = v = v+1; - } - break; -*/ - case ev_entity: - *(int *)val = EDICT_TO_PROG(progfuncs, EDICT_NUM(progfuncs, atoi (assignment))); - break; - - case ev_field: - fdef = ED_FindField (progfuncs, assignment); - if (!fdef) - { - size_t l,nl = strlen(assignment); - strcpy(buf, "Can't find field "); - l = strlen(buf); - if (nl > sizeof(buf)-l-2) - nl = sizeof(buf)-l-2; - memcpy(buf+l, assignment, nl); - assignment[l+nl+0] = '\n'; - assignment[l+nl+1] = 0; - return buf; - } - *(int *)val = G_INT(fdef->ofs); - break; - - case ev_function: - { - dfunction_t *func; - int i; - int progsnum = -1; - char *s = assignment; - if (s[0] && s[1] == ':') - { - progsnum = atoi(s); - s+=2; - } - else if (s[0] && s[1] && s[2] == ':') - { - progsnum = atoi(s); - s+=3; - } - - func = ED_FindFunction (progfuncs, s, &i, progsnum); - if (!func) - { - size_t l,nl = strlen(s); - - assignment[-1] = '='; - - strcpy(buf, "Can't find field "); - l = strlen(buf); - if (nl > sizeof(buf)-l-2) - nl = sizeof(buf)-l-2; - memcpy(buf+l, assignment, nl); - assignment[l+nl+0] = '\n'; - assignment[l+nl+1] = 0; - return buf; - } - *(func_t *)val = (func - pr_progstate[i].functions) | (i<<24); - } - break; - - default: - break; - - } - assignment[-1] = '='; - } - strcpy(buf, PR_ValueString(progfuncs, type, val)); - - return buf; -} - -int debugstatement; -//int EditorHighlightLine(window_t *wnd, int line); -void SetExecutionToLine(progfuncs_t *progfuncs, int linenum) -{ - int pn = pr_typecurrent; - int snum; - dfunction_t *f = pr_xfunction; - - switch(current_progstate->structtype) - { - case PST_DEFAULT: - case PST_QTEST: - for (snum = f->first_statement; pr_progstate[pn].linenums[snum] < linenum; snum++) - { - if (pr_statements16[snum].op == OP_DONE) - return; - } - break; - case PST_KKQWSV: - case PST_FTE32: - for (snum = f->first_statement; pr_progstate[pn].linenums[snum] < linenum; snum++) - { - if (pr_statements32[snum].op == OP_DONE) - return; - } - break; - default: - Sys_Error("Bad struct type"); - snum = 0; - } - debugstatement = snum; -// EditorHighlightLine(editwnd, pr_progstate[pn].linenums[snum]); -} - -//0 clear. 1 set, 2 toggle, 3 check -int PR_ToggleBreakpoint(progfuncs_t *progfuncs, char *filename, int linenum, int flag) //write alternate route to work by function name. -{ - int ret=0; - unsigned int fl; - unsigned int i; - int pn = pr_typecurrent; - dfunction_t *f; - int op = 0; //warning about not being initialized before use - - for (pn = 0; (unsigned)pn < maxprogs; pn++) - { - if (!pr_progstate || !pr_progstate[pn].progs) - continue; - - if (linenum) //linenum is set means to set the breakpoint on a file and line - { - if (!pr_progstate[pn].linenums) - continue; - - for (f = pr_progstate[pn].functions, fl = 0; fl < pr_progstate[pn].progs->numfunctions; f++, fl++) - { - if (!stricmp(f->s_file+progfuncs->stringtable, filename)) - { - for (i = f->first_statement; ; i++) - { - if (pr_progstate[pn].linenums[i] >= linenum) - { - fl = pr_progstate[pn].linenums[i]; - for (; ; i++) - { - if ((unsigned int)pr_progstate[pn].linenums[i] > fl) - break; - - switch(pr_progstate[pn].structtype) - { - case PST_DEFAULT: - case PST_QTEST: - op = ((dstatement16_t*)pr_progstate[pn].statements + i)->op; - break; - case PST_KKQWSV: - case PST_FTE32: - op = ((dstatement32_t*)pr_progstate[pn].statements + i)->op; - break; - default: - Sys_Error("Bad structtype"); - op = 0; - } - switch (flag) - { - default: - if (op & 0x8000) - { - op &= ~0x8000; - ret = false; - flag = 0; - } - else - { - op |= 0x8000; - ret = true; - flag = 1; - } - break; - case 0: - op &= ~0x8000; - ret = false; - break; - case 1: - op |= 0x8000; - ret = true; - break; - case 3: - if (op & 0x8000) - return true; - } - switch(pr_progstate[pn].structtype) - { - case PST_DEFAULT: - case PST_QTEST: - ((dstatement16_t*)pr_progstate[pn].statements + i)->op = op; - break; - case PST_KKQWSV: - case PST_FTE32: - ((dstatement32_t*)pr_progstate[pn].statements + i)->op = op; - break; - default: - Sys_Error("Bad structtype"); - op = 0; - } - } - goto cont; - } - } - } - } - } - else //set the breakpoint on the first statement of the function specified. - { - for (f = pr_progstate[pn].functions, fl = 0; fl < pr_progstate[pn].progs->numfunctions; f++, fl++) - { - if (!strcmp(f->s_name+progfuncs->stringtable, filename)) - { - i = f->first_statement; - switch(pr_progstate[pn].structtype) - { - case PST_DEFAULT: - case PST_QTEST: - op = ((dstatement16_t*)pr_progstate[pn].statements + i)->op; - break; - case PST_KKQWSV: - case PST_FTE32: - op = ((dstatement32_t*)pr_progstate[pn].statements + i)->op; - break; - default: - Sys_Error("Bad structtype"); - } - switch (flag) - { - default: - if (op & 0x8000) - { - op &= ~0x8000; - ret = false; - flag = 0; - } - else - { - op |= 0x8000; - ret = true; - flag = 1; - } - break; - case 0: - op &= ~0x8000; - ret = false; - break; - case 1: - op |= 0x8000; - ret = true; - break; - case 3: - if (op & 0x8000) - return true; - } - switch(pr_progstate[pn].structtype) - { - case PST_DEFAULT: - case PST_QTEST: - ((dstatement16_t*)pr_progstate[pn].statements + i)->op = op; - break; - case PST_KKQWSV: - case PST_FTE32: - ((dstatement32_t*)pr_progstate[pn].statements + i)->op = op; - break; - default: - Sys_Error("Bad structtype"); - } - break; - } - } - } -cont: - continue; - } - - return ret; -} - -int ShowStep(progfuncs_t *progfuncs, int statement) -{ -// return statement; -// texture realcursortex; -static int lastline = 0; -static char *lastfile = 0; - - int pn = pr_typecurrent; - int i; - dfunction_t *f = pr_xfunction; - - if (f && pr_progstate[pn].linenums && externs->useeditor) - { - if (lastline == pr_progstate[pn].linenums[statement] && lastfile == f->s_file+progfuncs->stringtable) - return statement; //no info/same line as last time - - lastline = pr_progstate[pn].linenums[statement]; - lastfile = f->s_file+progfuncs->stringtable; - - lastline = externs->useeditor(progfuncs, lastfile, lastline, 0, NULL); - - if (pr_progstate[pn].linenums[statement] != lastline) - { - for (i = f->first_statement; ; i++) - { - if (lastline == pr_progstate[pn].linenums[i]) - { - return i; - } - else if (lastline <= pr_progstate[pn].linenums[i]) - { - return statement; - } - } - } - } - else if (f) //annoying. - { - if (*(f->s_file+progfuncs->stringtable)) //if we can't get the filename, then it was stripped, and debugging it like this is useless - if (externs->useeditor) - externs->useeditor(progfuncs, f->s_file+progfuncs->stringtable, -1, 0, NULL); - return statement; - } - - - return statement; -} - -//DMW: all pointer functions are modified to be absoloute pointers from NULL not sv_edicts -/* -==================== -PR_ExecuteProgram -==================== -*/ -void PR_ExecuteCode (progfuncs_t *progfuncs, int s) -{ - eval_t *t, *swtch=NULL; - - int swtchtype = 0; //warning about not being initialized before use - dstatement16_t *st16; - dstatement32_t *st32; - dfunction_t *newf; - int runaway; - int i; - edictrun_t *ed; - eval_t *ptr; - - float *glob; - - int fnum; - - prinst->continuestatement = -1; -#ifdef QCJIT - if (current_progstate->jit) - { - PR_EnterJIT(progfuncs, current_progstate->jit, s); - return; - } -#endif - fnum = pr_xfunction - pr_functions; - - runaway = 100000000; - -#define PRBOUNDSCHECK -#define RUNAWAYCHECK() \ - if (!--runaway) \ - { \ - pr_xstatement = st-pr_statements; \ - PR_StackTrace(progfuncs); \ - printf ("runaway loop error\n"); \ - while(pr_depth > prinst->exitdepth) \ - PR_LeaveFunction(progfuncs); \ - pr_spushed = 0; \ - return; \ - } - -#define OPA ((eval_t *)&glob[st->a]) -#define OPB ((eval_t *)&glob[st->b]) -#define OPC ((eval_t *)&glob[st->c]) - -restart: //jumped to when the progs might have changed. - glob = pr_globals; - switch (current_progstate->structtype) - { - case PST_DEFAULT: - case PST_QTEST: -#define INTSIZE 16 - st16 = &pr_statements16[s]; - while (pr_trace) - { - #define DEBUGABLE - #ifdef SEPARATEINCLUDES - #include "execloop16d.h" - #else - #include "execloop.h" - #endif - #undef DEBUGABLE - } - - while(1) - { - #include "execloop.h" - } -#undef INTSIZE - Sys_Error("PR_ExecuteProgram - should be unreachable"); - break; - case PST_KKQWSV: - case PST_FTE32: -#define INTSIZE 32 - st32 = &pr_statements32[s]; - while (pr_trace) - { - #define DEBUGABLE - #ifdef SEPARATEINCLUDES - #include "execloop32d.h" - #else - #include "execloop.h" - #endif - #undef DEBUGABLE - } - - while(1) - { - #ifdef SEPARATEINCLUDES - #include "execloop32.h" - #else - #include "execloop.h" - #endif - } -#undef INTSIZE - Sys_Error("PR_ExecuteProgram - should be unreachable"); - break; - default: - Sys_Error("PR_ExecuteProgram - bad structtype"); - } -} - - -void PR_ExecuteProgram (progfuncs_t *progfuncs, func_t fnum) -{ - dfunction_t *f; - int i; - unsigned int initial_progs; - int oldexitdepth; - - int s; - - int tempdepth; - - unsigned int newprogs = (fnum & 0xff000000)>>24; - - initial_progs = pr_typecurrent; - if (newprogs != initial_progs) - { - if (newprogs >= maxprogs || !&pr_progstate[newprogs].globals) //can happen with hexen2... - { - printf("PR_ExecuteProgram: tried branching into invalid progs\n"); - return; - } - PR_MoveParms(progfuncs, newprogs, pr_typecurrent); - PR_SwitchProgs(progfuncs, newprogs); - } - - if (!(fnum & ~0xff000000) || (signed)(fnum & ~0xff000000) >= pr_progs->numfunctions) - { -// if (pr_global_struct->self) -// ED_Print (PROG_TO_EDICT(pr_global_struct->self)); - printf("PR_ExecuteProgram: NULL function from exe\n"); -// Host_Error ("PR_ExecuteProgram: NULL function from exe"); - -// PR_MoveParms(0, pr_typecurrent); - PR_SwitchProgs(progfuncs, initial_progs); - return; - } - - oldexitdepth = prinst->exitdepth; - - f = &pr_functions[fnum & ~0xff000000]; - - if (f->first_statement < 0) - { // negative statements are built in functions - i = -f->first_statement; - - if (i < externs->numglobalbuiltins) - (*externs->globalbuiltins[i]) (progfuncs, (struct globalvars_s *)current_progstate->globals); - else - { - i -= externs->numglobalbuiltins; - if (i > current_progstate->numbuiltins) - { - printf ("Bad builtin call number %i (from exe)\n", -f->first_statement); - // PR_MoveParms(p, pr_typecurrent); - PR_SwitchProgs(progfuncs, initial_progs); - return; - } - current_progstate->builtins [i] (progfuncs, (struct globalvars_s *)current_progstate->globals); - } - PR_MoveParms(progfuncs, initial_progs, pr_typecurrent); - PR_SwitchProgs(progfuncs, initial_progs); - return; - } - - if (pr_trace) - pr_trace--; - -// make a stack frame - prinst->exitdepth = pr_depth; - - - s = PR_EnterFunction (progfuncs, f, initial_progs); - - tempdepth = prinst->numtempstringsstack; - PR_ExecuteCode(progfuncs, s); - - - PR_MoveParms(progfuncs, initial_progs, pr_typecurrent); - PR_SwitchProgs(progfuncs, initial_progs); - - PR_FreeTemps(progfuncs, tempdepth); - prinst->numtempstringsstack = tempdepth; - - prinst->exitdepth = oldexitdepth; -} - - - - - - -typedef struct { - int fnum; - int progsnum; - int statement; -} qcthreadstack_t; -typedef struct qcthread_s { - int fstackdepth; - qcthreadstack_t fstack[MAX_STACK_DEPTH]; - int lstackused; - int lstack[LOCALSTACK_SIZE]; - int xstatement; - int xfunction; - progsnum_t xprogs; -} qcthread_t; - -struct qcthread_s *PR_ForkStack(progfuncs_t *progfuncs) -{ //QC code can call builtins that call qc code. - //to get around the problems of restoring the builtins we simply don't save the thread over the builtin. - int i, l; - int ed = prinst->exitdepth; - int localsoffset, baselocalsoffset; - qcthread_t *thread = memalloc(sizeof(qcthread_t)); - dfunction_t *f; - - //copy out the functions stack. - for (i = 0,localsoffset=0; i < ed; i++) - { - if (i+1 == pr_depth) - f = pr_xfunction; - else - f = pr_stack[i+1].f; - localsoffset += f->locals; //this is where it crashes - } - baselocalsoffset = localsoffset; - for (i = ed; i < pr_depth; i++) - { - thread->fstack[i-ed].fnum = pr_stack[i].f - pr_progstate[pr_stack[i].progsnum].functions; - thread->fstack[i-ed].progsnum = pr_stack[i].progsnum; - thread->fstack[i-ed].statement = pr_stack[i].s; - - if (i+1 == pr_depth) - f = pr_xfunction; - else - f = pr_stack[i+1].f; - localsoffset += f->locals; - } - thread->fstackdepth = pr_depth - ed; - - for (i = pr_depth - 1; i >= ed ; i--) - { - if (i+1 == pr_depth) - f = pr_xfunction; - else - f = pr_stack[i+1].f; - localsoffset -= f->locals; - for (l = 0; l < f->locals; l++) - { - thread->lstack[localsoffset-baselocalsoffset + l ] = ((int *)pr_globals)[f->parm_start + l]; - ((int *)pr_globals)[f->parm_start + l] = localstack[localsoffset+l]; //copy the old value into the globals (so the older functions have the correct locals. - } - } - - for (i = ed; i < pr_depth ; i++) //we need to get the locals back to how they were. - { - if (i+1 == pr_depth) - f = pr_xfunction; - else - f = pr_stack[i+1].f; - - for (l = 0; l < f->locals; l++) - { - ((int *)pr_globals)[f->parm_start + l] = thread->lstack[localsoffset-baselocalsoffset + l]; - } - localsoffset += f->locals; - } - thread->lstackused = localsoffset - baselocalsoffset; - - thread->xstatement = pr_xstatement; - thread->xfunction = pr_xfunction - pr_progstate[pr_typecurrent].functions; - thread->xprogs = pr_typecurrent; - - return thread; -} - -void PR_ResumeThread (progfuncs_t *progfuncs, struct qcthread_s *thread) -{ - dfunction_t *f, *oldf; - int i,l,ls; - progsnum_t initial_progs; - int oldexitdepth; - - int s; - int tempdepth; - - progsnum_t prnum = thread->xprogs; - int fnum = thread->xfunction; - - if (localstack_used + thread->lstackused > LOCALSTACK_SIZE) - PR_RunError(progfuncs, "Too many locals on resumtion of QC thread\n"); - - if (pr_depth + thread->fstackdepth > MAX_STACK_DEPTH) - PR_RunError(progfuncs, "Too large stack on resumtion of QC thread\n"); - - - //do progs switching stuff as appropriate. (fteqw only) - initial_progs = pr_typecurrent; - PR_MoveParms(progfuncs, prnum, pr_typecurrent); - PR_SwitchProgs(progfuncs, prnum); - - - oldexitdepth = prinst->exitdepth; - prinst->exitdepth = pr_depth; - - ls = 0; - //add on the callstack. - for (i = 0; i < thread->fstackdepth; i++) - { - if (pr_depth == prinst->exitdepth) - { - pr_stack[pr_depth].f = pr_xfunction; - pr_stack[pr_depth].s = pr_xstatement; - pr_stack[pr_depth].progsnum = initial_progs; - } - else - { - pr_stack[pr_depth].progsnum = thread->fstack[i].progsnum; - pr_stack[pr_depth].f = pr_progstate[thread->fstack[i].progsnum].functions + thread->fstack[i].fnum; - pr_stack[pr_depth].s = thread->fstack[i].statement; - } - - if (i+1 == thread->fstackdepth) - f = &pr_functions[fnum]; - else - f = pr_progstate[thread->fstack[i+1].progsnum].functions + thread->fstack[i+1].fnum; - for (l = 0; l < f->locals; l++) - { - localstack[localstack_used++] = ((int *)pr_globals)[f->parm_start + l]; - ((int *)pr_globals)[f->parm_start + l] = thread->lstack[ls++]; - } - - pr_depth++; - } - - if (ls != thread->lstackused) - PR_RunError(progfuncs, "Thread stores incorrect locals count\n"); - - - f = &pr_functions[fnum]; - -// thread->lstackused -= f->locals; //the current function is the odd one out. - - //add on the locals stack - memcpy(localstack+localstack_used, thread->lstack, sizeof(int)*thread->lstackused); - localstack_used += thread->lstackused; - - //bung the locals of the current function on the stack. -// for (i=0 ; i < f->locals ; i++) -// ((int *)pr_globals)[f->parm_start + i] = 0xff00ff00;//thread->lstack[thread->lstackused+i]; - - -// PR_EnterFunction (progfuncs, f, initial_progs); - oldf = pr_xfunction; - pr_xfunction = f; - s = thread->xstatement; - - tempdepth = prinst->numtempstringsstack; - PR_ExecuteCode(progfuncs, s); - - - PR_MoveParms(progfuncs, initial_progs, pr_typecurrent); - PR_SwitchProgs(progfuncs, initial_progs); - PR_FreeTemps(progfuncs, tempdepth); - prinst->numtempstringsstack = tempdepth; - - prinst->exitdepth = oldexitdepth; - pr_xfunction = oldf; -} - -void PR_AbortStack (progfuncs_t *progfuncs) -{ - while(pr_depth > prinst->exitdepth+1) - PR_LeaveFunction(progfuncs); - prinst->continuestatement = 0; -} - diff --git a/misc/source/fteqcc-src/pr_multi.c b/misc/source/fteqcc-src/pr_multi.c deleted file mode 100644 index 334536ec..00000000 --- a/misc/source/fteqcc-src/pr_multi.c +++ /dev/null @@ -1,447 +0,0 @@ -#define PROGSUSED -#include "progsint.h" - -#define HunkAlloc BADGDFG sdfhhsf FHS - -void PR_SetBuiltins(int type); -/* -progstate_t *pr_progstate; -progsnum_t pr_typecurrent; -int maxprogs; - -progstate_t *current_progstate; -int numshares; - -sharedvar_t *shares; //shared globals, not including parms -int maxshares; -*/ - -pbool PR_SwitchProgs(progfuncs_t *progfuncs, progsnum_t type) -{ - if ((unsigned)type >= maxprogs) - { - if (type == -1) - { - pr_typecurrent = -1; - current_progstate = NULL; - return true; - } - PR_RunError(progfuncs, "QCLIB: Bad prog type - %i", type); -// Sys_Error("Bad prog type - %i", type); - } - - if (pr_progstate[(unsigned)type].progs == NULL) //we havn't loaded it yet, for some reason - return false; - - current_progstate = &pr_progstate[(unsigned)type]; - - pr_typecurrent = type; - - return true; -} - -void PR_MoveParms(progfuncs_t *progfuncs, progsnum_t newpr, progsnum_t oldpr) //from 2 to 1 -{ - unsigned int a; - progstate_t *np; - progstate_t *op; - - if (newpr == oldpr) - return; //don't bother coping variables to themselves... - - np = &pr_progstate[(int)newpr]; - op = &pr_progstate[(int)oldpr]; - - if ((unsigned)newpr >= maxprogs || !np->globals) - PR_RunError(progfuncs, "QCLIB: Bad prog type - %i", newpr); - if ((unsigned)oldpr >= maxprogs || !op->globals) - return; - - //copy parms. - for (a = 0; a < MAX_PARMS;a++) - { - *(int *)&np->globals[OFS_PARM0+3*a ] = *(int *)&op->globals[OFS_PARM0+3*a ]; - *(int *)&np->globals[OFS_PARM0+3*a+1] = *(int *)&op->globals[OFS_PARM0+3*a+1]; - *(int *)&np->globals[OFS_PARM0+3*a+2] = *(int *)&op->globals[OFS_PARM0+3*a+2]; - } - np->globals[OFS_RETURN] = op->globals[OFS_RETURN]; - np->globals[OFS_RETURN+1] = op->globals[OFS_RETURN+1]; - np->globals[OFS_RETURN+2] = op->globals[OFS_RETURN+2]; - - //move the vars defined as shared. - for (a = 0; a < numshares; a++)//fixme: make offset per progs - { - memmove(&((int *)np->globals)[shares[a].varofs], &((int *)op->globals)[shares[a].varofs], shares[a].size*4); -/* ((int *)p1->globals)[shares[a].varofs] = ((int *)p2->globals)[shares[a].varofs]; - if (shares[a].size > 1) - { - ((int *)p1->globals)[shares[a].varofs+1] = ((int *)p2->globals)[shares[a].varofs+1]; - if (shares[a].size > 2) - ((int *)p1->globals)[shares[a].varofs+2] = ((int *)p2->globals)[shares[a].varofs+2]; - } -*/ - } -} - -progsnum_t PR_LoadProgs(progfuncs_t *progfuncs, char *s, int headercrc, builtin_t *builtins, int numbuiltins) -{ - unsigned int a; - progsnum_t oldtype; - oldtype = pr_typecurrent; - for (a = 0; a < maxprogs; a++) - { - if (pr_progstate[a].progs == NULL) - { - pr_typecurrent = a; - current_progstate = &pr_progstate[a]; - if (PR_ReallyLoadProgs(progfuncs, s, headercrc, &pr_progstate[a], false)) //try and load it - { - current_progstate->builtins = builtins; - current_progstate->numbuiltins = numbuiltins; - if (a <= progfuncs->numprogs) - progfuncs->numprogs = a+1; - -#ifdef QCJIT - current_progstate->jit = PR_GenerateJit(progfuncs); -#endif - if (oldtype != -1) - PR_SwitchProgs(progfuncs, oldtype); - return a; //we could load it. Yay! - } - PR_SwitchProgs(progfuncs, oldtype); - return -1; // loading failed. - } - } - PR_SwitchProgs(progfuncs, oldtype); - return -1; -} - -void PR_ShiftParms(progfuncs_t *progfuncs, int amount) -{ - int a; - for (a = 0; a < MAX_PARMS - amount;a++) - *(int *)&pr_globals[OFS_PARM0+3*a] = *(int *)&pr_globals[OFS_PARM0+3*(amount+a)]; -} - -//forget a progs -void PR_Clear(progfuncs_t *progfuncs) -{ - unsigned int a; - for (a = 0; a < maxprogs; a++) - { -#ifdef QCJIT - if (pr_progstate[a].jit) - PR_CloseJit(pr_progstate[a].jit); -#endif - pr_progstate[a].progs = NULL; - } -} - - - -void QC_StartShares(progfuncs_t *progfuncs) -{ - numshares = 0; - maxshares = 32; - if (shares) - memfree(shares); - shares = memalloc(sizeof(sharedvar_t)*maxshares); -} -void QC_AddSharedVar(progfuncs_t *progfuncs, int start, int size) //fixme: make offset per progs and optional -{ - int ofs; - unsigned int a; - - if (numshares >= maxshares) - { - void *buf; - buf = shares; - maxshares += 16; - shares = memalloc(sizeof(sharedvar_t)*maxshares); - - memcpy(shares, buf, sizeof(sharedvar_t)*numshares); - - memfree(buf); - } - ofs = start; - for (a = 0; a < numshares; a++) - { - if (shares[a].varofs+shares[a].size == ofs) - { - shares[a].size += size; //expand size. - return; - } - if (shares[a].varofs == start) - return; - } - - - shares[numshares].varofs = start; - shares[numshares].size = size; - numshares++; -} - - -//void ShowWatch(void); - -void QC_InitShares(progfuncs_t *progfuncs) -{ -// ShowWatch(); - if (!field) //don't make it so we will just need to remalloc everything - { - maxfields = 64; - field = memalloc(sizeof(fdef_t) * maxfields); - } - - numfields = 0; - progfuncs->fieldadjust = 0; -} - -void QC_FlushProgsOffsets(progfuncs_t *progfuncs) -{ //sets the fields up for loading a new progs. - //fields are matched by name to other progs - //not by offset - unsigned int i; - for (i = 0; i < numfields; i++) - field[i].progsofs = -1; -} - - -//called if a global is defined as a field -//returns offset. - -//vectors must be added before any of their corresponding _x/y/z vars -//in this way, even screwed up progs work. - -//requestedpos is the offset the engine WILL put it at. -//origionaloffs is used to track matching field offsets. fields with the same progs offset overlap - -//note: we probably suffer from progs with renamed system globals. -int QC_RegisterFieldVar(progfuncs_t *progfuncs, unsigned int type, char *name, signed long engineofs, signed long progsofs) -{ -// progstate_t *p; -// int pnum; - unsigned int i; - int namelen; - int ofs; - - int fnum; - - if (!name) //engine can use this to offset all progs fields - { //which fixes constant field offsets (some ktpro arrays) - progfuncs->fieldadjust = fields_size/4; - return 0; - } - - - prinst->reorganisefields = true; - - //look for an existing match - for (i = 0; i < numfields; i++) - { - if (!strcmp(name, field[i].name)) - { - if (field[i].type != type) - { - /*Hexen2/DP compat hack: if the new type is a float and the original type is a vector, make the new def alias to the engine's _x field - this 'works around' the unused .vector color field used for rtlight colours vs the .float color used for particle colours (the float initialisers in map files will expand into the x slot safely). - qc/hc can work around this by just using .vector color/color_x instead, which is the same as this hack, but would resolve defs to allow rtlight colours. - */ - if (field[i].type != ev_vector || type != ev_float) - { - printf("Field type mismatch on \"%s\". %i != %i\n", name, field[i].type, type); - continue; - } - } - if (!progfuncs->fieldadjust && engineofs>=0) - if ((unsigned)engineofs/4 != field[i].ofs) - Sys_Error("Field %s at wrong offset", name); - - if (field[i].progsofs == -1) - field[i].progsofs = progsofs; -// printf("Dupfield %s %i -> %i\n", name, field[i].progsofs,field[i].ofs); - return field[i].ofs-progfuncs->fieldadjust; //got a match - } - } - - if (numfields+1>maxfields) - { - fdef_t *nf; - i = maxfields; - maxfields += 32; - nf = memalloc(sizeof(fdef_t) * maxfields); - memcpy(nf, field, sizeof(fdef_t) * i); - memfree(field); - field = nf; - } - - //try to add a new one - fnum = numfields; - numfields++; - field[fnum].name = name; - if (type == ev_vector) - { - char *n; - namelen = strlen(name)+5; - - n=PRHunkAlloc(progfuncs, namelen); - sprintf(n, "%s_x", name); - ofs = QC_RegisterFieldVar(progfuncs, ev_float, n, engineofs, progsofs); - field[fnum].ofs = ofs+progfuncs->fieldadjust; - - n=PRHunkAlloc(progfuncs, namelen); - sprintf(n, "%s_y", name); - QC_RegisterFieldVar(progfuncs, ev_float, n, (engineofs==-1)?-1:(engineofs+4), (progsofs==-1)?-1:progsofs+1); - - n=PRHunkAlloc(progfuncs, namelen); - sprintf(n, "%s_z", name); - QC_RegisterFieldVar(progfuncs, ev_float, n, (engineofs==-1)?-1:(engineofs+8), (progsofs==-1)?-1:progsofs+2); - } - else if (engineofs >= 0) - { //the engine is setting up a list of required field indexes. - - //paranoid checking of the offset. - /* for (i = 0; i < numfields-1; i++) - { - if (field[i].ofs == ((unsigned)engineofs)/4) - { - if (type == ev_float && field[i].type == ev_vector) //check names - { - if (strncmp(field[i].name, name, strlen(field[i].name))) - Sys_Error("Duplicated offset"); - } - else - Sys_Error("Duplicated offset"); - } - }*/ - if (engineofs&3) - Sys_Error("field %s is %i&3", name, (int)engineofs); - field[fnum].ofs = ofs = engineofs/4; - } - else - { //we just found a new fieldname inside a progs - field[fnum].ofs = ofs = fields_size/4; //add on the end - - //if the progs field offset matches annother offset in the same progs, make it match up with the earlier one. - if (progsofs>=0) - { - for (i = 0; i < numfields-1; i++) - { - if (field[i].progsofs == (unsigned)progsofs) - { -// printf("found union field %s %i -> %i\n", field[i].name, field[i].progsofs, field[i].ofs); - field[fnum].ofs = ofs = field[i].ofs; - break; - } - } - } - } -// if (type != ev_vector) - if (fields_size < (ofs+type_size[type])*4) - fields_size = (ofs+type_size[type])*4; - - if (max_fields_size && fields_size > max_fields_size) - Sys_Error("Allocated too many additional fields after ents were inited."); - field[fnum].type = type; - - field[fnum].progsofs = progsofs; - -// printf("Field %s %i -> %i\n", name, field[fnum].progsofs,field[fnum].ofs); - - //we've finished setting the structure - return ofs - progfuncs->fieldadjust; -} - - -//called if a global is defined as a field -void QC_AddSharedFieldVar(progfuncs_t *progfuncs, int num, char *stringtable) -{ -// progstate_t *p; -// int pnum; - unsigned int i, o; - - char *s; - - //look for an existing match not needed, cos we look a little later too. - /* - for (i = 0; i < numfields; i++) - { - if (!strcmp(pr_globaldefs[num].s_name, field[i].s_name)) - { - //really we should look for a field def - - *(int *)&pr_globals[pr_globaldefs[num].ofs] = field[i].ofs; //got a match - - return; - } - } - */ - - switch(current_progstate->structtype) - { - case PST_KKQWSV: - case PST_DEFAULT: - for (i=1 ; inumfielddefs; i++) - { - if (!strcmp(pr_fielddefs16[i].s_name+stringtable, pr_globaldefs16[num].s_name+stringtable)) - { -// int old = *(int *)&pr_globals[pr_globaldefs16[num].ofs]; - *(int *)&pr_globals[pr_globaldefs16[num].ofs] = QC_RegisterFieldVar(progfuncs, pr_fielddefs16[i].type, pr_globaldefs16[num].s_name+stringtable, -1, *(int *)&pr_globals[pr_globaldefs16[num].ofs]); - -// printf("Field %s %i -> %i\n", pr_globaldefs16[num].s_name+stringtable, old, *(int *)&pr_globals[pr_globaldefs16[num].ofs]); - return; - } - } - - s = pr_globaldefs16[num].s_name+stringtable; - - for (i = 0; i < numfields; i++) - { - o = field[i].progsofs; - if (o == *(unsigned int *)&pr_globals[pr_globaldefs16[num].ofs]) - { -// int old = *(int *)&pr_globals[pr_globaldefs16[num].ofs]; - *(int *)&pr_globals[pr_globaldefs16[num].ofs] = field[i].ofs-progfuncs->fieldadjust; -// printf("Field %s %i -> %i\n", pr_globaldefs16[num].s_name+stringtable, old, *(int *)&pr_globals[pr_globaldefs16[num].ofs]); - return; - } - } - - //oh well, must be a parameter. -// if (*(int *)&pr_globals[pr_globaldefs16[num].ofs]) -// Sys_Error("QCLIB: Global field var with no matching field \"%s\", from offset %i", pr_globaldefs16[num].s_name+stringtable, *(int *)&pr_globals[pr_globaldefs16[num].ofs]); - return; - case PST_FTE32: - case PST_QTEST: - for (i=1 ; inumfielddefs; i++) - { - if (!strcmp(pr_fielddefs32[i].s_name+stringtable, pr_globaldefs32[num].s_name+stringtable)) - { - *(int *)&pr_globals[pr_globaldefs32[num].ofs] = QC_RegisterFieldVar(progfuncs, pr_fielddefs32[i].type, pr_globaldefs32[num].s_name+stringtable, -1, *(int *)&pr_globals[pr_globaldefs32[num].ofs]); - return; - } - } - - s = pr_globaldefs32[num].s_name+stringtable; - - for (i = 0; i < numfields; i++) - { - o = field[i].progsofs; - if (o == *(unsigned int *)&pr_globals[pr_globaldefs32[num].ofs]) - { - *(int *)&pr_globals[pr_globaldefs32[num].ofs] = field[i].ofs-progfuncs->fieldadjust; - return; - } - } - - //oh well, must be a parameter. - if (*(int *)&pr_globals[pr_globaldefs32[num].ofs]) - Sys_Error("QCLIB: Global field var with no matching field \"%s\", from offset %i", pr_globaldefs32[num].s_name+stringtable, *(int *)&pr_globals[pr_globaldefs32[num].ofs]); - return; - default: - Sys_Error("Bad bits"); - break; - } - Sys_Error("Should be unreachable"); -} - diff --git a/misc/source/fteqcc-src/pr_x86.c b/misc/source/fteqcc-src/pr_x86.c deleted file mode 100644 index 9f1f1acc..00000000 --- a/misc/source/fteqcc-src/pr_x86.c +++ /dev/null @@ -1,1445 +0,0 @@ -/* -when I say JIT, I mean load time, not execution time. - -notes: - qc jump offsets are all constants. we have no variable offset jumps (other than function calls/returns) - field remapping... fields are in place, and cannot be adjusted. if a field is not set to 0, its assumed to be a constant. - -optimisations: - none at the moment... - instructions need to be chained. stuff that writes to C should be cacheable, etc. maybe we don't even need to do the write to C - it should also be possible to fold in eq+ifnot, so none of this silly storeing of floats in equality tests - - this means that we need to track which vars are cached and in what form: fpreg, ireg+floatasint, ireg+float. - certain qccx hacks can use fpu operations on ints, so do what the instruction says, rather than considering an add an add regardless of types. - - OP_AND_F, OP_OR_F etc will generally result in ints, and we should be able to keep them as ints if they combine with other ints. - - some instructions are jump sites. any cache must be flushed before the start of the instruction. - some variables are locals, and will only ever be written by a single instruction, then read by the following instruction. such temps do not need to be written, or are overwritten later in the function anyway. - such locals need to be calculated PER FUNCTION as (fte)qcc can overlap locals making multiple distinct locals on a single offset. - - store locals on a proper stack instead of the current absurd mechanism. - - eax - tmp - ebx - prinst->edicttable - ecx - tmp - edx - tmp - esi - debug opcode number - edi - tmp (because its preserved by subfunctions - ebp - - - to use gas to provide binary opcodes: - vim -N blob.s && as blob.s && objdump.exe -d a.out - - - notable mods to test: - prydon gate, due to fpu mangling to carry values between maps -*/ - -#define PROGSUSED -#include "progsint.h" - -#ifdef QCJIT - -#ifndef _WIN32 -#include -#endif - -static float ta, tb, nullfloat=0; - -struct jitstate -{ - unsigned int *statementjumps; //[MAX_STATEMENTS*3] - unsigned char **statementoffsets; //[MAX_STATEMENTS] - unsigned int numjumps; - unsigned char *code; - unsigned int codesize; - unsigned int jitstatements; - - float *glob; - unsigned int cachedglobal; - unsigned int cachereg; -}; - -static void EmitByte(struct jitstate *jit, unsigned char byte) -{ - jit->code[jit->codesize++] = byte; -} -static void Emit4Byte(struct jitstate *jit, unsigned int value) -{ - jit->code[jit->codesize++] = (value>> 0)&0xff; - jit->code[jit->codesize++] = (value>> 8)&0xff; - jit->code[jit->codesize++] = (value>>16)&0xff; - jit->code[jit->codesize++] = (value>>24)&0xff; -} -static void EmitAdr(struct jitstate *jit, void *value) -{ - Emit4Byte(jit, (unsigned int)value); -} -static void EmitFloat(struct jitstate *jit, float value) -{ - union {float f; unsigned int i;} u; - u.f = value; - Emit4Byte(jit, u.i); -} -static void Emit2Byte(struct jitstate *jit, unsigned short value) -{ - jit->code[jit->codesize++] = (value>> 0)&0xff; - jit->code[jit->codesize++] = (value>> 8)&0xff; -} - -static void EmitFOffset(struct jitstate *jit, void *func, int bias) -{ - union {void *f; unsigned int i;} u; - u.f = func; - u.i -= (unsigned int)&jit->code[jit->codesize+bias]; - Emit4Byte(jit, u.i); -} - -static void Emit4ByteJump(struct jitstate *jit, int statementnum, int offset) -{ - jit->statementjumps[jit->numjumps++] = jit->codesize; - jit->statementjumps[jit->numjumps++] = statementnum; - jit->statementjumps[jit->numjumps++] = offset; - - //the offset is filled in later - jit->codesize += 4; -} - -enum -{ - REG_EAX, - REG_ECX, - REG_EDX, - REG_EBX, - REG_ESP, - REG_EBP, - REG_ESI, - REG_EDI, - - /*I'm not going to list S1 here, as that makes things too awkward*/ - REG_S0, - REG_NONE -}; -#define XOR(sr,dr) EmitByte(0x31);EmitByte(0xc0 | (sr<<3) | dr); -#define CLEARREG(reg) XOR(reg,reg) -#define LOADREG(addr, reg) if (reg == REG_EAX) {EmitByte(0xa1);} else {EmitByte(0x8b); EmitByte((reg<<3) | 0x05);} EmitAdr(addr); -#define STOREREG(reg, addr) if (reg == REG_EAX) {EmitByte(0xa3);} else {EmitByte(0x89); EmitByte((reg<<3) | 0x05);} EmitAdr(addr); -#define STOREF(f, addr) EmitByte(0xc7);EmitByte(0x05); EmitAdr(addr);EmitFloat(f); -#define STOREI(i, addr) EmitByte(0xc7);EmitByte(0x05); EmitAdr(addr);Emit4Byte(i); -#define SETREGI(val,reg) EmitByte(0xbe);Emit4Byte(val); - -#define ARGREGS(a,b,c) GCache_Load(jit, op[i].a, a, op[i].b, b, op[i].c, c) -#define RESULTREG(r) GCache_Store(jit, op[i].c, r) - -//for the purposes of the cache, 'temp' offsets are only read when they have been written only within the preceeding control block. -//if they were read at any other time, then we must write them out in full. -//this logic applies only to locals of a function. -//#define USECACHE - -static void GCache_Load(struct jitstate *jit, int ao, int ar, int bo, int br, int co, int cr) -{ -#if USECACHE - if (jit->cachedreg != REG_NONE) - { - /*something is cached, if its one of the input offsets then can chain the instruction*/ - - if (jit->cachedglobal === ao && ar != REG_NONE) - { - if (jit->cachedreg == ar) - ar = REG_NONE; - } - if (jit->cachedglobal === bo && br != REG_NONE) - { - if (jit->cachedreg == br) - br = REG_NONE; - } - if (jit->cachedglobal === co && cr != REG_NONE) - { - if (jit->cachedreg == cr) - cr = REG_NONE; - } - - if (!istemp(ao)) - { - /*purge the old cache*/ - switch(jit->cachedreg) - { - case REG_NONE: - break; - case REG_S0: - //fstps glob[C] - EmitByte(0xd9);EmitByte(0x1d);EmitAdr(jit->glob + jit->cachedglobal); - break; - default: - STOREREG(jit->cachedreg, jit->glob + jit->cachedglobal); - break; - } - jit->cachedglobal = -1; - jit->cachedreg = REG_NONE; - } - -#endif - switch(ar) - { - case REG_NONE: - break; - case REG_S0: - //flds glob[A] - EmitByte(0xd9);EmitByte(0x05);EmitAdr(jit->glob + op[i].a); - break; - default: - LOADREG(jit->glob + ao, ar); - break; - } - - switch(br) - { - case REG_NONE: - break; - case REG_S0: - //flds glob[A] - EmitByte(0xd9);EmitByte(0x05);EmitAdr(jit->glob + op[i].b); - break; - default: - LOADREG(jit->glob + bo, br); - break; - } - - switch(cr) - { - case REG_NONE: - break; - case REG_S0: - //flds glob[A] - EmitByte(0xd9);EmitByte(0x05);EmitAdr(jit->glob + op[i].c); - break; - default: - LOADREG(jit->glob + co, cr); - break; - } -} -static void GCache_Store(struct jitstate *jit, int ofs, int reg) -{ -#if USECACHE - jit->cachedglobal = ofs; - jit->cachedreg = reg; -#else - switch(reg) - { - case REG_NONE: - break; - case REG_S0: - //fstps glob[C] - EmitByte(0xd9);EmitByte(0x1d);EmitAdr(jit->glob + ofs); - break; - default: - STOREREG(reg, jit->glob + ofs); - break; - } -#endif -} - -static void *LocalLoc(struct jitstate *jit) -{ - return &jit->code[jit->codesize]; -} -static void *LocalJmp(struct jitstate *jit, int cond) -{ - /*floating point ops don't set the sign flag, thus we use the 'above/below' instructions instead of 'greater/less' instructions*/ - if (cond == OP_GOTO) - EmitByte(jit, 0xeb); //jmp - else if (cond == OP_LE_F) - EmitByte(jit, 0x76); //jbe - else if (cond == OP_GE_F) - EmitByte(jit, 0x73); //jae - else if (cond == OP_LT_F) - EmitByte(jit, 0x72); //jb - else if (cond == OP_GT_F) - EmitByte(jit, 0x77); //ja - else if (cond == OP_LE_I) - EmitByte(jit, 0x7e); //jle - else if (cond == OP_LT_I) - EmitByte(jit, 0x7c); //jl - else if ((cond >= OP_NE_F && cond <= OP_NE_FNC) || cond == OP_NE_I) - EmitByte(jit, 0x75); //jne - else if ((cond >= OP_EQ_F && cond <= OP_EQ_FNC) || cond == OP_EQ_I) - EmitByte(jit, 0x74); //je -#if defined(DEBUG) && defined(_WIN32) - else - { - OutputDebugString("oh noes!\n"); - return NULL; - } -#endif - - EmitByte(jit, 0); - - return LocalLoc(jit); -} -static void LocalJmpLoc(void *jmp, void *loc) -{ - int offs; - unsigned char *a = jmp; - offs = (char *)loc - (char *)jmp; -#if defined(DEBUG) && defined(_WIN32) - if (offs > 127 || offs <= -128) - { - OutputDebugStringA("bad jump\n"); - a[-2] = 0xcd; - a[-1] = 0xcc; - return; - } -#endif - a[-1] = offs; -} - -static void FixupJumps(struct jitstate *jit) -{ - unsigned int j; - unsigned char *codesrc; - unsigned char *codedst; - unsigned int offset; - - unsigned int v; - - for (j = 0; j < jit->numjumps;) - { - v = jit->statementjumps[j++]; - codesrc = &jit->code[v]; - - v = jit->statementjumps[j++]; - codedst = jit->statementoffsets[v]; - - v = jit->statementjumps[j++]; - offset = (int)(codedst - (codesrc-v)); //3rd term because the jump is relative to the instruction start, not the instruction's offset - - codesrc[0] = (offset>> 0)&0xff; - codesrc[1] = (offset>> 8)&0xff; - codesrc[2] = (offset>>16)&0xff; - codesrc[3] = (offset>>24)&0xff; - } -} - -int ASMCALL PR_LeaveFunction (progfuncs_t *progfuncs); -int ASMCALL PR_EnterFunction (progfuncs_t *progfuncs, dfunction_t *f, int progsnum); - -void PR_CloseJit(struct jitstate *jit) -{ - if (jit) - { - free(jit->statementjumps); - free(jit->statementoffsets); -#ifndef _WIN32 - munmap(jit->code, jit->jitstatements * 500); -#else - free(jit->code); -#endif - free(jit) - } -} - -#define EmitByte(v) EmitByte(jit, v) -#define EmitAdr(v) EmitAdr(jit, v) -#define EmitFOffset(a,b) EmitFOffset(jit, a, b) -#define Emit4ByteJump(a,b) Emit4ByteJump(jit, a, b) -#define Emit4Byte(v) Emit4Byte(jit, v) -#define EmitFloat(v) EmitFloat(jit, v) -#define LocalJmp(v) LocalJmp(jit, v) -#define LocalLoc() LocalLoc(jit) - - -struct jitstate *PR_GenerateJit(progfuncs_t *progfuncs) -{ - struct jitstate *jit; - - void *j0, *l0; - void *j1, *l1; - void *j2, *l2; - unsigned int i; - dstatement16_t *op = (dstatement16_t*)current_progstate->statements; - unsigned int numstatements = current_progstate->progs->numstatements; - int *glob = (int*)current_progstate->globals; - - if (current_progstate->numbuiltins) - return NULL; - jit = malloc(sizeof(*jit)); - jit->jitstatements = numstatements; - - jit->statementjumps = malloc(numstatements*12); - jit->statementoffsets = malloc(numstatements*4); -#ifndef _WIN32 - jit->code = mmap(NULL, numstatements*500, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); -#else - jit->code = malloc(numstatements*500); -#endif - if (!jit->code) - return NULL; - - jit->numjumps = 0; - jit->codesize = 0; - - - - for (i = 0; i < numstatements; i++) - { - jit->statementoffsets[i] = &jit->code[jit->codesize]; - - /*DEBUG*/ - SETREGI(op[i].op, REG_ESI); - - switch(op[i].op) - { - //jumps - case OP_IF_I: - //integer compare - //if a, goto b - - //cmpl $0,glob[A] - EmitByte(0x83);EmitByte(0x3d);EmitAdr(glob + op[i].a);EmitByte(0x0); - //jne B - EmitByte(0x0f);EmitByte(0x85);Emit4ByteJump(i + (signed short)op[i].b, -4); - break; - - case OP_IFNOT_I: - //integer compare - //if !a, goto b - - //cmpl $0,glob[A] - EmitByte(0x83);EmitByte(0x3d);EmitAdr(glob + op[i].a);EmitByte(0x0); - //je B - EmitByte(0x0f);EmitByte(0x84);Emit4ByteJump(i + (signed short)op[i].b, -4); - break; - - case OP_GOTO: - EmitByte(0xE9);Emit4ByteJump(i + (signed short)op[i].a, -4); - break; - - //function returns - case OP_DONE: - case OP_RETURN: - //done and return are the same - - //part 1: store A into OFS_RETURN - - if (!op[i].a) - { - //assumption: anything that returns address 0 is a void or zero return. - //thus clear eax and copy that to the return vector. - CLEARREG(REG_EAX); - STOREREG(REG_EAX, glob + OFS_RETURN+0); - STOREREG(REG_EAX, glob + OFS_RETURN+1); - STOREREG(REG_EAX, glob + OFS_RETURN+2); - } - else - { - LOADREG(glob + op[i].a+0, REG_EAX); - LOADREG(glob + op[i].a+1, REG_EDX); - LOADREG(glob + op[i].a+2, REG_ECX); - STOREREG(REG_EAX, glob + OFS_RETURN+0); - STOREREG(REG_EDX, glob + OFS_RETURN+1); - STOREREG(REG_ECX, glob + OFS_RETURN+2); - } - - //call leavefunction to get the return address - -// pushl progfuncs - EmitByte(0x68);EmitAdr(progfuncs); -// call PR_LeaveFunction - EmitByte(0xe8);EmitFOffset(PR_LeaveFunction, 4); -// add $4,%esp - EmitByte(0x83);EmitByte(0xc4);EmitByte(0x04); -// movl pr_depth,%edx - EmitByte(0x8b);EmitByte(0x15);EmitAdr(&pr_depth); -// cmp prinst->exitdepth,%edx - EmitByte(0x3b);EmitByte(0x15);EmitAdr(&prinst->exitdepth); -// je returntoc - j1 = LocalJmp(OP_EQ_E); -// mov statementoffsets[%eax*4],%eax - EmitByte(0x8b);EmitByte(0x04);EmitByte(0x85);EmitAdr(jit->statementoffsets+1); -// jmp *eax - EmitByte(0xff);EmitByte(0xe0); -// returntoc: - l1 = LocalLoc(); -// ret - EmitByte(0xc3); - - LocalJmpLoc(j1,l1); - break; - - //function calls - case OP_CALL0: - case OP_CALL1: - case OP_CALL2: - case OP_CALL3: - case OP_CALL4: - case OP_CALL5: - case OP_CALL6: - case OP_CALL7: - case OP_CALL8: - //FIXME: the size of this instruction is going to hurt cache performance if every single function call is expanded into this HUGE CHUNK of gibberish! - //FIXME: consider the feasability of just calling a C function and just jumping to the address it returns. - - //save the state in place the rest of the engine can cope with - //movl $i, pr_xstatement - EmitByte( 0xc7);EmitByte(0x05);EmitAdr(&pr_xstatement);Emit4Byte(i); - //movl $(op[i].op-OP_CALL0), pr_argc - EmitByte( 0xc7);EmitByte(0x05);EmitAdr(&pr_argc);Emit4Byte(op[i].op-OP_CALL0); - - //figure out who we're calling, and what that involves - //%eax = glob[A] - LOADREG(glob + op[i].a, REG_EAX); - //eax is now the func num - - //mov %eax,%ecx - EmitByte(0x89); EmitByte(0xc1); - //shr $24,%ecx - EmitByte(0xc1); EmitByte(0xe9); EmitByte(0x18); - //ecx is now the progs num for the new func - - //cmp %ecx,pr_typecurrent - EmitByte(0x39); EmitByte(0x0d); EmitAdr(&pr_typecurrent); - //je sameprogs - j1 = LocalJmp(OP_EQ_I); - { - //can't handle switching progs - - //FIXME: recurse though PR_ExecuteProgram - //push eax - //push progfuncs - //call PR_ExecuteProgram - //add $8,%esp - //remember to change the je above - - //err... exit depth? no idea - EmitByte(0xcd);EmitByte(op[i].op); //int $X - - - //ret - EmitByte(0xc3); - } - //sameprogs: - l1 = LocalLoc(); - LocalJmpLoc(j1,l1); - - //andl $0x00ffffff, %eax - EmitByte(0x25);Emit4Byte(0x00ffffff); - - //mov $sizeof(dfunction_t),%edx - EmitByte(0xba);Emit4Byte(sizeof(dfunction_t)); - //mul %edx - EmitByte(0xf7); EmitByte(0xe2); - //add pr_functions,%eax - EmitByte(0x05); EmitAdr(pr_functions); - - //eax is now the dfunction_t to be called - //edx is clobbered. - - //mov (%eax),%edx - EmitByte(0x8b);EmitByte(0x10); - //edx is now the first statement number - //cmp $0,%edx - EmitByte(0x83);EmitByte(0xfa);EmitByte(0x00); - //jl isabuiltin - j1 = LocalJmp(OP_LT_I); - { - /* call the function*/ - //push %ecx - EmitByte(0x51); - //push %eax - EmitByte(0x50); - //pushl progfuncs - EmitByte(0x68);EmitAdr(progfuncs); - //call PR_EnterFunction - EmitByte(0xe8);EmitFOffset(PR_EnterFunction, 4); - //sub $12,%esp - EmitByte(0x83);EmitByte(0xc4);EmitByte(0xc); - //eax is now the next statement number (first of the new function, usually equal to ecx, but not always) - - //jmp statementoffsets[%eax*4] - EmitByte(0xff);EmitByte(0x24);EmitByte(0x85);EmitAdr(jit->statementoffsets+1); - } - /*its a builtin, figure out which, and call it*/ - //isabuiltin: - l1 = LocalLoc(); - LocalJmpLoc(j1,l1); - - //push current_progstate->globals - EmitByte(0x68);EmitAdr(current_progstate->globals); - //push progfuncs - EmitByte(0x68);EmitAdr(progfuncs); - //neg %edx - EmitByte(0xf7);EmitByte(0xda); - //call externs->globalbuiltins[%edx,4] -//FIXME: make sure this dereferences - EmitByte(0xff);EmitByte(0x14);EmitByte(0x95);EmitAdr(externs->globalbuiltins); - //add $8,%esp - EmitByte(0x83);EmitByte(0xc4);EmitByte(0x8); - - //but that builtin might have been Abort() - - LOADREG(&prinst->continuestatement, REG_EAX); - //cmp $-1,%eax - EmitByte(0x83);EmitByte(0xf8);EmitByte(0xff); - //je donebuiltincall - j1 = LocalJmp(OP_EQ_I); - { - //mov $-1,prinst->continuestatement - EmitByte(0xc7);EmitByte(0x05);EmitAdr(&prinst->continuestatement);Emit4Byte((unsigned int)-1); - - //jmp statementoffsets[%eax*4] - EmitByte(0xff);EmitByte(0x24);EmitByte(0x85);EmitAdr(jit->statementoffsets); - } - //donebuiltincall: - l1 = LocalLoc(); - LocalJmpLoc(j1,l1); - break; - - case OP_MUL_F: - //flds glob[A] - EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a); - //fmuls glob[B] - EmitByte(0xd8);EmitByte(0x0d);EmitAdr(glob + op[i].b); - //fstps glob[C] - EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c); - break; - case OP_DIV_F: - //flds glob[A] - EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a); - //fdivs glob[B] - EmitByte(0xd8);EmitByte(0x35);EmitAdr(glob + op[i].b); - //fstps glob[C] - EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c); - break; - case OP_ADD_F: - //flds glob[A] - EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a); - //fadds glob[B] - EmitByte(0xd8);EmitByte(0x05);EmitAdr(glob + op[i].b); - //fstps glob[C] - EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c); - break; - case OP_SUB_F: - //flds glob[A] - EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a); - //fsubs glob[B] - EmitByte(0xd8);EmitByte(0x25);EmitAdr(glob + op[i].b); - //fstps glob[C] - EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c); - break; - - case OP_NOT_F: - //fldz - EmitByte(0xd9);EmitByte(0xee); - //fcomps glob[A] - EmitByte(0xd8); EmitByte(0x1d); EmitAdr(glob + op[i].a); - //fnstsw %ax - EmitByte(0xdf);EmitByte(0xe0); - //testb 0x40,%ah - EmitByte(0xf6);EmitByte(0xc4);EmitByte(0x40); - - j1 = LocalJmp(OP_NE_F); - { - STOREF(0.0f, glob + op[i].c); - j2 = LocalJmp(OP_GOTO); - } - { - //noteq: - l1 = LocalLoc(); - STOREF(1.0f, glob + op[i].c); - } - //end: - l2 = LocalLoc(); - LocalJmpLoc(j1,l1); - LocalJmpLoc(j2,l2); - break; - - case OP_STORE_F: - case OP_STORE_S: - case OP_STORE_ENT: - case OP_STORE_FLD: - case OP_STORE_FNC: - LOADREG(glob + op[i].a, REG_EAX); - STOREREG(REG_EAX, glob + op[i].b); - break; - - case OP_STORE_V: - LOADREG(glob + op[i].a+0, REG_EAX); - LOADREG(glob + op[i].a+1, REG_EDX); - LOADREG(glob + op[i].a+2, REG_ECX); - STOREREG(REG_EAX, glob + op[i].b+0); - STOREREG(REG_EDX, glob + op[i].b+1); - STOREREG(REG_ECX, glob + op[i].b+2); - break; - - case OP_LOAD_F: - case OP_LOAD_S: - case OP_LOAD_ENT: - case OP_LOAD_FLD: - case OP_LOAD_FNC: - case OP_LOAD_V: - //a is the ent number, b is the field - //c is the dest - - LOADREG(glob + op[i].a, REG_EAX); - LOADREG(glob + op[i].b, REG_ECX); - - //FIXME: bound eax (ent number) - //FIXME: bound ecx (field index) - //mov (ebx,eax,4).%eax - EmitByte(0x8b); EmitByte(0x04); EmitByte(0x83); - //eax is now an edictrun_t - //mov fields(,%eax,4),%edx - EmitByte(0x8b);EmitByte(0x50);EmitByte((int)&((edictrun_t*)NULL)->fields); - //edx is now the field array for that ent - - //mov fieldajust(%edx,%ecx,4),%eax - EmitByte(0x8b); EmitByte(0x84); EmitByte(0x8a); Emit4Byte(progfuncs->fieldadjust*4); - - STOREREG(REG_EAX, glob + op[i].c) - - if (op[i].op == OP_LOAD_V) - { - //mov fieldajust+4(%edx,%ecx,4),%eax - EmitByte(0x8b); EmitByte(0x84); EmitByte(0x8a); Emit4Byte(4+progfuncs->fieldadjust*4); - STOREREG(REG_EAX, glob + op[i].c+1) - - //mov fieldajust+8(%edx,%ecx,4),%eax - EmitByte(0x8b); EmitByte(0x84); EmitByte(0x8a); Emit4Byte(8+progfuncs->fieldadjust*4); - STOREREG(REG_EAX, glob + op[i].c+2) - } - break; - - case OP_ADDRESS: - //a is the ent number, b is the field - //c is the dest - - LOADREG(glob + op[i].a, REG_EAX); - LOADREG(glob + op[i].b, REG_ECX); - - //FIXME: bound eax (ent number) - //FIXME: bound ecx (field index) - //mov (ebx,eax,4).%eax - EmitByte(0x8b); EmitByte(0x04); EmitByte(0x83); - //eax is now an edictrun_t - //mov fields(,%eax,4),%edx - EmitByte(0x8b);EmitByte(0x50);EmitByte((int)&((edictrun_t*)NULL)->fields); - //edx is now the field array for that ent - //mov fieldajust(%edx,%ecx,4),%eax //offset = progfuncs->fieldadjust - //EmitByte(0x8d); EmitByte(0x84); EmitByte(0x8a); EmitByte(progfuncs->fieldadjust*4); - EmitByte(0x8d); EmitByte(0x84); EmitByte(0x8a); Emit4Byte(progfuncs->fieldadjust*4); - STOREREG(REG_EAX, glob + op[i].c); - break; - - case OP_STOREP_F: - case OP_STOREP_S: - case OP_STOREP_ENT: - case OP_STOREP_FLD: - case OP_STOREP_FNC: - LOADREG(glob + op[i].a, REG_EAX); - LOADREG(glob + op[i].b, REG_ECX); - //mov %eax,(%ecx) - EmitByte(0x89);EmitByte(0x01); - break; - - case OP_STOREP_V: - LOADREG(glob + op[i].b, REG_ECX); - - LOADREG(glob + op[i].a+0, REG_EAX); - //mov %eax,0(%ecx) - EmitByte(0x89);EmitByte(0x01); - - LOADREG(glob + op[i].a+1, REG_EAX); - //mov %eax,4(%ecx) - EmitByte(0x89);EmitByte(0x41);EmitByte(0x04); - - LOADREG(glob + op[i].a+2, REG_EAX); - //mov %eax,8(%ecx) - EmitByte(0x89);EmitByte(0x41);EmitByte(0x08); - break; - - case OP_NE_I: - case OP_NE_E: - case OP_NE_FNC: - case OP_EQ_I: - case OP_EQ_E: - case OP_EQ_FNC: - //integer equality - LOADREG(glob + op[i].a, REG_EAX); - - //cmp glob[B],%eax - EmitByte(0x3b); EmitByte(0x04); EmitByte(0x25); EmitAdr(glob + op[i].b); - j1 = LocalJmp(op[i].op); - { - STOREF(0.0f, glob + op[i].c); - j2 = LocalJmp(OP_GOTO); - } - { - l1 = LocalLoc(); - STOREF(1.0f, glob + op[i].c); - } - l2 = LocalLoc(); - LocalJmpLoc(j1,l1); - LocalJmpLoc(j2,l2); - break; - - case OP_NOT_I: - case OP_NOT_ENT: - case OP_NOT_FNC: - //cmp glob[B],$0 - EmitByte(0x83); EmitByte(0x3d); EmitAdr(glob + op[i].a); EmitByte(0x00); - j1 = LocalJmp(OP_NE_I); - { - STOREF(1.0f, glob + op[i].c); - j2 = LocalJmp(OP_GOTO); - } - { - l1 = LocalLoc(); - STOREF(0.0f, glob + op[i].c); - } - l2 = LocalLoc(); - LocalJmpLoc(j1,l1); - LocalJmpLoc(j2,l2); - break; - - case OP_BITOR_F: //floats... - //flds glob[A] - EmitByte(0xd9); EmitByte(0x05);EmitAdr(glob + op[i].a); - //flds glob[B] - EmitByte(0xd9); EmitByte(0x05);EmitAdr(glob + op[i].b); - //fistp tb - EmitByte(0xdb); EmitByte(0x1d);EmitAdr(&tb); - //fistp ta - EmitByte(0xdb); EmitByte(0x1d);EmitAdr(&ta); - LOADREG(&ta, REG_EAX) - //or %eax,tb - EmitByte(0x09); EmitByte(0x05);EmitAdr(&tb); - //fild tb - EmitByte(0xdb); EmitByte(0x05);EmitAdr(&tb); - //fstps glob[C] - EmitByte(0xd9); EmitByte(0x1d);EmitAdr(glob + op[i].c); - break; - - case OP_BITAND_F: - //flds glob[A] - EmitByte(0xd9); EmitByte(0x05);EmitAdr(glob + op[i].a); - //flds glob[B] - EmitByte(0xd9); EmitByte(0x05);EmitAdr(glob + op[i].b); - //fistp tb - EmitByte(0xdb); EmitByte(0x1d);EmitAdr(&tb); - //fistp ta - EmitByte(0xdb); EmitByte(0x1d);EmitAdr(&ta); - /*two args are now at ta and tb*/ - LOADREG(&ta, REG_EAX) - //and tb,%eax - EmitByte(0x21); EmitByte(0x05);EmitAdr(&tb); - /*we just wrote the int value to tb, convert that to a float and store it at c*/ - //fild tb - EmitByte(0xdb); EmitByte(0x05);EmitAdr(&tb); - //fstps glob[C] - EmitByte(0xd9); EmitByte(0x1d);EmitAdr(glob + op[i].c); - break; - - case OP_AND_F: - //test floats properly, so we don't get confused with -0.0 - //FIXME: is it feasable to grab the value as an int and test it against 0x7fffffff? - - //flds glob[A] - EmitByte(0xd9); EmitByte(0x05); EmitAdr(glob + op[i].a); - //fcomps nullfloat - EmitByte(0xd8); EmitByte(0x1d); EmitAdr(&nullfloat); - //fnstsw %ax - EmitByte(0xdf); EmitByte(0xe0); - //test $0x40,%ah - EmitByte(0xf6); EmitByte(0xc4);EmitByte(0x40); - //jz onefalse - EmitByte(0x75); EmitByte(0x1f); - - //flds glob[B] - EmitByte(0xd9); EmitByte(0x05); EmitAdr(glob + op[i].b); - //fcomps nullfloat - EmitByte(0xd8); EmitByte(0x1d); EmitAdr(&nullfloat); - //fnstsw %ax - EmitByte(0xdf); EmitByte(0xe0); - //test $0x40,%ah - EmitByte(0xf6); EmitByte(0xc4);EmitByte(0x40); - //jnz onefalse - EmitByte(0x75); EmitByte(0x0c); - - //mov float0,glob[C] - EmitByte(0xc7); EmitByte(0x05); EmitAdr(glob + op[i].c); EmitFloat(1.0f); - //jmp done - EmitByte(0xeb); EmitByte(0x0a); - - //onefalse: - //mov float1,glob[C] - EmitByte(0xc7); EmitByte(0x05); EmitAdr(glob + op[i].c); EmitFloat(0.0f); - //done: - break; - case OP_OR_F: - //test floats properly, so we don't get confused with -0.0 - - //flds glob[A] - EmitByte(0xd9); EmitByte(0x05); EmitAdr(glob + op[i].a); - //fcomps nullfloat - EmitByte(0xd8); EmitByte(0x1d); EmitAdr(&nullfloat); - //fnstsw %ax - EmitByte(0xdf); EmitByte(0xe0); - //test $0x40,%ah - EmitByte(0xf6); EmitByte(0xc4);EmitByte(0x40); - //je onetrue - EmitByte(0x74); EmitByte(0x1f); - - //flds glob[B] - EmitByte(0xd9); EmitByte(0x05); EmitAdr(glob + op[i].b); - //fcomps nullfloat - EmitByte(0xd8); EmitByte(0x1d); EmitAdr(&nullfloat); - //fnstsw %ax - EmitByte(0xdf); EmitByte(0xe0); - //test $0x40,%ah - EmitByte(0xf6); EmitByte(0xc4);EmitByte(0x40); - //je onetrue - EmitByte(0x74); EmitByte(0x0c); - - //mov float0,glob[C] - EmitByte(0xc7); EmitByte(0x05); EmitAdr(glob + op[i].c); EmitFloat(0.0f); - //jmp done - EmitByte(0xeb); EmitByte(0x0a); - - //onetrue: - //mov float1,glob[C] - EmitByte(0xc7); EmitByte(0x05); EmitAdr(glob + op[i].c); EmitFloat(1.0f); - //done: - break; - - case OP_EQ_S: - case OP_NE_S: - { - //put a in ecx - LOADREG(glob + op[i].a, REG_ECX); - //put b in edi - LOADREG(glob + op[i].b, REG_EDI); -/* - //early out if they're equal - //cmp %ecx,%edi - EmitByte(0x39); EmitByte(0xc0 | (REG_EDI<<3) | REG_ECX); - j1c = LocalJmp(OP_EQ_S); - - //if a is 0, check if b is "" - //jecxz ais0 - EmitByte(0xe3); EmitByte(0x1a); - - //if b is 0, check if a is "" - //cmp $0,%edi - EmitByte(0x83); EmitByte(0xff); EmitByte(0x00); - //jne bnot0 - EmitByte(0x75); EmitByte(0x2a); - { - //push a - EmitByte(0x51); - //push progfuncs - EmitByte(0x68); EmitAdr(progfuncs); - //call PR_StringToNative - EmitByte(0xe8); EmitFOffset(PR_StringToNative,4); - //add $8,%esp - EmitByte(0x83); EmitByte(0xc4); EmitByte(0x08); - //cmpb $0,(%eax) - EmitByte(0x80); EmitByte(0x38); EmitByte(0x00); - j1b = LocalJmp(OP_EQ_S); - j0b = LocalJmp(OP_GOTO); - } - - //ais0: - { - //push edi - EmitByte(0x57); - //push progfuncs - EmitByte(0x68); EmitAdr(progfuncs); - //call PR_StringToNative - EmitByte(0xe8); EmitFOffset(PR_StringToNative,4); - //add $8,%esp - EmitByte(0x83); EmitByte(0xc4); EmitByte(0x08); - //cmpb $0,(%eax) - EmitByte(0x80); EmitByte(0x38); EmitByte(0x00); - //je _true - EmitByte(0x74); EmitByte(0x36); - //jmp _false - EmitByte(0xeb); EmitByte(0x28); - } - //bnot0: -*/ -LOADREG(glob + op[i].a, REG_ECX); - //push ecx - EmitByte(0x51); - //push progfuncs - EmitByte(0x68); EmitAdr(progfuncs); - //call PR_StringToNative - EmitByte(0xe8); EmitFOffset(PR_StringToNative,4); - //push %eax - EmitByte(0x50); - -LOADREG(glob + op[i].b, REG_EDI); - //push %edi - EmitByte(0x57); - //push progfuncs - EmitByte(0x68); EmitAdr(progfuncs); - //call PR_StringToNative - EmitByte(0xe8); EmitFOffset(PR_StringToNative,4); - //add $8,%esp - EmitByte(0x83); EmitByte(0xc4); EmitByte(0x08); - - - //push %eax - EmitByte(0x50); - //call strcmp - EmitByte(0xe8); EmitFOffset(strcmp,4); - //add $16,%esp - EmitByte(0x83); EmitByte(0xc4); EmitByte(0x10); - - //cmp $0,%eax - EmitByte(0x83); EmitByte(0xf8); EmitByte(0x00); - j1 = LocalJmp(OP_EQ_S); - { - l0 = LocalLoc(); - STOREF((op[i].op == OP_NE_S)?1.0f:0.0f, glob + op[i].c); - j2 = LocalJmp(OP_GOTO); - } - { - l1 = LocalLoc(); - STOREF((op[i].op == OP_NE_S)?0.0f:1.0f, glob + op[i].c); - } - l2 = LocalLoc(); - -// LocalJmpLoc(j0b, l0); - LocalJmpLoc(j1, l1); -// LocalJmpLoc(j1b, l1); - LocalJmpLoc(j2, l2); - } - break; - - case OP_NOT_S: - LOADREG(glob + op[i].a, REG_EAX) - - //cmp $0,%eax - EmitByte(0x83); EmitByte(0xf8); EmitByte(0x00); - j2 = LocalJmp(OP_EQ_S); - - //push %eax - EmitByte(0x50); - //push progfuncs - EmitByte(0x68); EmitAdr(progfuncs); - //call PR_StringToNative - EmitByte(0xe8); EmitFOffset(PR_StringToNative,4); - //add $8,%esp - EmitByte(0x83); EmitByte(0xc4); EmitByte(0x08); - - //cmpb $0,(%eax) - EmitByte(0x80); EmitByte(0x38); EmitByte(0x00); - j1 = LocalJmp(OP_EQ_S); - { - STOREF(0.0f, glob + op[i].c); - j0 = LocalJmp(OP_GOTO); - } - { - l1 = LocalLoc(); - STOREF(1.0f, glob + op[i].c); - } - l2 = LocalLoc(); - LocalJmpLoc(j2, l1); - LocalJmpLoc(j1, l1); - LocalJmpLoc(j0, l2); - break; - - case OP_ADD_V: - //flds glob[A] - EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+0); - //fadds glob[B] - EmitByte(0xd8);EmitByte(0x05);EmitAdr(glob + op[i].b+0); - //fstps glob[C] - EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c+0); - - //flds glob[A] - EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+1); - //fadds glob[B] - EmitByte(0xd8);EmitByte(0x05);EmitAdr(glob + op[i].b+1); - //fstps glob[C] - EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c+1); - - //flds glob[A] - EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+2); - //fadds glob[B] - EmitByte(0xd8);EmitByte(0x05);EmitAdr(glob + op[i].b+2); - //fstps glob[C] - EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c+2); - break; - case OP_SUB_V: - //flds glob[A] - EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+0); - //fsubs glob[B] - EmitByte(0xd8);EmitByte(0x25);EmitAdr(glob + op[i].b+0); - //fstps glob[C] - EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c+0); - - //flds glob[A] - EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+1); - //fsubs glob[B] - EmitByte(0xd8);EmitByte(0x25);EmitAdr(glob + op[i].b+1); - //fstps glob[C] - EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c+1); - - //flds glob[A] - EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+2); - //fsubs glob[B] - EmitByte(0xd8);EmitByte(0x25);EmitAdr(glob + op[i].b+2); - //fstps glob[C] - EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c+2); - break; - - case OP_MUL_V: - //this is actually a dotproduct - //flds glob[A] - EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+0); - //fmuls glob[B] - EmitByte(0xd8);EmitByte(0x0d);EmitAdr(glob + op[i].b+0); - - //flds glob[A] - EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+1); - //fmuls glob[B] - EmitByte(0xd8);EmitByte(0x0d);EmitAdr(glob + op[i].b+1); - - //flds glob[A] - EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+2); - //fmuls glob[B] - EmitByte(0xd8);EmitByte(0x0d);EmitAdr(glob + op[i].b+2); - - //faddp - EmitByte(0xde);EmitByte(0xc1); - //faddp - EmitByte(0xde);EmitByte(0xc1); - - //fstps glob[C] - EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c); - break; - - case OP_EQ_F: - case OP_NE_F: - case OP_LE_F: - case OP_GE_F: - case OP_LT_F: - case OP_GT_F: - //flds glob[A] - EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].b); - //flds glob[B] - EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a); - //fcomip %st(1),%st - EmitByte(0xdf);EmitByte(0xe9); - //fstp %st(0) (aka: pop) - EmitByte(0xdd);EmitByte(0xd8); - - j1 = LocalJmp(op[i].op); - { - STOREF(0.0f, glob + op[i].c); - j2 = LocalJmp(OP_GOTO); - } - { - l1 = LocalLoc(); - STOREF(1.0f, glob + op[i].c); - } - l2 = LocalLoc(); - LocalJmpLoc(j1,l1); - LocalJmpLoc(j2,l2); - break; - - case OP_MUL_FV: - case OP_MUL_VF: - // - { - int v; - int f; - if (op[i].op == OP_MUL_FV) - { - f = op[i].a; - v = op[i].b; - } - else - { - v = op[i].a; - f = op[i].b; - } - - //flds glob[F] - EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + f); - - //flds glob[V0] - EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + v+0); - //fmul st(1) - EmitByte(0xd8);EmitByte(0xc9); - //fstps glob[C] - EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c+0); - - //flds glob[V0] - EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + v+1); - //fmul st(1) - EmitByte(0xd8);EmitByte(0xc9); - //fstps glob[C] - EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c+1); - - //flds glob[V0] - EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + v+2); - //fmul st(1) - EmitByte(0xd8);EmitByte(0xc9); - //fstps glob[C] - EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c+2); - - //fstp %st(0) (aka: pop) - EmitByte(0xdd);EmitByte(0xd8); - } - break; - - case OP_STATE: - //externs->stateop(progfuncs, OPA->_float, OPB->function); - //push b - EmitByte(0xff);EmitByte(0x35);EmitAdr(glob + op[i].b); - //push a - EmitByte(0xff);EmitByte(0x35);EmitAdr(glob + op[i].a); - //push $progfuncs - EmitByte(0x68); EmitAdr(progfuncs); - //call externs->stateop - EmitByte(0xe8); EmitFOffset(externs->stateop, 4); - //add $12,%esp - EmitByte(0x83); EmitByte(0xc4); EmitByte(0x0c); - break; -#if 1 -/* case OP_NOT_V: - //flds 0 - //flds glob[A+0] - //fcomip %st(1),%st - //jne _true - //flds glob[A+1] - //fcomip %st(1),%st - //jne _true - //flds glob[A+1] - //fcomip %st(1),%st - //jne _true - //mov 1,C - //jmp done - //_true: - //mov 0,C - //done: - break; -*/ - - case OP_NOT_V: - EmitByte(0xcd);EmitByte(op[i].op); - printf("QCJIT: instruction %i is not implemented\n", op[i].op); - break; -#endif - case OP_NE_V: - case OP_EQ_V: - { - void *f0, *f1, *f2, *floc; -//compare v[0] - //flds glob[A] - EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+0); - //flds glob[B] - EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].b+0); - //fcomip %st(1),%st - EmitByte(0xdf);EmitByte(0xe9); - //fstp %st(0) (aka: pop) - EmitByte(0xdd);EmitByte(0xd8); - - /*if the condition is true, don't fail*/ - j1 = LocalJmp(op[i].op); - { - STOREF(0.0f, glob + op[i].c); - f0 = LocalJmp(OP_GOTO); - } - l1 = LocalLoc(); - LocalJmpLoc(j1,l1); - -//compare v[1] - //flds glob[A] - EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+1); - //flds glob[B] - EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].b+1); - //fcomip %st(1),%st - EmitByte(0xdf);EmitByte(0xe9); - //fstp %st(0) (aka: pop) - EmitByte(0xdd);EmitByte(0xd8); - - /*if the condition is true, don't fail*/ - j1 = LocalJmp(op[i].op); - { - STOREF(0.0f, glob + op[i].c); - f1 = LocalJmp(OP_GOTO); - } - l1 = LocalLoc(); - LocalJmpLoc(j1,l1); - -//compare v[2] - //flds glob[A] - EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+2); - //flds glob[B] - EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].b+2); - //fcomip %st(1),%st - EmitByte(0xdf);EmitByte(0xe9); - //fstp %st(0) (aka: pop) - EmitByte(0xdd);EmitByte(0xd8); - - /*if the condition is true, don't fail*/ - j1 = LocalJmp(op[i].op); - { - STOREF(0.0f, glob + op[i].c); - f2 = LocalJmp(OP_GOTO); - } - l1 = LocalLoc(); - LocalJmpLoc(j1,l1); - -//success! - STOREF(1.0f, glob + op[i].c); - - floc = LocalLoc(); - LocalJmpLoc(f0,floc); - LocalJmpLoc(f1,floc); - LocalJmpLoc(f2,floc); - break; - } - - /*fteqcc generates these from reading 'fast arrays', and are part of hexenc extras*/ - case OP_FETCH_GBL_F: - case OP_FETCH_GBL_S: - case OP_FETCH_GBL_E: - case OP_FETCH_GBL_FNC: - case OP_FETCH_GBL_V: - { - unsigned int max = ((unsigned int*)glob)[op[i].a-1]; - unsigned int base = op[i].a; - //flds glob[B] - EmitByte(0xd9); EmitByte(0x05);EmitAdr(glob + op[i].b); - //fistp ta - EmitByte(0xdb); EmitByte(0x1d);EmitAdr(&ta); - LOADREG(&ta, REG_EAX) - //FIXME: if eax >= $max, abort - - if (op[i].op == OP_FETCH_GBL_V) - { - /*scale the index by 3*/ - SETREGI(3, REG_EDX) - //mul %edx - EmitByte(0xf7); EmitByte(0xe2); - } - - //lookup global - //mov &glob[base](,%eax,4),%edx - EmitByte(0x8b);EmitByte(0x14);EmitByte(0x85);Emit4Byte((unsigned int)(glob + base+0)); - STOREREG(REG_EDX, glob + op[i].c+0) - if (op[i].op == OP_FETCH_GBL_V) - { - //mov &glob[base+1](,%eax,4),%edx - EmitByte(0x8b);EmitByte(0x14);EmitByte(0x85);Emit4Byte((unsigned int)(glob + base+1)); - STOREREG(REG_EDX, glob + op[i].c+1) - //mov &glob[base+2](,%eax,4),%edx - EmitByte(0x8b);EmitByte(0x14);EmitByte(0x85);Emit4Byte((unsigned int)(glob + base+2)); - STOREREG(REG_EDX, glob + op[i].c+2) - } - break; - } - - /*fteqcc generates these from writing 'fast arrays'*/ - case OP_GLOBALADDRESS: - LOADREG(glob + op[i].b, REG_EAX); - //lea &glob[A](, %eax, 4),%eax - EmitByte(0x8d);EmitByte(0x04);EmitByte(0x85);EmitAdr(glob + op[i].b+2); - STOREREG(REG_EAX, glob + op[i].c); - break; -// case OP_BOUNDCHECK: - //FIXME: assert b <= a < c - break; - case OP_CONV_FTOI: - //flds glob[A] - EmitByte(0xd9); EmitByte(0x05);EmitAdr(glob + op[i].a); - //fistp glob[C] - EmitByte(0xdb); EmitByte(0x1d);EmitAdr(glob + op[i].c); - break; - case OP_MUL_I: - LOADREG(glob + op[i].a, REG_EAX); - //mull glob[C] (arg*eax => edx:eax) - EmitByte(0xfc); EmitByte(0x25);EmitAdr(glob + op[i].b); - STOREREG(REG_EAX, glob + op[i].c); - break; - - /*other extended opcodes*/ - case OP_BITOR_I: - LOADREG(glob + op[i].a, REG_EAX) - //or %eax,tb - EmitByte(0x0b); EmitByte(0x05);EmitAdr(glob + op[i].b); - STOREREG(REG_EAX, glob + op[i].c); - break; - - - default: - { - enum qcop_e e = op[i].op; - printf("QCJIT: Extended instruction set %i is not supported, not using jit.\n", e); - } - - - free(jit->statementjumps); //[MAX_STATEMENTS] - free(jit->statementoffsets); //[MAX_STATEMENTS] - free(jit->code); - free(jit); - return NULL; - } - } - - FixupJumps(jit); - - /* most likely want executable memory calls somewhere else more common */ -#ifdef _WIN32 - { - DWORD old; - - //this memory is on the heap. - //this means that we must maintain read/write protection, or libc will crash us - VirtualProtect(jit->code, jit->codesize, PAGE_EXECUTE_READWRITE, &old); - } -#else - mprotect(jit->code, jit->codesize, PROT_READ|PROT_EXEC); -#endif - -// externs->WriteFile("jit.x86", jit->code, jit->codesize); - - return jit; -} - -float foo(float arg) -{ - float f; - if (!arg) - f = 1; - else - f = 0; - return f; -} - -void PR_EnterJIT(progfuncs_t *progfuncs, struct jitstate *jit, int statement) -{ -#ifdef __GNUC__ - //call, it clobbers pretty much everything. - asm("call *%0" :: "r"(jit->statementoffsets[statement+1]),"b"(prinst->edicttable):"cc","memory","eax","ecx","edx"); -#elif defined(_MSC_VER) - void *entry = jit->statementoffsets[statement+1]; - void *edicttable = prinst->edicttable; - __asm { - pushad - mov eax,entry - mov ebx,edicttable - call eax - popad - } -#else - #error "Sorry, no idea how to enter assembler safely for your compiler" -#endif -} -#endif diff --git a/misc/source/fteqcc-src/progsint.h b/misc/source/fteqcc-src/progsint.h deleted file mode 100644 index 060d1e84..00000000 --- a/misc/source/fteqcc-src/progsint.h +++ /dev/null @@ -1,486 +0,0 @@ -#ifdef _WIN32 - #ifndef _CRT_SECURE_NO_WARNINGS - #define _CRT_SECURE_NO_WARNINGS - #endif - #define _CRT_NONSTDC_NO_WARNINGS - #ifndef AVAIL_ZLIB - #ifdef _MSC_VER - //#define AVAIL_ZLIB - #endif - #endif - - #include - - enum{false, true}; -#else - #include - #include - - #include - #include - #include - #include - - #ifndef __declspec - #define __declspec(mode) - #endif - - typedef enum{false, true} boolean; -//#define _inline inline -#endif -typedef unsigned char qbyte; -#include - -#define DLL_PROG -#ifndef PROGSUSED -#define PROGSUSED -#endif - -#include "progtype.h" -#include "progslib.h" - -#ifdef _MSC_VER -#pragma warning(disable : 4244) -#pragma warning(disable : 4267) -#endif - -//extern progfuncs_t *progfuncs; - -#define prinst progfuncs->inst -#define externs progfuncs->parms - -#include "pr_comp.h" - -#include "qcd.h" - -typedef struct -{ - int targetflags; //weather we need to mark the progs as a newer version - char *name; - char *opname; - int priority; - enum {ASSOC_LEFT, ASSOC_RIGHT, ASSOC_RIGHT_RESULT} associative; - struct QCC_type_s **type_a, **type_b, **type_c; -} QCC_opcode_t; -extern QCC_opcode_t pr_opcodes[]; // sized by initialization - - - - -#ifdef _MSC_VER -#define Q_vsnprintf _vsnprintf -#else -#define Q_vsnprintf vsnprintf -#endif - - -#define sv_num_edicts (*externs->sv_num_edicts) -#define sv_edicts (*externs->sv_edicts) - -#define printf externs->printf -#define Sys_Error externs->Sys_Error -#define Abort externs->Abort - -#define memalloc externs->memalloc -#define memfree externs->memfree - -int PRHunkMark(progfuncs_t *progfuncs); -void PRHunkFree(progfuncs_t *progfuncs, int mark); -void *PRHunkAlloc(progfuncs_t *progfuncs, int size); -void *PRAddressableExtend(progfuncs_t *progfuncs, int ammount); - -#ifdef printf -#undef LIKEPRINTF -#define LIKEPRINTF(x) -#endif - -//void *HunkAlloc (int size); -char *VARGS qcva (char *text, ...) LIKEPRINTF(1); -void QC_InitShares(progfuncs_t *progfuncs); -void QC_StartShares(progfuncs_t *progfuncs); -void QC_AddSharedVar(progfuncs_t *progfuncs, int num, int type); -void QC_AddSharedFieldVar(progfuncs_t *progfuncs, int num, char *stringtable); -int QC_RegisterFieldVar(progfuncs_t *progfuncs, unsigned int type, char *name, signed long requestedpos, signed long originalofs); -pbool Decompile(progfuncs_t *progfuncs, char *fname); -int PR_ToggleBreakpoint(progfuncs_t *progfuncs, char *filename, int linenum, int flag); -void StripExtension (char *path); - - -#define edvars(ed) (((edictrun_t*)ed)->fields) //pointer to the field vars, given an edict - - -void SetEndian(void); -extern short (*PRBigShort) (short l); -extern short (*PRLittleShort) (short l); -extern int (*PRBigLong) (int l); -extern int (*PRLittleLong) (int l); -extern float (*PRBigFloat) (float l); -extern float (*PRLittleFloat) (float l); - - - -/* -#ifndef COMPILER -typedef union eval_s -{ - string_t string; - float _float; - float vector[3]; - func_t function; - int _int; - int edict; - progsnum_t prog; //so it can easily be changed -} eval_t; -#endif -*/ - -typedef struct edictrun_s -{ - pbool isfree; - - float freetime; // realtime when the object was freed - unsigned int entnum; - pbool readonly; //causes error when QC tries writing to it. (quake's world entity) - void *fields; - -// other fields from progs come immediately after -} edictrun_t; - - -int Comp_Begin(progfuncs_t *progfuncs, int nump, char **parms); -int Comp_Continue(progfuncs_t *progfuncs); - -char *EvaluateDebugString(progfuncs_t *progfuncs, char *key); -char *SaveEnts(progfuncs_t *progfuncs, char *mem, int *size, int mode); -int LoadEnts(progfuncs_t *progfuncs, char *file, float killonspawnflags); -char *SaveEnt (progfuncs_t *progfuncs, char *buf, int *size, struct edict_s *ed); -struct edict_s *RestoreEnt (progfuncs_t *progfuncs, char *buf, int *size, struct edict_s *ed); -void PR_StackTrace (progfuncs_t *progfuncs); - -extern int noextensions; - -typedef enum -{ - PST_DEFAULT, //16 - PST_FTE32, //32 - PST_KKQWSV, //24 - PST_QTEST, -} progstructtype_t; - -#ifndef COMPILER -typedef struct progstate_s -{ - dprograms_t *progs; - dfunction_t *functions; - char *strings; - union { - ddefXX_t *globaldefs; - ddef16_t *globaldefs16; - ddef32_t *globaldefs32; - }; - union { - ddefXX_t *fielddefs; - ddef16_t *fielddefs16; - ddef32_t *fielddefs32; - }; - void *statements; -// void *global_struct; - float *globals; // same as pr_global_struct - int globals_size; // in bytes - - typeinfo_t *types; - - int edict_size; // in bytes - - char filename[128]; - - builtin_t *builtins; - int numbuiltins; - - int *linenums; //debug versions only - - progstructtype_t structtype; - -#ifdef QCJIT - struct jitstate *jit; -#endif -} progstate_t; - -typedef struct extensionbuiltin_s { - char *name; - builtin_t func; - struct extensionbuiltin_s *prev; -} extensionbuiltin_t; - -//============================================================================ - - -#define pr_progs current_progstate->progs -#define pr_functions current_progstate->functions -#define pr_strings current_progstate->strings -#define pr_globaldefs16 ((ddef16_t*)current_progstate->globaldefs) -#define pr_globaldefs32 ((ddef32_t*)current_progstate->globaldefs) -#define pr_fielddefs16 ((ddef16_t*)current_progstate->fielddefs) -#define pr_fielddefs32 ((ddef32_t*)current_progstate->fielddefs) -#define pr_statements16 ((dstatement16_t*)current_progstate->statements) -#define pr_statements32 ((dstatement32_t*)current_progstate->statements) -//#define pr_global_struct current_progstate->global_struct -#define pr_globals current_progstate->globals -#define pr_linenums current_progstate->linenums -#define pr_types current_progstate->types - - - -//============================================================================ - -void PR_Init (void); - -void PR_ExecuteProgram (progfuncs_t *progfuncs, func_t fnum); -int PR_LoadProgs(progfuncs_t *progfncs, char *s, int headercrc, builtin_t *builtins, int numbuiltins); -int PR_ReallyLoadProgs (progfuncs_t *progfuncs, char *filename, int headercrc, progstate_t *progstate, pbool complain); - -void *PRHunkAlloc(progfuncs_t *progfuncs, int ammount); - -void PR_Profile_f (void); - -struct edict_s *ED_Alloc (progfuncs_t *progfuncs); -void ED_Free (progfuncs_t *progfuncs, struct edict_s *ed); - -char *ED_NewString (progfuncs_t *progfuncs, char *string, int minlength); -// returns a copy of the string allocated from the server's string heap - -void ED_Print (progfuncs_t *progfuncs, struct edict_s *ed); -//void ED_Write (FILE *f, edictrun_t *ed); -char *ED_ParseEdict (progfuncs_t *progfuncs, char *data, edictrun_t *ent); - -//void ED_WriteGlobals (FILE *f); -void ED_ParseGlobals (char *data); - -//void ED_LoadFromFile (char *data); - -//define EDICT_NUM(n) ((edict_t *)(sv.edicts+ (n)*pr_edict_size)) -//define NUM_FOR_EDICT(e) (((byte *)(e) - sv.edicts)/pr_edict_size) - -struct edict_s *EDICT_NUM(progfuncs_t *progfuncs, unsigned int n); -unsigned int NUM_FOR_EDICT(progfuncs_t *progfuncs, struct edict_s *e); - -//#define NEXT_EDICT(e) ((edictrun_t *)( (byte *)e + pr_edict_size)) - -#define EDICT_TO_PROG(pf, e) (((edictrun_t*)e)->entnum) -#define PROG_TO_EDICT(pf, e) ((struct edictrun_s *)prinst->edicttable[e]) - -//============================================================================ - -#define G_FLOAT(o) (pr_globals[o]) -#define G_FLOAT2(o) (pr_globals[OFS_PARM0 + o*3]) -#define G_INT(o) (*(int *)&pr_globals[o]) -#define G_EDICT(o) ((edict_t *)((qbyte *)sv_edicts+ *(int *)&pr_globals[o])) -#define G_EDICTNUM(o) NUM_FOR_EDICT(G_EDICT(o)) -#define G_VECTOR(o) (&pr_globals[o]) -#define G_STRING(o) (*(string_t *)&pr_globals[o]) -#define G_STRING2(o) ((char*)*(string_t *)&pr_globals[o]) -#define GQ_STRING(o) (*(QCC_string_t *)&pr_globals[o]) -#define GQ_STRING2(o) ((char*)*(QCC_string_t *)&pr_globals[o]) -#define G_FUNCTION(o) (*(func_t *)&pr_globals[o]) -#define G_PROG(o) G_FLOAT(o) //simply so it's nice and easy to change... - -#define RETURN_EDICT(e) (((int *)pr_globals)[OFS_RETURN] = EDICT_TO_PROG(e)) - -#define E_FLOAT(e,o) (((float*)&e->v)[o]) -#define E_INT(e,o) (*(int *)&((float*)&e->v)[o]) -#define E_VECTOR(e,o) (&((float*)&e->v)[o]) -#define E_STRING(e,o) (*(string_t *)&((float*)(e+1))[o]) - -const extern unsigned int type_size[]; - - -extern unsigned short pr_crc; - -void VARGS PR_RunError (progfuncs_t *progfuncs, char *error, ...) LIKEPRINTF(2); - -void ED_PrintEdicts (progfuncs_t *progfuncs); -void ED_PrintNum (progfuncs_t *progfuncs, int ent); - - -pbool PR_SwitchProgs(progfuncs_t *progfuncs, progsnum_t type); -void PR_MoveParms(progfuncs_t *progfuncs, progsnum_t progs1, progsnum_t progs2); - - - - -eval_t *GetEdictFieldValue(progfuncs_t *progfuncs, struct edict_s *ed, char *name, evalc_t *cache); - -#endif - - - - -#ifndef COMPILER - -//this is windows - all files are written with this endian standard -//optimisation -//leave undefined if in doubt over os. -#ifdef _WIN32 -#define NOENDIAN -#endif - - -typedef struct { - int varofs; - int size; -} sharedvar_t; -typedef struct -{ - int s; - dfunction_t *f; - int progsnum; - int pushed; -} prstack_t; - - - -//pr_multi.c -void PR_SetBuiltins(int type); - -#define var(type, name) type name -#define vars(type, name, size) type name[size] - -typedef struct prinst_s { - char **tempstrings; - int maxtempstrings; - int numtempstrings; - int numtempstringsstack; - - char **allocedstrings; - int maxallocedstrings; - int numallocedstrings; - -var(progstate_t *, progstate); -#define pr_progstate prinst->progstate - -var(progsnum_t, pr_typecurrent); -#define pr_typecurrent prinst->pr_typecurrent -var(unsigned int, maxprogs); -#define maxprogs prinst->maxprogs - -var(progstate_t *,current_progstate); -#define current_progstate prinst->current_progstate - -var(unsigned int, numshares); -#define numshares prinst->numshares -var(sharedvar_t *,shares); //shared globals, not including parms -#define shares prinst->shares -var(unsigned int, maxshares); -#define maxshares prinst->maxshares - -var(struct prmemb_s *, memblocks); -#define memb prinst->memblocks - -var(unsigned int, maxfields); -#define maxfields prinst->maxfields -var(unsigned int, numfields); -#define numfields prinst->numfields -var(fdef_t*, field); //biggest size -#define field prinst->field - -int reorganisefields; - - -//pr_exec.c -#define MAX_STACK_DEPTH 64 -vars(prstack_t, pr_stack, MAX_STACK_DEPTH); -#define pr_stack prinst->pr_stack -var(int, pr_depth); -#define pr_depth prinst->pr_depth -var(int, spushed); -#define pr_spushed prinst->spushed - -#define LOCALSTACK_SIZE 4096 -vars(int, localstack, LOCALSTACK_SIZE); -#define localstack prinst->localstack -var(int, localstack_used); -#define localstack_used prinst->localstack_used - -var(int, continuestatement); -var(int, exitdepth); - -var(int, pr_trace); -#define pr_trace prinst->pr_trace -var(dfunction_t *, pr_xfunction); -#define pr_xfunction prinst->pr_xfunction -var(int, pr_xstatement); -#define pr_xstatement prinst->pr_xstatement - -var(int, pr_argc); -#define pr_argc prinst->pr_argc - -//pr_edict.c - -var(unsigned int, maxedicts); -#define maxedicts prinst->maxedicts - -var(evalc_t, spawnflagscache); -#define spawnflagscache prinst->spawnflagscache - - - - -var(unsigned int, fields_size); // in bytes -#define fields_size prinst->fields_size -var(unsigned int, max_fields_size); -#define max_fields_size prinst->max_fields_size - - -//initlib.c -int mfreelist; -var(char *, addressablehunk); -var(unsigned int, addressableused); -var(unsigned int, addressablesize); - - -//var(extensionbuiltin_t *, extensionbuiltin); -//#define extensionbuiltin prinst->extensionbuiltin - - struct edict_s **edicttable; -} prinst_t; -extern vec3_t vec3_origin; - -eval_t *PR_FindGlobal(progfuncs_t *prfuncs, char *globname, progsnum_t pnum, etype_t *type); -ddef16_t *ED_FindTypeGlobalFromProgs16 (progfuncs_t *progfuncs, char *name, progsnum_t prnum, int type); -ddef32_t *ED_FindTypeGlobalFromProgs32 (progfuncs_t *progfuncs, char *name, progsnum_t prnum, int type); -ddef16_t *ED_FindGlobalFromProgs16 (progfuncs_t *progfuncs, char *name, progsnum_t prnum); -ddef32_t *ED_FindGlobalFromProgs32 (progfuncs_t *progfuncs, char *name, progsnum_t prnum); -fdef_t *ED_FindField (progfuncs_t *progfuncs, char *name); -fdef_t *ED_FieldAtOfs (progfuncs_t *progfuncs, unsigned int ofs); -dfunction_t *ED_FindFunction (progfuncs_t *progfuncs, char *name, progsnum_t *pnum, progsnum_t fromprogs); -func_t PR_FindFunc(progfuncs_t *progfncs, char *funcname, progsnum_t pnum); -void PR_Configure (progfuncs_t *progfncs, int addressable_size, int max_progs); -int PR_InitEnts(progfuncs_t *progfncs, int maxents); -char *PR_ValueString (progfuncs_t *progfuncs, etype_t type, eval_t *val); -void QC_ClearEdict (progfuncs_t *progfuncs, struct edict_s *ed); -void PRAddressableFlush(progfuncs_t *progfuncs, int totalammount); -void QC_FlushProgsOffsets(progfuncs_t *progfuncs); - -ddef16_t *ED_GlobalAtOfs16 (progfuncs_t *progfuncs, int ofs); -ddef16_t *ED_FindGlobal16 (progfuncs_t *progfuncs, char *name); -ddef32_t *ED_FindGlobal32 (progfuncs_t *progfuncs, char *name); -ddef32_t *ED_GlobalAtOfs32 (progfuncs_t *progfuncs, unsigned int ofs); - -string_t PR_StringToProgs (progfuncs_t *inst, char *str); -char *ASMCALL PR_StringToNative (progfuncs_t *inst, string_t str); - -void PR_FreeTemps (progfuncs_t *progfuncs, int depth); - -char *PR_GlobalString (progfuncs_t *progfuncs, int ofs); -char *PR_GlobalStringNoContents (progfuncs_t *progfuncs, int ofs); - -pbool CompileFile(progfuncs_t *progfuncs, char *filename); - -struct jitstate; -struct jitstate *PR_GenerateJit(progfuncs_t *progfuncs); -void PR_EnterJIT(progfuncs_t *progfuncs, struct jitstate *jitstate, int statement); -void PR_CloseJit(struct jitstate *jit); - -char *QCC_COM_Parse (char *data); -extern char qcc_token[1024]; -#endif diff --git a/misc/source/fteqcc-src/progslib.h b/misc/source/fteqcc-src/progslib.h deleted file mode 100644 index 0bb9f1a9..00000000 --- a/misc/source/fteqcc-src/progslib.h +++ /dev/null @@ -1,323 +0,0 @@ - -#ifndef PROGSLIB_H -#define PROGSLIB_H -#ifdef _MSC_VER - #define VARGS __cdecl -#endif -#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) - #define LIKEPRINTF(x) __attribute__((format(printf,x,x+1))) -#endif -#ifndef LIKEPRINTF - #define LIKEPRINTF(x) -#endif -#ifndef VARGS - #define VARGS -#endif - -#if defined(_M_IX86) || defined(__i386__) -//#define QCJIT -#endif - -#ifdef QCJIT -#define ASMCALL VARGS -#else -#define ASMCALL -#endif -#define QCBUILTIN ASMCALL - - -struct edict_s; -struct entvars_s; -struct globalvars_s; -struct qcthread_s; -typedef struct progfuncs_s progfuncs_t; -typedef void (ASMCALL *builtin_t) (progfuncs_t *prinst, struct globalvars_s *gvars); - -//used by progs engine. All nulls is reset. -typedef struct { - char *varname; - struct fdef_s *ofs32; - - int spare[2]; -} evalc_t; -#define sizeofevalc sizeof(evalc_t) -typedef enum {ev_void, ev_string, ev_float, ev_vector, ev_entity, ev_field, ev_function, ev_pointer, ev_integer, ev_variant, ev_struct, ev_union} etype_t; - -//the number of pointers to variables (as opposed to functions - those are fine) in these structures is excessive. -//Many of the functions are also obsolete. -struct progfuncs_s { - int progsversion; //PROGSTRUCT_VERSION - - - void (*Configure) (progfuncs_t *prinst, int addressablesize, int max_progs); //configure buffers and memory. Used to reset and must be called first. Flushes a running VM. - progsnum_t (*LoadProgs) (progfuncs_t *prinst, char *s, int headercrc, builtin_t *builtins, int numbuiltins); //load a progs - int (*InitEnts) (progfuncs_t *prinst, int max_ents); //returns size of edicts for use with nextedict macro - void (*ExecuteProgram) (progfuncs_t *prinst, func_t fnum); //start execution - pbool (*SwitchProgs) (progfuncs_t *prinst, progsnum_t num); //switch to a different progs - this should be obsolete. - struct globalvars_s *(*globals) (progfuncs_t *prinst, progsnum_t num); //get the globals of a progs - struct entvars_s *(*entvars) (progfuncs_t *prinst, struct edict_s *ent); //return a pointer to the entvars of an ent. can be achieved via the edict_t structure instead, so obsolete. - - void (VARGS *RunError) (progfuncs_t *prinst, char *msg, ...) LIKEPRINTF(2); //builtins call this to say there was a problem - void (*PrintEdict) (progfuncs_t *prinst, struct edict_s *ed); //get a listing of all vars on an edict (sent back via 'print') - - struct edict_s *(*EntAlloc) (progfuncs_t *prinst); - void (*EntFree) (progfuncs_t *prinst, struct edict_s *ed); - - struct edict_s *(*EDICT_NUM) (progfuncs_t *prinst, unsigned int n); //get the nth edict - unsigned int (*NUM_FOR_EDICT) (progfuncs_t *prinst, struct edict_s *e); //so you can find out what that 'n' will be - - void (*SetGlobalEdict) (progfuncs_t *prinst, struct edict_s *ed, int ofs); //set a global to an edict (partially obsolete) - - char *(*VarString) (progfuncs_t *prinst, int first); //returns a string made up of multiple arguments - - struct progstate_s **progstate; //internal to the library. - - func_t (*FindFunction) (progfuncs_t *prinst, char *funcname, progsnum_t num); - - int (*StartCompile) (progfuncs_t *prinst, int argv, char **argc); //1 if can compile, 0 if failed to compile - int (*ContinueCompile) (progfuncs_t *prinst); //2 if finished, 1 if more to go, 0 if failed - - char *(*filefromprogs) (progfuncs_t *prinst, progsnum_t prnum, char *fname, int *size, char *buffer); //reveals encoded/added files from already loaded progs - char *(*filefromnewprogs) (progfuncs_t *prinst, char *prname, char *fname, int *size, char *buffer); //reveals encoded/added files from a progs on the disk somewhere - - char *(*save_ents) (progfuncs_t *prinst, char *buf, int *size, int mode); //dump the entire progs info into one big self allocated string - int (*load_ents) (progfuncs_t *prinst, char *s, float killonspawnflags); //restore the entire progs state (or just add some more ents) (returns edicts ize) - - char *(*saveent) (progfuncs_t *prinst, char *buf, int *size, struct edict_s *ed); //will save just one entities vars - struct edict_s *(*restoreent) (progfuncs_t *prinst, char *buf, int *size, struct edict_s *ed); //will restore the entity that had it's values saved (can use NULL for ed) - - union eval_s *(*FindGlobal) (progfuncs_t *prinst, char *name, progsnum_t num, etype_t *type); //find a pointer to the globals value - char *(*AddString) (progfuncs_t *prinst, char *val, int minlength); //dump a string into the progs memory (for setting globals and whatnot) - void *(*Tempmem) (progfuncs_t *prinst, int ammount, char *whatfor); //grab some mem for as long as the progs stays loaded - - union eval_s *(*GetEdictFieldValue) (progfuncs_t *prinst, struct edict_s *ent, char *name, evalc_t *s); //get an entityvar (cache it) and return the possible values - struct edict_s *(*ProgsToEdict) (progfuncs_t *prinst, int progs); //edicts are stored as ints and need to be adjusted - int (*EdictToProgs) (progfuncs_t *prinst, struct edict_s *ed); //edicts are stored as ints and need to be adjusted - - char *(*EvaluateDebugString) (progfuncs_t *prinst, char *key); //evaluate a string and return it's value (according to current progs) (expands edict vars) - - int *pr_trace; //start calling the editor for each line executed - - void (*StackTrace) (progfuncs_t *prinst); - - int (*ToggleBreak) (progfuncs_t *prinst, char *filename, int linenum, int mode); - - int numprogs; - - struct progexterns_s *parms; //these are the initial parms, they may be changed - - pbool (*Decompile) (progfuncs_t *prinst, char *fname); - - - struct prinst_s *inst; //internal variables. Leave alone. - - int *callargc; //number of args of built-in call - void (*RegisterBuiltin) (progfuncs_t *prinst, char *, builtin_t); - - char *stringtable; //qc strings are all relative. add to a qc string. this is required for support of frikqcc progs that strip string immediates. - int fieldadjust; //FrikQCC style arrays can cause problems due to field remapping. This causes us to leave gaps but offsets identical. - - struct qcthread_s *(*Fork) (progfuncs_t *prinst); //returns a pointer to a thread which can be resumed via RunThread. - void (*RunThread) (progfuncs_t *prinst, struct qcthread_s *thread); - void (*AbortStack) (progfuncs_t *prinst); //annigilates the current stack, positioning on a return statement. It is expected that this is only used via a builtin! - - int lastcalledbuiltinnumber; //useful with non-implemented opcodes. - - int (*RegisterFieldVar) (progfuncs_t *prinst, unsigned int type, char *name, signed long requestedpos, signed long originalofs); - - char *tempstringbase; //for engine's use. Store your base tempstring pointer here. - int tempstringnum; //for engine's use. - - string_t (*TempString) (progfuncs_t *prinst, char *str); - - string_t (*StringToProgs) (progfuncs_t *prinst, char *str); - char *(ASMCALL *StringToNative) (progfuncs_t *prinst, string_t str); - int stringtablesize; - - int (*QueryField) (progfuncs_t *prinst, unsigned int fieldoffset, etype_t *type, char **name, evalc_t *fieldcache); //find info on a field definition at an offset - - void (*EntClear) (progfuncs_t *progfuncs, struct edict_s *e); - void (*FindPrefixGlobals) (progfuncs_t *progfuncs, char *prefix, void (*found) (progfuncs_t *progfuncs, char *name, union eval_s *val, etype_t type) ); - - void *(*AddressableAlloc) (progfuncs_t *progfuncs, unsigned int ammount); /*returns memory within the qc block, use stringtoprogs to get a usable qc pointer/string*/ - - string_t (*AllocTempString) (progfuncs_t *prinst, char **str, unsigned int len); - void (*AddressableFree) (progfuncs_t *progfuncs, void *mem); /*frees a block of addressable memory*/ -}; - -typedef struct progexterns_s { - int progsversion; //PROGSTRUCT_VERSION - - unsigned char *(*ReadFile) (const char *fname, void *buffer, int len); - int (*FileSize) (const char *fname); //-1 if file does not exist - pbool (*WriteFile) (const char *name, void *data, int len); - int (VARGS *printf) (const char *, ...) LIKEPRINTF(1); - void (VARGS *Sys_Error) (const char *, ...) LIKEPRINTF(1); - void (VARGS *Abort) (char *, ...) LIKEPRINTF(1); - int edictsize; //size of edict_t - - void (*entspawn) (struct edict_s *ent, int loading); //ent has been spawned, but may not have all the extra variables (that may need to be set) set - pbool (*entcanfree) (struct edict_s *ent); //return true to stop ent from being freed - void (ASMCALL *stateop) (progfuncs_t *prinst, float var, func_t func); //what to do on qc's state opcode. - void (ASMCALL *cstateop) (progfuncs_t *prinst, float vara, float varb, func_t currentfunc); //a hexen2 opcode. - void (ASMCALL *cwstateop) (progfuncs_t *prinst, float vara, float varb, func_t currentfunc); //a hexen2 opcode. - void (ASMCALL *thinktimeop) (progfuncs_t *prinst, struct edict_s *ent, float varb); //a hexen2 opcode. - - - //used when loading a game - builtin_t *(*builtinsfor) (int num, int headercrc); //must return a pointer to the builtins that were used before the state was saved. - void (*loadcompleate) (int edictsize); //notification to reset any pointers. - pbool (*badfield)(progfuncs_t *prinst, struct edict_s *ent, const char *keyname, const char *value); //called for any fields that are not registered - - void *(VARGS *memalloc) (int size); //small string allocation malloced and freed randomly by the executor. (use malloc if you want) - void (VARGS *memfree) (void * mem); - - - builtin_t *globalbuiltins; //these are available to all progs - int numglobalbuiltins; - - enum {PR_NOCOMPILE, PR_COMPILENEXIST, PR_COMPILEEXISTANDCHANGED, PR_COMPILECHANGED, PR_COMPILEALWAYS, PR_COMPILEIGNORE} autocompile; - - double *gametime; //used to prevent the vm from reusing an entity faster than 2 secs. - - struct edict_s **sv_edicts; //pointer to the engine's reference to world. - unsigned int *sv_num_edicts; //pointer to the engine's edict count. - - int (*useeditor) (progfuncs_t *prinst, char *filename, int line, int nump, char **parms); //called on syntax errors or step-by-step debugging. - void (*addressablerelocated) (progfuncs_t *progfuncs, char *oldb, char *newb, int oldlen); //called when the progs memory was resized. you must fix up all pointers to globals, strings, fields, addressable blocks. - - void *user; /*contains the owner's world reference in FTE*/ -} progparms_t, progexterns_t; - -//FIXMEs -void QC_AddSharedVar(progfuncs_t *progfuncs, int start, int size); -void QC_AddSharedFieldVar(progfuncs_t *progfuncs, int num, char *relstringtable); -void ED_Print(progfuncs_t *progfuncs, struct edict_s *ed); -char *PR_RemoveProgsString(progfuncs_t *progfuncs, string_t str); -int PR_GetFuncArgCount(progfuncs_t *progfuncs, func_t func); - -#if defined(QCLIBDLL_EXPORTS) -__declspec(dllexport) -#endif -progfuncs_t * InitProgs(progparms_t *ext); -#if defined(QCLIBDLL_EXPORTS) -__declspec(dllexport) -#endif -void CloseProgs(progfuncs_t *inst); - -#ifndef COMPILER -typedef union eval_s -{ - string_t string; - float _float; - float _vector[3]; - func_t function; - int _int; - int edict; - float prog; //so it can easily be changed -} eval_t; -#endif - -#define PR_CURRENT -1 -#define PR_ANY -2 //not always valid. Use for finding funcs -#define PR_ANYBACK -3 -#define PROGSTRUCT_VERSION 2 - - -#ifndef DLL_PROG -#define PR_Configure(pf, memsize, max_progs) (*pf->Configure) (pf, memsize, max_progs) -#define PR_LoadProgs(pf, s, headercrc, builtins, numb) (*pf->LoadProgs) (pf, s, headercrc, builtins, numb) -#define PR_InitEnts(pf, maxents) (*pf->InitEnts) (pf, maxents) -#define PR_ExecuteProgram(pf, fnum) (*pf->ExecuteProgram) (pf, fnum) -#define PR_SwitchProgs(pf, num) (*pf->SwitchProgs) (pf, num) -#define PR_globals(pf, num) (*pf->globals) (pf, num) -#define PR_entvars(pf, ent) (*pf->entvars) (pf, ent) - -#define PR_RegisterFieldVar(pf,type,name,reqofs,qcofs) (*pf->RegisterFieldVar) (pf,type,name,reqofs,qcofs) - -#define ED_Alloc(pf) (*pf->EntAlloc) (pf) -#define ED_Free(pf, ed) (*pf->EntFree) (pf, ed) -#define ED_Clear(pf, ed) (*pf->EntClear) (pf, ed) - -#define PR_LoadEnts(pf, s, kf) (*pf->load_ents) (pf, s, kf) -#define PR_SaveEnts(pf, buf, size, mode) (*pf->save_ents) (pf, buf, size, mode) - -#define EDICT_NUM(pf, num) (*pf->EDICT_NUM) (pf, num) -#define NUM_FOR_EDICT(pf, e) (*pf->NUM_FOR_EDICT) (pf, e) -#define SetGlobalEdict(pf, ed, ofs) (*pf->SetGlobalEdict) (pf, ed, ofs) -#define PR_VarString(pf,first) (*pf->VarString) (pf,first) - -#define PR_StartCompile(pf,argc,argv) (*pf->StartCompile) (pf,argc,argv) -#define PR_ContinueCompile(pf) (*pf->ContinueCompile) (pf) - -#define PR_StackTrace(pf) (*pf->StackTrace) (pf) -#define PR_AbortStack(pf) (*pf->AbortStack) (pf) - -#define PR_RunError(pf,str) (*pf->RunError) (pf,str) - -#define PR_PrintEdict(pf,ed) (*pf->PrintEdict) (pf, ed) - -#define PR_FindFunction(pf, name, num) (*pf->FindFunction) (pf, name, num) -#define PR_FindGlobal(pf, name, progs, type) (*pf->FindGlobal) (pf, name, progs, type) -#define PR_AddString(pf, ed, len) (*pf->AddString) (pf, ed, len) -#define PR_Alloc(pf,size,whatfor) (*pf->Tempmem) (pf, size, whatfor) -#define PR_AddressableAlloc(pf,size) (*pf->AddressableAlloc) (pf, size) - -#define PROG_TO_EDICT(pf, ed) (*pf->ProgsToEdict) (pf, ed) -#define EDICT_TO_PROG(pf, ed) (*pf->EdictToProgs) (pf, (struct edict_s*)ed) - -#define PR_RegisterBuiltin(pf, name, func) (*pf->RegisterBuiltin) (pf, name, func) - -#define PR_GetString(pf,s) (*pf->StringToNative) (pf, s) -#define PR_GetStringOfs(pf,o) (*pf->StringToNative) (pf, G_INT(o)) -#define PR_SetString(pf, s) (*pf->StringToProgs) (pf, s) - -#define NEXT_EDICT(pf,o) EDICT_NUM(pf, NUM_FOR_EDICT(pf, o)+1) -#define RETURN_EDICT(pf, e) (((int *)pr_globals)[OFS_RETURN] = EDICT_TO_PROG(pf, e)) - - -//builtin funcs (which operate on globals) -//To use these outside of builtins, you will likly have to use the 'globals' method. -#define G_FLOAT(o) (((float *)pr_globals)[o]) -#define G_FLOAT2(o) (((float *)pr_globals)[OFS_PARM0 + o*3]) -#define G_INT(o) (((int *)pr_globals)[o]) -#define G_EDICT(pf, o) PROG_TO_EDICT(pf, G_INT(o)) //((edict_t *)((char *) sv.edicts+ *(int *)&((float *)pr_globals)[o])) -#define G_EDICTNUM(pf, o) NUM_FOR_EDICT(pf, G_EDICT(pf, o)) -#define G_VECTOR(o) (&((float *)pr_globals)[o]) -#define G_FUNCTION(o) (*(func_t *)&((float *)pr_globals)[o]) - -/* -#define PR_GetString(p,s) (s?s + p->stringtable:"") -#define PR_GetStringOfs(p,o) (G_INT(o)?G_INT(o) + p->stringtable:"") -#define PR_SetStringOfs(p,o,s) (G_INT(o) = s - p->stringtable) -*/ -//#define PR_SetString(p, s) ((s&&*s)?(s - p->stringtable):0) -#define PR_NewString(p, s, l) PR_SetString(p, PR_AddString(p, s, l)) -/**/ - -#define ev_prog ev_integer - -#define E_STRING(o) (char *)(((int *)((char *)ed) + progparms.edictsize)[o]) - -//#define pr_global_struct pr_globals - -#endif - - -#define OFS_NULL 0 -#define OFS_RETURN 1 -#define OFS_PARM0 4 // leave 3 ofs for each parm to hold vectors -#define OFS_PARM1 7 -#define OFS_PARM2 10 -#define OFS_PARM3 13 -#define OFS_PARM4 16 -#define OFS_PARM5 19 -#define OFS_PARM6 22 -#define OFS_PARM7 25 -#define RESERVED_OFS 28 - - -#undef edict_t -#undef globalvars_t - -#endif //PROGSLIB_H diff --git a/misc/source/fteqcc-src/progtype.h b/misc/source/fteqcc-src/progtype.h deleted file mode 100644 index ab120154..00000000 --- a/misc/source/fteqcc-src/progtype.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef QCLIB_PROGTYPE_H -#define QCLIB_PROGTYPE_H - -#ifndef DLL_PROG - -#else -typedef float vec_t; -typedef vec_t vec3_t[3]; -#endif - -#ifndef t_bool -#define t_bool -typedef int pbool; - -#else -#define t_bool -#endif -typedef int progsnum_t; -typedef int func_t; -typedef int string_t; - -#endif /* QCLIB_PROGTYPE_H */ - diff --git a/misc/source/fteqcc-src/qcc.h b/misc/source/fteqcc-src/qcc.h deleted file mode 100644 index 1ee2ac62..00000000 --- a/misc/source/fteqcc-src/qcc.h +++ /dev/null @@ -1,933 +0,0 @@ -#define COMPILER -#define PROGSUSED - -//#define COMMONINLINES -//#define inline _inline - -#include "cmdlib.h" -#include -/* -#include -#include - - -#include "pr_comp.h" -*/ - -//this is for testing -#define WRITEASM - -#ifdef __MINGW32_VERSION -#define MINGW -#endif - -#define progfuncs qccprogfuncs -extern progfuncs_t *qccprogfuncs; - -#ifndef _WIN32 -#define stricmp strcasecmp -#define strnicmp strncasecmp -#endif - -void *qccHunkAlloc(size_t mem); -void qccClearHunk(void); - -extern short (*PRBigShort) (short l); -extern short (*PRLittleShort) (short l); -extern int (*PRBigLong) (int l); -extern int (*PRLittleLong) (int l); -extern float (*PRBigFloat) (float l); -extern float (*PRLittleFloat) (float l); - - -#define MAX_ERRORS 10 - -#define MAX_NAME 256 // chars long - -extern unsigned int MAX_REGS; - -extern int MAX_STRINGS; -extern int MAX_GLOBALS; -extern int MAX_FIELDS; -extern int MAX_STATEMENTS; -extern int MAX_FUNCTIONS; - -#define MAX_SOUNDS 1024 //convert to int? -#define MAX_TEXTURES 1024 //convert to int? -#define MAX_MODELS 1024 //convert to int? -#define MAX_FILES 1024 //convert to int? -#define MAX_DATA_PATH 64 - -extern int MAX_CONSTANTS; -#define MAXCONSTANTNAMELENGTH 64 -#define MAXCONSTANTPARAMLENGTH 32 -#define MAXCONSTANTPARAMS 32 - -typedef enum {QCF_STANDARD, QCF_HEXEN2, QCF_DARKPLACES, QCF_FTE, QCF_FTEDEBUG, QCF_KK7, QCF_QTEST} qcc_targetformat_t; -extern qcc_targetformat_t qcc_targetformat; - - -/* - -TODO: - -"stopped at 10 errors" - -other pointer types for models and clients? - -compact string heap? - -always initialize all variables to something safe - -the def->type->type arrangement is really silly. - -return type checking - -parm count type checking - -immediate overflow checking - -pass the first two parms in call->b and call->c - -*/ - -/* - -comments --------- -// comments discard text until the end of line -/ * * / comments discard all enclosed text (spaced out on this line because this documentation is in a regular C comment block, and typing them in normally causes a parse error) - -code structure --------------- -A definition is: - [ = ] {, [ = ] }; - - -types ------ -simple types: void, float, vector, string, or entity - float width, height; - string name; - entity self, other; - -vector types: - vector org; // also creates org_x, org_y, and org_z float defs - - -A function type is specified as: simpletype ( type name {,type name} ) -The names are ignored except when the function is initialized. - void() think; - entity() FindTarget; - void(vector destination, float speed, void() callback) SUB_CalcMove; - void(...) dprint; // variable argument builtin - -A field type is specified as: .type - .vector origin; - .string netname; - .void() think, touch, use; - - -names ------ -Names are a maximum of 64 characters, must begin with A-Z,a-z, or _, and can continue with those characters or 0-9. - -There are two levels of scoping: global, and function. The parameter list of a function and any vars declared inside a function with the "local" statement are only visible within that function, - - -immediates ----------- -Float immediates must begin with 0-9 or minus sign. .5 is illegal. - -A parsing ambiguity is present with negative constants. "a-5" will be parsed as "a", then "-5", causing an error. Seperate the - from the digits with a space "a - 5" to get the proper behavior. - 12 - 1.6 - 0.5 - -100 - -Vector immediates are three float immediates enclosed in single quotes. - '0 0 0' - '20.5 -10 0.00001' - -String immediates are characters enclosed in double quotes. The string cannot contain explicit newlines, but the escape character \n can embed one. The \" escape can be used to include a quote in the string. - "maps/jrwiz1.bsp" - "sound/nin/pain.wav" - "ouch!\n" - -Code immediates are statements enclosed in {} braces. -statement: - { } - ; - local [ = ] {, [ = ] }; - return ; - if ( ) [ else ]; - while ( ) ; - do while ( ); - ( ); - -expression: - combiations of names and these operators with standard C precedence: - "&&", "||", "<=", ">=","==", "!=", "!", "*", "/", "-", "+", "=", ".", "<", ">", "&", "|" - Parenthesis can be used to alter order of operation. - The & and | operations perform integral bit ops on floats - -A built in function immediate is a number sign followed by an integer. - #1 - #12 - - -compilation ------------ -Source files are processed sequentially without dumping any state, so if a defs file is the first one processed, the definitions will be available to all other files. - -The language is strongly typed and there are no casts. - -Anything that is initialized is assumed to be constant, and will have immediates folded into it. If you change the value, your program will malfunction. All uninitialized globals will be saved to savegame files. - -Functions cannot have more than eight parameters. - -Error recovery during compilation is minimal. It will skip to the next global definition, so you will never see more than one error at a time in a given function. All compilation aborts after ten error messages. - -Names can be defined multiple times until they are defined with an initialization, allowing functions to be prototyped before their definition. - -void() MyFunction; // the prototype - -void() MyFunction = // the initialization -{ - dprint ("we're here\n"); -}; - - -entities and fields -------------------- - - -execution ---------- -Code execution is initiated by C code in quake from two main places: the timed think routines for periodic control, and the touch function when two objects impact each other. - -There are three global variables that are set before beginning code execution: - entity world; // the server's world object, which holds all global - // state for the server, like the deathmatch flags - // and the body ques. - entity self; // the entity the function is executing for - entity other; // the other object in an impact, not used for thinks - float time; // the current game time. Note that because the - // entities in the world are simulated sequentially, - // time is NOT strictly increasing. An impact late - // in one entity's time slice may set time higher - // than the think function of the next entity. - // The difference is limited to 0.1 seconds. -Execution is also caused by a few uncommon events, like the addition of a new client to an existing server. - -There is a runnaway counter that stops a program if 100000 statements are executed, assuming it is in an infinite loop. - -It is acceptable to change the system set global variables. This is usually done to pose as another entity by changing self and calling a function. - -The interpretation is fairly efficient, but it is still over an order of magnitude slower than compiled C code. All time consuming operations should be made into built in functions. - -A profile counter is kept for each function, and incremented for each interpreted instruction inside that function. The "profile" console command in Quake will dump out the top 10 functions, then clear all the counters. The "profile all" command will dump sorted stats for every function that has been executed. - - -afunc ( 4, bfunc(1,2,3)); -will fail because there is a shared parameter marshaling area, which will cause the 1 from bfunc to overwrite the 4 already placed in parm0. When a function is called, it copies the parms from the globals into it's privately scoped variables, so there is no collision when calling another function. - -total = factorial(3) + factorial(4); -Will fail because the return value from functions is held in a single global area. If this really gets on your nerves, tell me and I can work around it at a slight performance and space penalty by allocating a new register for the function call and copying it out. - - -built in functions ------------------- -void(string text) dprint; -Prints the string to the server console. - -void(entity client, string text) cprint; -Prints a message to a specific client. - -void(string text) bprint; -Broadcast prints a message to all clients on the current server. - -entity() spawn; -Returns a totally empty entity. You can manually set everything up, or just set the origin and call one of the existing entity setup functions. - -entity(entity start, .string field, string match) find; -Searches the server entity list beginning at start, looking for an entity that has entity.field = match. To start at the beginning of the list, pass world. World is returned when the end of the list is reached. - - - - -gotchas -------- - -The && and || operators DO NOT EARLY OUT like C! - -Don't confuse single quoted vectors with double quoted strings - -The function declaration syntax takes a little getting used to. - -Don't forget the ; after the trailing brace of a function initialization. - -Don't forget the "local" before defining local variables. - -There are no ++ / -- operators, or operate/assign operators. - -*/ - - -#if 1 -#include "hash.h" -extern hashtable_t compconstantstable; -extern hashtable_t globalstable, localstable; -#endif - -#ifdef WRITEASM -extern FILE *asmfile; -#endif -//============================================================================= - -// offsets are always multiplied by 4 before using -typedef unsigned int gofs_t; // offset in global data block -typedef struct QCC_function_s QCC_function_t; - -#define MAX_PARMS 8 - -typedef struct QCC_type_s -{ - etype_t type; - - struct QCC_type_s *parentclass; //type_entity... - struct QCC_type_s *next; -// function types are more complex - struct QCC_type_s *aux_type; // return type or field type - struct QCC_type_s *param; - int num_parms; // -1 = variable args -// struct QCC_type_s *parm_types[MAX_PARMS]; // only [num_parms] allocated - - unsigned int ofs; //inside a structure. - unsigned int size; - unsigned int arraysize; - pbool typedefed; - char *name; -} QCC_type_t; -int typecmp(QCC_type_t *a, QCC_type_t *b); - -typedef struct temp_s { - gofs_t ofs; - struct QCC_def_s *scope; -#ifdef WRITEASM - struct QCC_def_s *lastfunc; -#endif - struct temp_s *next; - pbool used; - unsigned int size; -} temp_t; -void QCC_PurgeTemps(void); - -//not written -typedef struct QCC_def_s -{ - QCC_type_t *type; - char *name; - struct QCC_def_s *next; - struct QCC_def_s *nextlocal; //provides a chain of local variables for the opt_locals_marshalling optimisation. - gofs_t ofs; - struct QCC_def_s *scope; // function the var was defined in, or NULL - struct QCC_def_s *deftail; // arrays and structs create multiple globaldef objects providing different types at the different parts of the single object (struct), or alternative names (vectors). this allows us to correctly set the const type based upon how its initialised. - int initialized; // 1 when a declaration included "= immediate" - int constant; // 1 says we can use the value over and over again - - int references; - int timescalled; //part of the opt_stripfunctions optimisation. - - int s_file; - int s_line; - - int arraysize; - pbool shared:1; - pbool saved:1; - pbool isstatic:1; - pbool subscoped_away:1; - pbool followptr:1; - - temp_t *temp; -} QCC_def_t; - -//============================================================================ - -// pr_loc.h -- program local defs - - -//============================================================================= -extern char QCC_copyright[1024]; -extern char QCC_Packname[5][128]; -extern int QCC_packid; - -typedef union QCC_eval_s -{ - QCC_string_t string; - float _float; - float vector[3]; - func_t function; - int _int; - union QCC_eval_s *ptr; -} QCC_eval_t; - -const extern unsigned int type_size[]; -//extern QCC_def_t *def_for_type[9]; - -extern QCC_type_t *type_void, *type_string, *type_float, *type_vector, *type_entity, *type_field, *type_function, *type_pointer, *type_floatpointer, *type_intpointer, *type_integer, *type_variant, *type_floatfield; - -struct QCC_function_s -{ - int builtin; // if non 0, call an internal function - int code; // first statement - char *file; // source file with definition - int file_line; - struct QCC_def_s *def; - unsigned int parm_ofs[MAX_PARMS]; // always contiguous, right? -}; - - -// -// output generated by prog parsing -// -typedef struct -{ - char *memory; - int max_memory; - int current_memory; - QCC_type_t *types; - - QCC_def_t def_head; // unused head of linked list - QCC_def_t *def_tail; // add new defs after this and move it - QCC_def_t *localvars; // chain of variables which need to be pushed and stuff. - - int size_fields; -} QCC_pr_info_t; - -extern QCC_pr_info_t pr; - - -typedef struct -{ - char name[MAXCONSTANTNAMELENGTH]; - char *value; - char params[MAXCONSTANTPARAMS][MAXCONSTANTPARAMLENGTH]; - int numparams; - pbool used; - pbool inside; - - int namelen; -} CompilerConstant_t; -extern CompilerConstant_t *CompilerConstant; - -//============================================================================ - -extern pbool pr_dumpasm; - -//extern QCC_def_t **pr_global_defs; // to find def for a global variable - -typedef enum { -tt_eof, // end of file reached -tt_name, // an alphanumeric name token -tt_punct, // code punctuation -tt_immediate, // string, float, vector -} token_type_t; - -extern char pr_token[8192]; -extern token_type_t pr_token_type; -extern QCC_type_t *pr_immediate_type; -extern QCC_eval_t pr_immediate; - -extern pbool keyword_asm; -extern pbool keyword_break; -extern pbool keyword_case; -extern pbool keyword_class; -extern pbool keyword_const; -extern pbool keyword_optional; -extern pbool keyword_continue; -extern pbool keyword_default; -extern pbool keyword_do; -extern pbool keyword_entity; -extern pbool keyword_float; -extern pbool keyword_for; -extern pbool keyword_goto; -extern pbool keyword_int; -extern pbool keyword_integer; -extern pbool keyword_state; -extern pbool keyword_string; -extern pbool keyword_struct; -extern pbool keyword_switch; -extern pbool keyword_thinktime; -extern pbool keyword_var; -extern pbool keyword_vector; -extern pbool keyword_union; -extern pbool keyword_enum; //kinda like in c, but typedef not supported. -extern pbool keyword_enumflags; //like enum, but doubles instead of adds 1. -extern pbool keyword_typedef; //fixme -extern pbool keyword_extern; //function is external, don't error or warn if the body was not found -extern pbool keyword_shared; //mark global to be copied over when progs changes (part of FTE_MULTIPROGS) -extern pbool keyword_noref; //nowhere else references this, don't strip it. -extern pbool keyword_nosave; //don't write the def to the output. -extern pbool keyword_union; //you surly know what a union is! - - -extern pbool keywords_coexist; -extern pbool output_parms; -extern pbool autoprototype; -extern pbool pr_subscopedlocals; -extern pbool flag_ifstring; -extern pbool flag_iffloat; -extern pbool flag_acc; -extern pbool flag_caseinsensative; -extern pbool flag_laxcasts; -extern pbool flag_hashonly; -extern pbool flag_fasttrackarrays; -extern pbool flag_assume_integer; -extern pbool flag_msvcstyle; -extern pbool flag_filetimes; -extern pbool flag_typeexplicit; - -extern pbool opt_overlaptemps; -extern pbool opt_shortenifnots; -extern pbool opt_noduplicatestrings; -extern pbool opt_constantarithmatic; -extern pbool opt_nonvec_parms; -extern pbool opt_constant_names; -extern pbool opt_precache_file; -extern pbool opt_filenames; -extern pbool opt_assignments; -extern pbool opt_unreferenced; -extern pbool opt_function_names; -extern pbool opt_locals; -extern pbool opt_dupconstdefs; -extern pbool opt_constant_names_strings; -extern pbool opt_return_only; -extern pbool opt_compound_jumps; -//extern pbool opt_comexprremoval; -extern pbool opt_stripfunctions; -extern pbool opt_locals_marshalling; -extern pbool opt_logicops; -extern pbool opt_vectorcalls; - -extern int optres_shortenifnots; -extern int optres_overlaptemps; -extern int optres_noduplicatestrings; -extern int optres_constantarithmatic; -extern int optres_nonvec_parms; -extern int optres_constant_names; -extern int optres_precache_file; -extern int optres_filenames; -extern int optres_assignments; -extern int optres_unreferenced; -extern int optres_function_names; -extern int optres_locals; -extern int optres_dupconstdefs; -extern int optres_constant_names_strings; -extern int optres_return_only; -extern int optres_compound_jumps; -//extern int optres_comexprremoval; -extern int optres_stripfunctions; -extern int optres_locals_marshalling; -extern int optres_logicops; - -pbool CompileParams(progfuncs_t *progfuncs, int doall, int nump, char **parms); - -void QCC_PR_PrintStatement (QCC_dstatement_t *s); - -void QCC_PR_Lex (void); -// reads the next token into pr_token and classifies its type - -QCC_type_t *QCC_PR_NewType (char *name, int basictype, pbool typedefed); -QCC_type_t *QCC_PointerTypeTo(QCC_type_t *type); -QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail); -extern pbool type_inlinefunction; -QCC_type_t *QCC_TypeForName(char *name); -QCC_type_t *QCC_PR_ParseFunctionType (int newtype, QCC_type_t *returntype); -QCC_type_t *QCC_PR_ParseFunctionTypeReacc (int newtype, QCC_type_t *returntype); -char *QCC_PR_ParseName (void); -CompilerConstant_t *QCC_PR_DefineName(char *name); - -void QCC_RemapOffsets(unsigned int firststatement, unsigned int laststatement, unsigned int min, unsigned int max, unsigned int newmin); - -int QCC_PR_IntConstExpr(void); - -#ifndef COMMONINLINES -pbool QCC_PR_CheckImmediate (char *string); -pbool QCC_PR_CheckToken (char *string); -pbool QCC_PR_CheckName (char *string); -void QCC_PR_Expect (char *string); -pbool QCC_PR_CheckKeyword(int keywordenabled, char *string); -#endif -void VARGS QCC_PR_ParseError (int errortype, char *error, ...); -void VARGS QCC_PR_ParseWarning (int warningtype, char *error, ...); -pbool VARGS QCC_PR_Warning (int type, char *file, int line, char *error, ...); -void VARGS QCC_PR_Note (int type, char *file, int line, char *error, ...); -void QCC_PR_ParsePrintDef (int warningtype, QCC_def_t *def); -void VARGS QCC_PR_ParseErrorPrintDef (int errortype, QCC_def_t *def, char *error, ...); - -int QCC_WarningForName(char *name); - -//QccMain.c must be changed if this is changed. -enum { - WARN_DEBUGGING, - WARN_ERROR, - WARN_NOTREFERENCED, - WARN_NOTREFERENCEDCONST, - WARN_CONFLICTINGRETURNS, - WARN_TOOFEWPARAMS, - WARN_TOOMANYPARAMS, - WARN_UNEXPECTEDPUNCT, - WARN_ASSIGNMENTTOCONSTANT, - WARN_ASSIGNMENTTOCONSTANTFUNC, - WARN_MISSINGRETURNVALUE, - WARN_WRONGRETURNTYPE, - WARN_CORRECTEDRETURNTYPE, - WARN_POINTLESSSTATEMENT, - WARN_MISSINGRETURN, - WARN_DUPLICATEDEFINITION, - WARN_UNDEFNOTDEFINED, - WARN_PRECOMPILERMESSAGE, - WARN_TOOMANYPARAMETERSFORFUNC, - WARN_STRINGTOOLONG, - WARN_BADTARGET, - WARN_BADPRAGMA, - WARN_HANGINGSLASHR, - WARN_NOTDEFINED, - WARN_NOTCONSTANT, - WARN_SWITCHTYPEMISMATCH, - WARN_CONFLICTINGUNIONMEMBER, - WARN_KEYWORDDISABLED, - WARN_ENUMFLAGS_NOTINTEGER, - WARN_ENUMFLAGS_NOTBINARY, - WARN_CASEINSENSATIVEFRAMEMACRO, - WARN_DUPLICATELABEL, - WARN_DUPLICATEMACRO, - WARN_ASSIGNMENTINCONDITIONAL, - WARN_MACROINSTRING, - WARN_BADPARAMS, - WARN_IMPLICITCONVERSION, - WARN_FIXEDRETURNVALUECONFLICT, - WARN_EXTRAPRECACHE, - WARN_NOTPRECACHED, - WARN_DEADCODE, - WARN_UNREACHABLECODE, - WARN_NOTSTANDARDBEHAVIOUR, - WARN_INEFFICIENTPLUSPLUS, - WARN_DUPLICATEPRECOMPILER, - WARN_IDENTICALPRECOMPILER, - WARN_FTE_SPECIFIC, //extension that only FTEQCC will have a clue about. - WARN_EXTENSION_USED, //extension that frikqcc also understands - WARN_IFSTRING_USED, - WARN_LAXCAST, //some errors become this with a compiler flag - WARN_UNDESIRABLECONVENTION, - WARN_SAMENAMEASGLOBAL, - WARN_CONSTANTCOMPARISON, - WARN_UNSAFEFUNCTIONRETURNTYPE, - WARN_MISSINGOPTIONAL, - - ERR_PARSEERRORS, //caused by qcc_pr_parseerror being called. - - //these are definatly my fault... - ERR_INTERNAL, - ERR_TOOCOMPLEX, - ERR_BADOPCODE, - ERR_TOOMANYSTATEMENTS, - ERR_TOOMANYSTRINGS, - ERR_BADTARGETSWITCH, - ERR_TOOMANYTYPES, - ERR_TOOMANYPAKFILES, - ERR_PRECOMPILERCONSTANTTOOLONG, - ERR_MACROTOOMANYPARMS, - ERR_TOOMANYFRAMEMACROS, - - //limitations, some are imposed by compiler, some arn't. - ERR_TOOMANYGLOBALS, - ERR_TOOMANYGOTOS, - ERR_TOOMANYBREAKS, - ERR_TOOMANYCONTINUES, - ERR_TOOMANYCASES, - ERR_TOOMANYLABELS, - ERR_TOOMANYOPENFILES, - ERR_TOOMANYPARAMETERSVARARGS, - ERR_TOOMANYTOTALPARAMETERS, - - //these are probably yours, or qcc being fussy. - ERR_BADEXTENSION, - ERR_BADIMMEDIATETYPE, - ERR_NOOUTPUT, - ERR_NOTAFUNCTION, - ERR_FUNCTIONWITHVARGS, - ERR_BADHEX, - ERR_UNKNOWNPUCTUATION, - ERR_EXPECTED, - ERR_NOTANAME, - ERR_NAMETOOLONG, - ERR_NOFUNC, - ERR_COULDNTOPENFILE, - ERR_NOTFUNCTIONTYPE, - ERR_TOOFEWPARAMS, - ERR_TOOMANYPARAMS, - ERR_CONSTANTNOTDEFINED, - ERR_BADFRAMEMACRO, - ERR_TYPEMISMATCH, - ERR_TYPEMISMATCHREDEC, - ERR_TYPEMISMATCHPARM, - ERR_TYPEMISMATCHARRAYSIZE, - ERR_UNEXPECTEDPUNCTUATION, - ERR_NOTACONSTANT, - ERR_REDECLARATION, - ERR_INITIALISEDLOCALFUNCTION, - ERR_NOTDEFINED, - ERR_ARRAYNEEDSSIZE, - ERR_ARRAYNEEDSBRACES, - ERR_TOOMANYINITIALISERS, - ERR_TYPEINVALIDINSTRUCT, - ERR_NOSHAREDLOCALS, - ERR_TYPEWITHNONAME, - ERR_BADARRAYSIZE, - ERR_NONAME, - ERR_SHAREDINITIALISED, - ERR_UNKNOWNVALUE, - ERR_BADARRAYINDEXTYPE, - ERR_NOVALIDOPCODES, - ERR_MEMBERNOTVALID, - ERR_BADPLUSPLUSOPERATOR, - ERR_BADNOTTYPE, - ERR_BADTYPECAST, - ERR_MULTIPLEDEFAULTS, - ERR_CASENOTIMMEDIATE, - ERR_BADSWITCHTYPE, - ERR_BADLABELNAME, - ERR_NOLABEL, - ERR_THINKTIMETYPEMISMATCH, - ERR_STATETYPEMISMATCH, - ERR_BADBUILTINIMMEDIATE, - ERR_PARAMWITHNONAME, - ERR_BADPARAMORDER, - ERR_ILLEGALCONTINUES, - ERR_ILLEGALBREAKS, - ERR_ILLEGALCASES, - ERR_NOTANUMBER, - ERR_WRONGSUBTYPE, - ERR_EOF, - ERR_NOPRECOMPILERIF, - ERR_NOENDIF, - ERR_HASHERROR, - ERR_NOTATYPE, - ERR_TOOMANYPACKFILES, - ERR_INVALIDVECTORIMMEDIATE, - ERR_INVALIDSTRINGIMMEDIATE, - ERR_BADCHARACTERCODE, - ERR_BADPARMS, - ERR_WERROR, - - WARN_MAX -}; - -#define FLAG_KILLSDEBUGGERS 1 -#define FLAG_ASDEFAULT 2 -#define FLAG_SETINGUI 4 -#define FLAG_HIDDENINGUI 8 -#define FLAG_MIDCOMPILE 16 //option can be changed mid-compile with the special pragma -typedef struct { - pbool *enabled; - char *abbrev; - int optimisationlevel; - int flags; //1: kills debuggers. 2: applied as default. - char *fullname; - char *description; - void *guiinfo; -} optimisations_t; -extern optimisations_t optimisations[]; - -typedef struct { - pbool *enabled; - int flags; //2 applied as default - char *abbrev; - char *fullname; - char *description; - void *guiinfo; -} compiler_flag_t; -extern compiler_flag_t compiler_flag[]; - -extern pbool qccwarningdisabled[WARN_MAX]; - -extern jmp_buf pr_parse_abort; // longjump with this on parse error -extern int pr_source_line; -extern char *pr_file_p; - -void *QCC_PR_Malloc (int size); - - -#define OFS_NULL 0 -#define OFS_RETURN 1 -#define OFS_PARM0 4 // leave 3 ofs for each parm to hold vectors -#define OFS_PARM1 7 -#define OFS_PARM2 10 -#define OFS_PARM3 13 -#define OFS_PARM4 16 -#define RESERVED_OFS 28 - - -extern QCC_def_t *pr_scope; -extern int pr_error_count, pr_warning_count; - -void QCC_PR_NewLine (pbool incomment); -QCC_def_t *QCC_PR_GetDef (QCC_type_t *type, char *name, QCC_def_t *scope, pbool allocate, int arraysize, pbool saved); - -void QCC_PR_PrintDefs (void); - -void QCC_PR_SkipToSemicolon (void); - -#define MAX_EXTRA_PARMS 128 -#ifdef MAX_EXTRA_PARMS -extern char pr_parm_names[MAX_PARMS+MAX_EXTRA_PARMS][MAX_NAME]; -extern QCC_def_t *extra_parms[MAX_EXTRA_PARMS]; -#else -extern char pr_parm_names[MAX_PARMS][MAX_NAME]; -#endif -extern pbool pr_trace; - -#define G_FLOAT(o) (qcc_pr_globals[o]) -#define G_INT(o) (*(int *)&qcc_pr_globals[o]) -#define G_VECTOR(o) (&qcc_pr_globals[o]) -#define G_STRING(o) (strings + *(QCC_string_t *)&qcc_pr_globals[o]) -#define G_FUNCTION(o) (*(func_t *)&qcc_pr_globals[o]) - -char *QCC_PR_ValueString (etype_t type, void *val); - -void QCC_PR_ClearGrabMacros (void); - -pbool QCC_PR_CompileFile (char *string, char *filename); -void QCC_PR_ResetErrorScope(void); - -extern pbool pr_dumpasm; - -extern QCC_string_t s_file; // filename for function definition - -extern QCC_def_t def_ret, def_parms[MAX_PARMS]; - -void QCC_PR_EmitArrayGetFunction(QCC_def_t *scope, char *arrayname); -void QCC_PR_EmitArraySetFunction(QCC_def_t *scope, char *arrayname); -void QCC_PR_EmitClassFromFunction(QCC_def_t *scope, char *tname); - -void PostCompile(void); -pbool PreCompile(void); - -//============================================================================= - -extern char pr_immediate_string[8192]; - -extern float *qcc_pr_globals; -extern unsigned int numpr_globals; - -extern char *strings; -extern int strofs; - -extern QCC_dstatement_t *statements; -extern int numstatements; -extern int *statement_linenums; - -extern QCC_dfunction_t *functions; -extern int numfunctions; - -extern QCC_ddef_t *qcc_globals; -extern int numglobaldefs; - -extern QCC_def_t *activetemps; - -extern QCC_ddef_t *fields; -extern int numfielddefs; - -extern QCC_type_t *qcc_typeinfo; -extern int numtypeinfos; -extern int maxtypeinfos; - -extern int ForcedCRC; -extern pbool defaultnoref; -extern pbool defaultstatic; - -extern int *qcc_tempofs; -extern int max_temps; -//extern int qcc_functioncalled; //unuse temps if this is true - don't want to reuse the same space. - -extern int tempsstart; -extern int numtemps; - -typedef char PATHSTRING[MAX_DATA_PATH]; - -extern PATHSTRING *precache_sounds; -extern int *precache_sounds_block; -extern int *precache_sounds_used; -extern int numsounds; - -extern PATHSTRING *precache_textures; -extern int *precache_textures_block; -extern int numtextures; - -extern PATHSTRING *precache_models; -extern int *precache_models_block; -extern int *precache_models_used; -extern int nummodels; - -extern PATHSTRING *precache_files; -extern int *precache_files_block; -extern int numfiles; - -typedef struct qcc_includechunk_s { - struct qcc_includechunk_s *prev; - char *filename; - char *currentdatapoint; - int currentlinenumber; - CompilerConstant_t *cnst; -} qcc_includechunk_t; -extern qcc_includechunk_t *currentchunk; - -int QCC_CopyString (char *str); - - - - -typedef struct qcc_cachedsourcefile_s { - char filename[128]; - int size; - char *file; - enum{FT_CODE, FT_DATA} type; //quakec source file or not. - struct qcc_cachedsourcefile_s *next; -} qcc_cachedsourcefile_t; -extern qcc_cachedsourcefile_t *qcc_sourcefile; - - - - - -#ifdef COMMONINLINES -static bool inline QCC_PR_CheckToken (char *string) -{ - if (pr_token_type != tt_punct) - return false; - - if (STRCMP (string, pr_token)) - return false; - - QCC_PR_Lex (); - return true; -} - -static void inline QCC_PR_Expect (char *string) -{ - if (strcmp (string, pr_token)) - QCC_PR_ParseError ("expected %s, found %s",string, pr_token); - QCC_PR_Lex (); -} -#endif - -void editbadfile(char *fname, int line); -char *TypeName(QCC_type_t *type); -void QCC_PR_IncludeChunk (char *data, pbool duplicate, char *filename); -void QCC_PR_IncludeChunkEx(char *data, pbool duplicate, char *filename, CompilerConstant_t *cnst); -pbool QCC_PR_UnInclude(void); -extern void *(*pHash_Get)(hashtable_t *table, const char *name); -extern void *(*pHash_GetNext)(hashtable_t *table, const char *name, void *old); -extern void *(*pHash_Add)(hashtable_t *table, const char *name, void *data, bucket_t *); diff --git a/misc/source/fteqcc-src/qcc_cmdlib.c b/misc/source/fteqcc-src/qcc_cmdlib.c deleted file mode 100644 index b3c4b6b8..00000000 --- a/misc/source/fteqcc-src/qcc_cmdlib.c +++ /dev/null @@ -1,928 +0,0 @@ -// cmdlib.c - -#include "qcc.h" -#include -//#include - -#define PATHSEPERATOR '/' - -#ifndef QCC -extern jmp_buf qcccompileerror; -#endif - -#ifdef _WIN64 - #ifdef _SDL - #define snprintf linuxlike_snprintf - int VARGS linuxlike_snprintf(char *buffer, int size, const char *format, ...) LIKEPRINTF(3); - #define vsnprintf linuxlike_vsnprintf - int VARGS linuxlike_vsnprintf(char *buffer, int size, const char *format, va_list argptr); - void *__imp__vsnprintf = vsnprintf; - #endif -#endif - -// set these before calling CheckParm -int myargc; -char **myargv; - -char qcc_token[1024]; -int qcc_eof; - -const unsigned int type_size[12] = {1, //void - sizeof(string_t)/4, //string - 1, //float - 3, //vector - 1, //entity - 1, //field - sizeof(func_t)/4,//function - 1, //pointer (its an int index) - 1, //integer - 3, //fixme: how big should a variant be? - 0, //ev_struct. variable sized. - 0 //ev_union. variable sized. - }; - -/* -============================================================================ - - BYTE ORDER FUNCTIONS - -============================================================================ -*/ -short (*PRBigShort) (short l); -short (*PRLittleShort) (short l); -int (*PRBigLong) (int l); -int (*PRLittleLong) (int l); -float (*PRBigFloat) (float l); -float (*PRLittleFloat) (float l); - - -short QCC_SwapShort (short l) -{ - qbyte b1,b2; - - b1 = l&255; - b2 = (l>>8)&255; - - return (b1<<8) + b2; -} - -short QCC_Short (short l) -{ - return l; -} - - -int QCC_SwapLong (int l) -{ - qbyte b1,b2,b3,b4; - - b1 = (qbyte)l; - b2 = (qbyte)(l>>8); - b3 = (qbyte)(l>>16); - b4 = (qbyte)(l>>24); - - return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4; -} - -int QCC_Long (int l) -{ - return l; -} - - -float QCC_SwapFloat (float l) -{ - union {qbyte b[4]; float f;} in, out; - - in.f = l; - out.b[0] = in.b[3]; - out.b[1] = in.b[2]; - out.b[2] = in.b[1]; - out.b[3] = in.b[0]; - - return out.f; -} - -float QCC_Float (float l) -{ - return l; -} - -void SetEndian(void) -{ - union {qbyte b[2]; unsigned short s;} ed; - ed.s = 255; - if (ed.b[0] == 255) - { - PRBigShort = QCC_SwapShort; - PRLittleShort = QCC_Short; - PRBigLong = QCC_SwapLong; - PRLittleLong = QCC_Long; - PRBigFloat = QCC_SwapFloat; - PRLittleFloat = QCC_Float; - } - else - { - PRBigShort = QCC_Short; - PRLittleShort = QCC_SwapShort; - PRBigLong = QCC_Long; - PRLittleLong = QCC_SwapLong; - PRBigFloat = QCC_Float; - PRLittleFloat = QCC_SwapFloat; - } -} - - - -#ifndef MINIMAL -/* -================ -I_FloatTime -================ -*/ -/* -double I_FloatTime (void) -{ - struct timeval tp; - struct timezone tzp; - static int secbase; - - gettimeofday(&tp, &tzp); - - if (!secbase) - { - secbase = tp.tv_sec; - return tp.tv_usec/1000000.0; - } - - return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0; -} - - */ - - -#ifdef QCC -int QC_strncasecmp (const char *s1, const char *s2, int n) -{ - int c1, c2; - - while (1) - { - c1 = *s1++; - c2 = *s2++; - - if (!n--) - return 0; // strings are equal until end point - - if (c1 != c2) - { - if (c1 >= 'a' && c1 <= 'z') - c1 -= ('a' - 'A'); - if (c2 >= 'a' && c2 <= 'z') - c2 -= ('a' - 'A'); - if (c1 != c2) - return -1; // strings not equal - } - if (!c1) - return 0; // strings are equal -// s1++; -// s2++; - } - - return -1; -} - -int QC_strcasecmp (const char *s1, const char *s2) -{ - return QC_strncasecmp(s1, s2, 0x7fffffff); -} - -#else -int QC_strncasecmp(const char *s1, const char *s2, int n); -int QC_strcasecmp (const char *s1, const char *s2) -{ - return QC_strncasecmp(s1, s2, 0x7fffffff); -} - -#endif - - - -#endif //minimal -/* -============== -COM_Parse - -Parse a token out of a string -============== -*/ -char *QCC_COM_Parse (char *data) -{ - int c; - int len; - - len = 0; - qcc_token[0] = 0; - - if (!data) - return NULL; - -// skip whitespace -skipwhite: - while ( (c = *data) <= ' ') - { - if (c == 0) - { - qcc_eof = true; - return NULL; // end of file; - } - data++; - } - -// skip // comments - if (c=='/' && data[1] == '/') - { - while (*data && *data != '\n') - data++; - goto skipwhite; - } - - // skip /* comments - if (c=='/' && data[1] == '*') - { - while (data[1] && (data[0] != '*' || data[1] != '/')) - data++; - data+=2; - goto skipwhite; - } - - -// handle quoted strings specially - if (c == '\"') - { - data++; - do - { - c = *data++; - if (c=='\\' && *data == '\"') - c = *data++; //allow C-style string escapes - else if (c=='\\' && *data == '\\') - c = *data++; // \ is now a special character so it needs to be marked up using itself - else if (c=='\\' && *data == 'n') - { // and do new lines while we're at it. - c = '\n'; - data++; - } - else if (c=='\"') - { - qcc_token[len] = 0; - return data; - } - else if (c=='\0'||c=='\n') - { - qcc_token[len] = 0; - return data; - } - qcc_token[len] = c; - len++; - } while (1); - } - -// parse single characters - if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':' || c==',') - { - qcc_token[len] = c; - len++; - qcc_token[len] = 0; - return data+1; - } - -// parse a regular word - do - { - qcc_token[len] = c; - data++; - len++; - c = *data; - if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':' || c=='\"' || c==',') - break; - } while (c>32); - - qcc_token[len] = 0; - return data; -} - -//more C tokens... -char *QCC_COM_Parse2 (char *data) -{ - int c; - int len; - - len = 0; - qcc_token[0] = 0; - - if (!data) - return NULL; - -// skip whitespace -skipwhite: - while ( (c = *data) <= ' ') - { - if (c == 0) - { - qcc_eof = true; - return NULL; // end of file; - } - data++; - } - -// skip // comments - if (c=='/' && data[1] == '/') - { - while (*data && *data != '\n') - data++; - goto skipwhite; - } - - -// handle quoted strings specially - if (c == '\"') - { - data++; - do - { - c = *data++; - if (c=='\\' && *data == '\"') - c = *data++; //allow C-style string escapes - else if (c=='\\' && *data == '\\') - c = *data++; // \ is now a special character so it needs to be marked up using itself - else if (c=='\\' && *data == 'n') - { // and do new lines while we're at it. - c = '\n'; - data++; - } - else if (c=='\"'||c=='\0') - qcc_token[len] = c; - len++; - } while (1); - } - -// parse numbers - if (c >= '0' && c <= '9') - { - if (c == '0' && data[1] == 'x') - { //parse hex - qcc_token[0] = '0'; - c='x'; - len=1; - data++; - for(;;) - { //parse regular number - qcc_token[len] = c; - data++; - len++; - c = *data; - if ((c<'0'|| c>'9') && (c<'a'||c>'f') && (c<'A'||c>'F') && c != '.') - break; - } - - } - else - { - for(;;) - { //parse regular number - qcc_token[len] = c; - data++; - len++; - c = *data; - if ((c<'0'|| c>'9') && c != '.') - break; - } - } - - qcc_token[len] = 0; - return data; - } -// parse words - else if ((c>= 'a' && c <= 'z') || (c>= 'A' && c <= 'Z') || c == '_') - { - do - { - qcc_token[len] = c; - data++; - len++; - c = *data; - } while ((c>= 'a' && c <= 'z') || (c>= 'A' && c <= 'Z') || c == '_'); - - qcc_token[len] = 0; - return data; - } - else - { - qcc_token[len] = c; - len++; - qcc_token[len] = 0; - return data+1; - } -} - -char *VARGS qcva (char *text, ...) -{ - va_list argptr; - static char msg[2048]; - - va_start (argptr,text); - QC_vsnprintf (msg,sizeof(msg)-1, text,argptr); - va_end (argptr); - - return msg; -} - - -#ifndef MINIMAL - -char *QC_strupr (char *start) -{ - char *in; - in = start; - while (*in) - { - *in = toupper(*in); - in++; - } - return start; -} - -char *QC_strlower (char *start) -{ - char *in; - in = start; - while (*in) - { - *in = tolower(*in); - in++; - } - return start; -} - - -/* -============================================================================= - - MISC FUNCTIONS - -============================================================================= -*/ - -/* -================= -Error - -For abnormal program terminations -================= -*/ -void VARGS QCC_Error (int errortype, const char *error, ...) -{ - extern int numsourcefiles; - va_list argptr; - char msg[2048]; - - va_start (argptr,error); - QC_vsnprintf (msg,sizeof(msg)-1, error,argptr); - va_end (argptr); - - printf ("\n************ ERROR ************\n%s\n", msg); - - - editbadfile(strings+s_file, pr_source_line); - - numsourcefiles = 0; - -#ifndef QCC - longjmp(qcccompileerror, 1); -#else - print ("Press any key\n"); - getch(); -#endif - exit (1); -} - - -/* -================= -CheckParm - -Checks for the given parameter in the program's command line arguments -Returns the argument number (1 to argc-1) or 0 if not present -================= -*/ -int QCC_CheckParm (char *check) -{ - int i; - - for (i = 1;i 0 && path[length] != PATHSEPERATOR) - length--; - path[length] = 0; -} - -/* -==================== -Extract file parts -==================== -*/ -void ExtractFilePath (char *path, char *dest) -{ - char *src; - - src = path + strlen(path) - 1; - -// -// back up until a \ or the start -// - while (src != path && *(src-1) != PATHSEPERATOR) - src--; - - memcpy (dest, path, src-path); - dest[src-path] = 0; -} - -void ExtractFileBase (char *path, char *dest) -{ - char *src; - - src = path + strlen(path) - 1; - -// -// back up until a \ or the start -// - while (src != path && *(src-1) != PATHSEPERATOR) - src--; - - while (*src && *src != '.') - { - *dest++ = *src++; - } - *dest = 0; -} - -void ExtractFileExtension (char *path, char *dest) -{ - char *src; - - src = path + strlen(path) - 1; - -// -// back up until a . or the start -// - while (src != path && *(src-1) != '.') - src--; - if (src == path) - { - *dest = 0; // no extension - return; - } - - strcpy (dest,src); -} - - -/* -============== -ParseNum / ParseHex -============== -*/ -long ParseHex (char *hex) -{ - char *str; - long num; - - num = 0; - str = hex; - - while (*str) - { - num <<= 4; - if (*str >= '0' && *str <= '9') - num += *str-'0'; - else if (*str >= 'a' && *str <= 'f') - num += 10 + *str-'a'; - else if (*str >= 'A' && *str <= 'F') - num += 10 + *str-'A'; - else - QCC_Error (ERR_BADHEX, "Bad hex number: %s",hex); - str++; - } - - return num; -} - - -long ParseNum (char *str) -{ - if (str[0] == '$') - return ParseHex (str+1); - if (str[0] == '0' && str[1] == 'x') - return ParseHex (str+2); - return atol (str); -} - - - - - - - - -//buffer size and max size are different. buffer is bigger. - -#define MAXQCCFILES 3 -struct { - char name[64]; - char *buff; -// int buffismalloc; - int buffsize; - int ofs; - int maxofs; -} qccfile[MAXQCCFILES]; -int SafeOpenWrite (char *filename, int maxsize) -{ - int i; - for (i = 0; i < MAXQCCFILES; i++) - { - if (!qccfile[i].buff) - { - strcpy(qccfile[i].name, filename); - qccfile[i].buffsize = maxsize; - qccfile[i].maxofs = 0; - qccfile[i].ofs = 0; -// if (maxsize > 8192) -// qccfile[i].buffismalloc = 1; -// else -// qccfile[i].buffismalloc = 0; -// if (qccfile[i].buffismalloc) - qccfile[i].buff = malloc(qccfile[i].buffsize); -// else -// qccfile[i].buff = memalloc(qccfile[i].buffsize); - return i; - } - } - QCC_Error(ERR_TOOMANYOPENFILES, "Too many open files on file %s", filename); - return -1; -} - -void ResizeBuf(int hand, int newsize) -{ -// int wasmal = qccfile[hand].buffismalloc; - char *nb; - - if (qccfile[hand].buffsize >= newsize) - return; //already big enough - -// if (newsize > 8192) -// { -// qccfile[hand].buffismalloc = true; - nb = malloc(newsize); -// } -// else -// { -// qccfile[hand].buffismalloc = false; -// nb = memalloc(newsize); -// } - - memcpy(nb, qccfile[hand].buff, qccfile[hand].maxofs); -// if (wasmal) - free(qccfile[hand].buff); -// else -// memfree(qccfile[hand].buff); - qccfile[hand].buff = nb; - qccfile[hand].buffsize = newsize; -} -void SafeWrite(int hand, void *buf, long count) -{ - if (qccfile[hand].ofs +count >= qccfile[hand].buffsize) - ResizeBuf(hand, qccfile[hand].ofs + count+(64*1024)); - - memcpy(&qccfile[hand].buff[qccfile[hand].ofs], buf, count); - qccfile[hand].ofs+=count; - if (qccfile[hand].ofs > qccfile[hand].maxofs) - qccfile[hand].maxofs = qccfile[hand].ofs; -} -int SafeSeek(int hand, int ofs, int mode) -{ - if (mode == SEEK_CUR) - return qccfile[hand].ofs; - else - { - ResizeBuf(hand, ofs+1024); - qccfile[hand].ofs = ofs; - if (qccfile[hand].ofs > qccfile[hand].maxofs) - qccfile[hand].maxofs = qccfile[hand].ofs; - return 0; - } -} -void SafeClose(int hand) -{ - externs->WriteFile(qccfile[hand].name, qccfile[hand].buff, qccfile[hand].maxofs); -// if (qccfile[hand].buffismalloc) - free(qccfile[hand].buff); -// else -// memfree(qccfile[hand].buff); - qccfile[hand].buff = NULL; -} - -qcc_cachedsourcefile_t *qcc_sourcefile; -long QCC_LoadFile (char *filename, void **bufferptr) -{ - char *mem; - int len; - len = externs->FileSize(filename); - if (len < 0) - { - QCC_Error(ERR_COULDNTOPENFILE, "Couldn't open file %s", filename); -// if (!Abort) - return -1; -// Abort("failed to find file %s", filename); - } - mem = qccHunkAlloc(sizeof(qcc_cachedsourcefile_t) + len+2); - - ((qcc_cachedsourcefile_t*)mem)->next = qcc_sourcefile; - qcc_sourcefile = (qcc_cachedsourcefile_t*)mem; - qcc_sourcefile->size = len; - mem += sizeof(qcc_cachedsourcefile_t); - strcpy(qcc_sourcefile->filename, filename); - qcc_sourcefile->file = mem; - qcc_sourcefile->type = FT_CODE; - - externs->ReadFile(filename, mem, len+2); - mem[len] = '\n'; - mem[len+1] = '\0'; - *bufferptr=mem; - - return len; -} -void QCC_AddFile (char *filename) -{ - char *mem; - int len; - len = externs->FileSize(filename); - if (len < 0) - Abort("failed to find file %s", filename); - mem = qccHunkAlloc(sizeof(qcc_cachedsourcefile_t) + len+1); - - ((qcc_cachedsourcefile_t*)mem)->next = qcc_sourcefile; - qcc_sourcefile = (qcc_cachedsourcefile_t*)mem; - qcc_sourcefile->size = len; - mem += sizeof(qcc_cachedsourcefile_t); - strcpy(qcc_sourcefile->filename, filename); - qcc_sourcefile->file = mem; - qcc_sourcefile->type = FT_DATA; - - externs->ReadFile(filename, mem, len+1); - mem[len] = '\0'; -} -void *FS_ReadToMem(char *filename, void *mem, int *len) -{ - if (!mem) - { - *len = externs->FileSize(filename); - mem = memalloc(*len); - } - return externs->ReadFile(filename, mem, *len); -} - -void FS_CloseFromMem(void *mem) -{ - memfree(mem); -} - - -#endif - -void StripExtension (char *path) -{ - int length; - - length = strlen(path)-1; - while (length > 0 && path[length] != '.') - { - length--; - if (path[length] == '/') - return; // no extension - } - if (length) - path[length] = 0; -} diff --git a/misc/source/fteqcc-src/qcc_pr_comp.c b/misc/source/fteqcc-src/qcc_pr_comp.c deleted file mode 100644 index badefcac..00000000 --- a/misc/source/fteqcc-src/qcc_pr_comp.c +++ /dev/null @@ -1,9521 +0,0 @@ -#ifndef MINIMAL - -#include "qcc.h" -void QCC_PR_ParseAsm(void); - -#define MEMBERFIELDNAME "__m%s" - -#define STRCMP(s1,s2) (((*s1)!=(*s2)) || strcmp(s1+1,s2+1)) //saves about 2-6 out of 120 - expansion of idea from fastqcc -#define STRNCMP(s1,s2,l) (((*s1)!=(*s2)) || strncmp(s1+1,s2+1,l)) //pathetic saving here. - -extern char *compilingfile; - -int conditional; - -//standard qcc keywords -#define keyword_do 1 -#define keyword_return 1 -#define keyword_if 1 -#define keyword_else 1 -#define keyword_local 1 -#define keyword_while 1 - -//extended keywords. -pbool keyword_asm; -pbool keyword_break; -pbool keyword_case; -pbool keyword_class; -pbool keyword_optional; -pbool keyword_const; //fixme -pbool keyword_continue; -pbool keyword_default; -pbool keyword_entity; //for skipping the local -pbool keyword_float; //for skipping the local -pbool keyword_for; -pbool keyword_goto; -pbool keyword_int; //for skipping the local -pbool keyword_integer; //for skipping the local -pbool keyword_state; -pbool keyword_string; //for skipping the local -pbool keyword_struct; -pbool keyword_switch; -pbool keyword_thinktime; -pbool keyword_var; //allow it to be initialised and set around the place. -pbool keyword_vector; //for skipping the local - - -pbool keyword_enum; //kinda like in c, but typedef not supported. -pbool keyword_enumflags; //like enum, but doubles instead of adds 1. -pbool keyword_typedef; //fixme -#define keyword_codesys flag_acc //reacc needs this (forces the resultant crc) -#define keyword_function flag_acc //reacc needs this (reacc has this on all functions, wierd eh?) -#define keyword_objdata flag_acc //reacc needs this (following defs are fields rather than globals, use var to disable) -#define keyword_object flag_acc //reacc needs this (an entity) -#define keyword_pfunc flag_acc //reacc needs this (pointer to function) -#define keyword_system flag_acc //reacc needs this (potatos) -#define keyword_real flag_acc //reacc needs this (a float) -#define keyword_exit flag_acc //emits an OP_DONE opcode. -#define keyword_external flag_acc //reacc needs this (a builtin) -pbool keyword_extern; //function is external, don't error or warn if the body was not found -pbool keyword_shared; //mark global to be copied over when progs changes (part of FTE_MULTIPROGS) -pbool keyword_noref; //nowhere else references this, don't strip it. -pbool keyword_nosave; //don't write the def to the output. -pbool keyword_union; //you surly know what a union is! - -#define keyword_not 1 //hexenc support needs this, and fteqcc can optimise without it, but it adds an extra token after the if, so it can cause no namespace conflicts - -pbool keywords_coexist; //don't disable a keyword simply because a var was made with the same name. -pbool output_parms; //emit some PARMX fields. confuses decompilers. -pbool autoprototype; //take two passes over the source code. First time round doesn't enter and functions or initialise variables. -pbool pr_subscopedlocals; //causes locals to be valid ONLY within their statement block. (they simply can't be referenced by name outside of it) -pbool flag_ifstring; //makes if (blah) equivelent to if (blah != "") which resolves some issues in multiprogs situations. -pbool flag_iffloat; //use an op_if_f instruction instead of op_if so if(-0) evaluates to false. -pbool flag_acc; //reacc like behaviour of src files (finds *.qc in start dir and compiles all in alphabetical order) -pbool flag_caseinsensative; //symbols will be matched to an insensative case if the specified case doesn't exist. This should b usable for any mod -pbool flag_laxcasts; //Allow lax casting. This'll produce loadsa warnings of course. But allows compilation of certain dodgy code. -pbool flag_hashonly; //Allows use of only #constant for precompiler constants, allows certain preqcc using mods to compile -pbool flag_fasttrackarrays; //Faster arrays, dynamically detected, activated only in supporting engines. -pbool flag_msvcstyle; //MSVC style warnings, so msvc's ide works properly -pbool flag_assume_integer; //5 - is that an integer or a float? qcc says float. but we support int too, so maybe we want that instead? -pbool flag_filetimes; -pbool flag_typeexplicit; //no implicit type conversions, you must do the casts yourself. - -pbool opt_overlaptemps; //reduce numpr_globals by reuse of temps. When they are not needed they are freed for reuse. The way this is implemented is better than frikqcc's. (This is the single most important optimisation) -pbool opt_assignments; //STORE_F isn't used if an operation wrote to a temp. -pbool opt_shortenifnots; //if(!var) is made an IF rather than NOT IFNOT -pbool opt_noduplicatestrings; //brute force string check. time consuming but more effective than the equivelent in frikqcc. -pbool opt_constantarithmatic; //3*5 appears as 15 instead of the extra statement. -pbool opt_nonvec_parms; //store_f instead of store_v on function calls, where possible. -pbool opt_constant_names; //take out the defs and name strings of constants. -pbool opt_constant_names_strings;//removes the defs of strings too. plays havok with multiprogs. -pbool opt_precache_file; //remove the call, the parameters, everything. -pbool opt_filenames; //strip filenames. hinders older decompilers. -pbool opt_unreferenced; //strip defs that are not referenced. -pbool opt_function_names; //strip out the names of builtin functions. -pbool opt_locals; //strip out the names of locals and immediates. -pbool opt_dupconstdefs; //float X = 5; and float Y = 5; occupy the same global with this. -pbool opt_return_only; //RETURN; DONE; at the end of a function strips out the done statement if there is no way to get to it. -pbool opt_compound_jumps; //jumps to jump statements jump to the final point. -pbool opt_stripfunctions; //if a functions is only ever called directly or by exe, don't emit the def. -pbool opt_locals_marshalling; //make the local vars of all functions occupy the same globals. -pbool opt_logicops; //don't make conditions enter functions if the return value will be discarded due to a previous value. (C style if statements) -pbool opt_vectorcalls; //vectors can be packed into 3 floats, which can yield lower numpr_globals, but cost two more statements per call (only works for q1 calling conventions). -pbool opt_simplifiedifs; //if (f != 0) -> if (f). if (f == 0) -> ifnot (f) -//bool opt_comexprremoval; - -//these are the results of the opt_. The values are printed out when compilation is compleate, showing effectivness. -int optres_shortenifnots; -int optres_assignments; -int optres_overlaptemps; -int optres_noduplicatestrings; -int optres_constantarithmatic; -int optres_nonvec_parms; -int optres_constant_names; -int optres_constant_names_strings; -int optres_precache_file; -int optres_filenames; -int optres_unreferenced; -int optres_function_names; -int optres_locals; -int optres_dupconstdefs; -int optres_return_only; -int optres_compound_jumps; -//int optres_comexprremoval; -int optres_stripfunctions; -int optres_locals_marshalling; -int optres_logicops; - -int optres_test1; -int optres_test2; - -void *(*pHash_Get)(hashtable_t *table, const char *name); -void *(*pHash_GetNext)(hashtable_t *table, const char *name, void *old); -void *(*pHash_Add)(hashtable_t *table, const char *name, void *data, bucket_t *); - -QCC_def_t *QCC_PR_DummyDef(QCC_type_t *type, char *name, QCC_def_t *scope, int arraysize, unsigned int ofs, int referable, pbool saved); -QCC_type_t *QCC_PR_FindType (QCC_type_t *type); -QCC_type_t *QCC_PR_PointerType (QCC_type_t *pointsto); -QCC_type_t *QCC_PR_FieldType (QCC_type_t *pointsto); - -void QCC_PR_ParseState (void); -pbool simplestore; -pbool expandedemptymacro; - -QCC_pr_info_t pr; -//QCC_def_t **pr_global_defs/*[MAX_REGS]*/; // to find def for a global variable - -//keeps track of how many funcs are called while parsing a statement -//int qcc_functioncalled; - -//======================================== - -QCC_def_t *pr_scope; // the function being parsed, or NULL -QCC_type_t *pr_classtype; -pbool pr_dumpasm; -QCC_string_t s_file, s_file2; // filename for function definition - -unsigned int locals_start; // for tracking local variables vs temps -unsigned int locals_end; // for tracking local variables vs temps - -jmp_buf pr_parse_abort; // longjump with this on parse error - -void QCC_PR_ParseDefs (char *classname); - -pbool qcc_usefulstatement; - -int max_breaks; -int max_continues; -int max_cases; -int num_continues; -int num_breaks; -int num_cases; -int *pr_breaks; -int *pr_continues; -int *pr_cases; -QCC_def_t **pr_casesdef; -QCC_def_t **pr_casesdef2; - -typedef struct { - int statementno; - int lineno; - char name[256]; -} gotooperator_t; - -int max_labels; -int max_gotos; -gotooperator_t *pr_labels; -gotooperator_t *pr_gotos; -int num_gotos; -int num_labels; - -QCC_def_t *extra_parms[MAX_EXTRA_PARMS]; - -//#define ASSOC_RIGHT_RESULT ASSOC_RIGHT - -//======================================== - -//FIXME: modifiy list so most common GROUPS are first -//use look up table for value of first char and sort by first char and most common...? - -//if true, effectivly {b=a; return a;} -QCC_opcode_t pr_opcodes[] = -{ - {6, "", "DONE", -1, ASSOC_LEFT, &type_void, &type_void, &type_void}, - - {6, "*", "MUL_F", 3, ASSOC_LEFT, &type_float, &type_float, &type_float}, - {6, "*", "MUL_V", 3, ASSOC_LEFT, &type_vector, &type_vector, &type_float}, - {6, "*", "MUL_FV", 3, ASSOC_LEFT, &type_float, &type_vector, &type_vector}, - {6, "*", "MUL_VF", 3, ASSOC_LEFT, &type_vector, &type_float, &type_vector}, - - {6, "/", "DIV_F", 3, ASSOC_LEFT, &type_float, &type_float, &type_float}, - - {6, "+", "ADD_F", 4, ASSOC_LEFT, &type_float, &type_float, &type_float}, - {6, "+", "ADD_V", 4, ASSOC_LEFT, &type_vector, &type_vector, &type_vector}, - - {6, "-", "SUB_F", 4, ASSOC_LEFT, &type_float, &type_float, &type_float}, - {6, "-", "SUB_V", 4, ASSOC_LEFT, &type_vector, &type_vector, &type_vector}, - - {6, "==", "EQ_F", 5, ASSOC_LEFT, &type_float, &type_float, &type_float}, - {6, "==", "EQ_V", 5, ASSOC_LEFT, &type_vector, &type_vector, &type_float}, - {6, "==", "EQ_S", 5, ASSOC_LEFT, &type_string, &type_string, &type_float}, - {6, "==", "EQ_E", 5, ASSOC_LEFT, &type_entity, &type_entity, &type_float}, - {6, "==", "EQ_FNC", 5, ASSOC_LEFT, &type_function, &type_function, &type_float}, - - {6, "!=", "NE_F", 5, ASSOC_LEFT, &type_float, &type_float, &type_float}, - {6, "!=", "NE_V", 5, ASSOC_LEFT, &type_vector, &type_vector, &type_float}, - {6, "!=", "NE_S", 5, ASSOC_LEFT, &type_string, &type_string, &type_float}, - {6, "!=", "NE_E", 5, ASSOC_LEFT, &type_entity, &type_entity, &type_float}, - {6, "!=", "NE_FNC", 5, ASSOC_LEFT, &type_function, &type_function, &type_float}, - - {6, "<=", "LE", 5, ASSOC_LEFT, &type_float, &type_float, &type_float}, - {6, ">=", "GE", 5, ASSOC_LEFT, &type_float, &type_float, &type_float}, - {6, "<", "LT", 5, ASSOC_LEFT, &type_float, &type_float, &type_float}, - {6, ">", "GT", 5, ASSOC_LEFT, &type_float, &type_float, &type_float}, - - {6, ".", "INDIRECT_F", 1, ASSOC_LEFT, &type_entity, &type_field, &type_float}, - {6, ".", "INDIRECT_V", 1, ASSOC_LEFT, &type_entity, &type_field, &type_vector}, - {6, ".", "INDIRECT_S", 1, ASSOC_LEFT, &type_entity, &type_field, &type_string}, - {6, ".", "INDIRECT_E", 1, ASSOC_LEFT, &type_entity, &type_field, &type_entity}, - {6, ".", "INDIRECT_FI", 1, ASSOC_LEFT, &type_entity, &type_field, &type_field}, - {6, ".", "INDIRECT_FU", 1, ASSOC_LEFT, &type_entity, &type_field, &type_function}, - - {6, ".", "ADDRESS", 1, ASSOC_LEFT, &type_entity, &type_field, &type_pointer}, - - {6, "=", "STORE_F", 6, ASSOC_RIGHT, &type_float, &type_float, &type_float}, - {6, "=", "STORE_V", 6, ASSOC_RIGHT, &type_vector, &type_vector, &type_vector}, - {6, "=", "STORE_S", 6, ASSOC_RIGHT, &type_string, &type_string, &type_string}, - {6, "=", "STORE_ENT", 6, ASSOC_RIGHT, &type_entity, &type_entity, &type_entity}, - {6, "=", "STORE_FLD", 6, ASSOC_RIGHT, &type_field, &type_field, &type_field}, - {6, "=", "STORE_FNC", 6, ASSOC_RIGHT, &type_function, &type_function, &type_function}, - - {6, "=", "STOREP_F", 6, ASSOC_RIGHT, &type_pointer, &type_float, &type_float}, - {6, "=", "STOREP_V", 6, ASSOC_RIGHT, &type_pointer, &type_vector, &type_vector}, - {6, "=", "STOREP_S", 6, ASSOC_RIGHT, &type_pointer, &type_string, &type_string}, - {6, "=", "STOREP_ENT", 6, ASSOC_RIGHT, &type_pointer, &type_entity, &type_entity}, - {6, "=", "STOREP_FLD", 6, ASSOC_RIGHT, &type_pointer, &type_field, &type_field}, - {6, "=", "STOREP_FNC", 6, ASSOC_RIGHT, &type_pointer, &type_function, &type_function}, - - {6, "", "RETURN", -1, ASSOC_LEFT, &type_float, &type_void, &type_void}, - - {6, "!", "NOT_F", -1, ASSOC_LEFT, &type_float, &type_void, &type_float}, - {6, "!", "NOT_V", -1, ASSOC_LEFT, &type_vector, &type_void, &type_float}, - {6, "!", "NOT_S", -1, ASSOC_LEFT, &type_vector, &type_void, &type_float}, - {6, "!", "NOT_ENT", -1, ASSOC_LEFT, &type_entity, &type_void, &type_float}, - {6, "!", "NOT_FNC", -1, ASSOC_LEFT, &type_function, &type_void, &type_float}, - - {6, "", "IF", -1, ASSOC_RIGHT, &type_float, NULL, &type_void}, - {6, "", "IFNOT", -1, ASSOC_RIGHT, &type_float, NULL, &type_void}, - -// calls returns REG_RETURN - {6, "", "CALL0", -1, ASSOC_LEFT, &type_function, &type_void, &type_void}, - {6, "", "CALL1", -1, ASSOC_LEFT, &type_function, &type_void, &type_void}, - {6, "", "CALL2", -1, ASSOC_LEFT, &type_function, &type_void, &type_void}, - {6, "", "CALL3", -1, ASSOC_LEFT, &type_function, &type_void, &type_void}, - {6, "", "CALL4", -1, ASSOC_LEFT, &type_function, &type_void, &type_void}, - {6, "", "CALL5", -1, ASSOC_LEFT, &type_function, &type_void, &type_void}, - {6, "", "CALL6", -1, ASSOC_LEFT, &type_function, &type_void, &type_void}, - {6, "", "CALL7", -1, ASSOC_LEFT, &type_function, &type_void, &type_void}, - {6, "", "CALL8", -1, ASSOC_LEFT, &type_function, &type_void, &type_void}, - - {6, "", "STATE", -1, ASSOC_LEFT, &type_float, &type_float, &type_void}, - - {6, "", "GOTO", -1, ASSOC_RIGHT, NULL, &type_void, &type_void}, - - {6, "&&", "AND", 7, ASSOC_LEFT, &type_float, &type_float, &type_float}, - {6, "||", "OR", 7, ASSOC_LEFT, &type_float, &type_float, &type_float}, - - {6, "&", "BITAND", 3, ASSOC_LEFT, &type_float, &type_float, &type_float}, - {6, "|", "BITOR", 3, ASSOC_LEFT, &type_float, &type_float, &type_float}, - - //version 6 are in normal progs. - - - -//these are hexen2 - {7, "*=", "MULSTORE_F", 6, ASSOC_RIGHT_RESULT, &type_float, &type_float, &type_float}, - {7, "*=", "MULSTORE_VF", 6, ASSOC_RIGHT_RESULT, &type_vector, &type_float, &type_vector}, - {7, "*=", "MULSTOREP_F", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_float, &type_float}, - {7, "*=", "MULSTOREP_VF", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_float, &type_vector}, - - {7, "/=", "DIVSTORE_F", 6, ASSOC_RIGHT_RESULT, &type_float, &type_float, &type_float}, - {7, "/=", "DIVSTOREP_F", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_float, &type_float}, - - {7, "+=", "ADDSTORE_F", 6, ASSOC_RIGHT_RESULT, &type_float, &type_float, &type_float}, - {7, "+=", "ADDSTORE_V", 6, ASSOC_RIGHT_RESULT, &type_vector, &type_vector, &type_vector}, - {7, "+=", "ADDSTOREP_F", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_float, &type_float}, - {7, "+=", "ADDSTOREP_V", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_vector, &type_vector}, - - {7, "-=", "SUBSTORE_F", 6, ASSOC_RIGHT_RESULT, &type_float, &type_float, &type_float}, - {7, "-=", "SUBSTORE_V", 6, ASSOC_RIGHT_RESULT, &type_vector, &type_vector, &type_vector}, - {7, "-=", "SUBSTOREP_F", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_float, &type_float}, - {7, "-=", "SUBSTOREP_V", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_vector, &type_vector}, - - {7, "", "FETCH_GBL_F", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, - {7, "", "FETCH_GBL_V", -1, ASSOC_LEFT, &type_vector, &type_float, &type_vector}, - {7, "", "FETCH_GBL_S", -1, ASSOC_LEFT, &type_string, &type_float, &type_string}, - {7, "", "FETCH_GBL_E", -1, ASSOC_LEFT, &type_entity, &type_float, &type_entity}, - {7, "", "FETCH_GBL_FNC", -1, ASSOC_LEFT, &type_function, &type_float, &type_function}, - - {7, "", "CSTATE", -1, ASSOC_LEFT, &type_float, &type_float, &type_void}, - - {7, "", "CWSTATE", -1, ASSOC_LEFT, &type_float, &type_float, &type_void}, - - {7, "", "THINKTIME", -1, ASSOC_LEFT, &type_entity, &type_float, &type_void}, - - {7, "|=", "BITSET_F", 6, ASSOC_RIGHT, &type_float, &type_float, &type_float}, - {7, "|=", "BITSETP_F", 6, ASSOC_RIGHT, &type_pointer, &type_float, &type_float}, - {7, "&~=", "BITCLR_F", 6, ASSOC_RIGHT, &type_float, &type_float, &type_float}, - {7, "&~=", "BITCLRP_F", 6, ASSOC_RIGHT, &type_pointer, &type_float, &type_float}, - - {7, "", "RAND0", -1, ASSOC_LEFT, &type_void, &type_void, &type_float}, - {7, "", "RAND1", -1, ASSOC_LEFT, &type_float, &type_void, &type_float}, - {7, "", "RAND2", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, - {7, "", "RANDV0", -1, ASSOC_LEFT, &type_void, &type_void, &type_vector}, - {7, "", "RANDV1", -1, ASSOC_LEFT, &type_vector, &type_void, &type_vector}, - {7, "", "RANDV2", -1, ASSOC_LEFT, &type_vector, &type_vector, &type_vector}, - - {7, "", "SWITCH_F", -1, ASSOC_LEFT, &type_void, NULL, &type_void}, - {7, "", "SWITCH_V", -1, ASSOC_LEFT, &type_void, NULL, &type_void}, - {7, "", "SWITCH_S", -1, ASSOC_LEFT, &type_void, NULL, &type_void}, - {7, "", "SWITCH_E", -1, ASSOC_LEFT, &type_void, NULL, &type_void}, - {7, "", "SWITCH_FNC", -1, ASSOC_LEFT, &type_void, NULL, &type_void}, - - {7, "", "CASE", -1, ASSOC_LEFT, &type_void, NULL, &type_void}, - {7, "", "CASERANGE", -1, ASSOC_LEFT, &type_void, &type_void, NULL}, - - -//Later are additions by DMW. - - {7, "", "CALL1H", -1, ASSOC_LEFT, &type_function, &type_vector, &type_void}, - {7, "", "CALL2H", -1, ASSOC_LEFT, &type_function, &type_vector, &type_vector}, - {7, "", "CALL3H", -1, ASSOC_LEFT, &type_function, &type_vector, &type_vector}, - {7, "", "CALL4H", -1, ASSOC_LEFT, &type_function, &type_vector, &type_vector}, - {7, "", "CALL5H", -1, ASSOC_LEFT, &type_function, &type_vector, &type_vector}, - {7, "", "CALL6H", -1, ASSOC_LEFT, &type_function, &type_vector, &type_vector}, - {7, "", "CALL7H", -1, ASSOC_LEFT, &type_function, &type_vector, &type_vector}, - {7, "", "CALL8H", -1, ASSOC_LEFT, &type_function, &type_vector, &type_vector}, - - {7, "=", "STORE_I", 6, ASSOC_RIGHT, &type_integer, &type_integer, &type_integer}, - {7, "=", "STORE_IF", 6, ASSOC_RIGHT, &type_float, &type_integer, &type_integer}, - {7, "=", "STORE_FI", 6, ASSOC_RIGHT, &type_integer, &type_float, &type_float}, - - {7, "+", "ADD_I", 4, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, - {7, "+", "ADD_FI", 4, ASSOC_LEFT, &type_float, &type_integer, &type_float}, - {7, "+", "ADD_IF", 4, ASSOC_LEFT, &type_integer, &type_float, &type_float}, - - {7, "-", "SUB_I", 4, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, - {7, "-", "SUB_FI", 4, ASSOC_LEFT, &type_float, &type_integer, &type_float}, - {7, "-", "SUB_IF", 4, ASSOC_LEFT, &type_integer, &type_float, &type_float}, - - {7, "", "C_ITOF", -1, ASSOC_LEFT, &type_integer, &type_void, &type_float}, - {7, "", "C_FTOI", -1, ASSOC_LEFT, &type_float, &type_void, &type_integer}, - {7, "", "CP_ITOF", -1, ASSOC_LEFT, &type_pointer, &type_integer, &type_float}, - {7, "", "CP_FTOI", -1, ASSOC_LEFT, &type_pointer, &type_float, &type_integer}, - - {7, ".", "INDIRECT", 1, ASSOC_LEFT, &type_entity, &type_field, &type_integer}, - {7, "=", "STOREP_I", 6, ASSOC_RIGHT, &type_pointer, &type_integer, &type_integer}, - {7, "=", "STOREP_IF", 6, ASSOC_RIGHT, &type_pointer, &type_float, &type_integer}, - {7, "=", "STOREP_FI", 6, ASSOC_RIGHT, &type_pointer, &type_integer, &type_float}, - - {7, "&", "BITAND_I", 3, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, - {7, "|", "BITOR_I", 3, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, - - {7, "*", "MUL_I", 3, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, - {7, "/", "DIV_I", 3, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, - {7, "==", "EQ_I", 5, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, - {7, "!=", "NE_I", 5, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, - - {7, "", "IFNOTS", -1, ASSOC_RIGHT, &type_string, NULL, &type_void}, - {7, "", "IFS", -1, ASSOC_RIGHT, &type_string, NULL, &type_void}, - - {7, "!", "NOT_I", -1, ASSOC_LEFT, &type_integer, &type_void, &type_integer}, - - {7, "/", "DIV_VF", 3, ASSOC_LEFT, &type_vector, &type_float, &type_float}, - - {7, "^", "XOR_I", 3, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, - {7, ">>", "RSHIFT_I", 3, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, - {7, "<<", "LSHIFT_I", 3, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, - - //var, offset return - {7, "", "GET_POINTER", -1, ASSOC_LEFT, &type_float, &type_integer, &type_pointer}, - {7, "", "MUL4ADD_I", -1, ASSOC_LEFT, &type_pointer, &type_integer, &type_pointer}, - - {7, "=", "LOADA_F", 6, ASSOC_LEFT, &type_float, &type_integer, &type_float}, - {7, "=", "LOADA_V", 6, ASSOC_LEFT, &type_vector, &type_integer, &type_vector}, - {7, "=", "LOADA_S", 6, ASSOC_LEFT, &type_string, &type_integer, &type_string}, - {7, "=", "LOADA_ENT", 6, ASSOC_LEFT, &type_entity, &type_integer, &type_entity}, - {7, "=", "LOADA_FLD", 6, ASSOC_LEFT, &type_field, &type_integer, &type_field}, - {7, "=", "LOADA_FNC", 6, ASSOC_LEFT, &type_function, &type_integer, &type_function}, - {7, "=", "LOADA_I", 6, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, - - {7, "=", "STORE_P", 6, ASSOC_RIGHT, &type_pointer, &type_pointer, &type_void}, - {7, ".", "INDIRECT_P", 1, ASSOC_LEFT, &type_entity, &type_field, &type_pointer}, - - {7, "=", "LOADP_F", 6, ASSOC_LEFT, &type_pointer, &type_integer, &type_float}, - {7, "=", "LOADP_V", 6, ASSOC_LEFT, &type_pointer, &type_integer, &type_vector}, - {7, "=", "LOADP_S", 6, ASSOC_LEFT, &type_pointer, &type_integer, &type_string}, - {7, "=", "LOADP_ENT", 6, ASSOC_LEFT, &type_pointer, &type_integer, &type_entity}, - {7, "=", "LOADP_FLD", 6, ASSOC_LEFT, &type_pointer, &type_integer, &type_field}, - {7, "=", "LOADP_FNC", 6, ASSOC_LEFT, &type_pointer, &type_integer, &type_function}, - {7, "=", "LOADP_I", 6, ASSOC_LEFT, &type_pointer, &type_integer, &type_integer}, - - - {7, "<=", "LE_I", 5, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, - {7, ">=", "GE_I", 5, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, - {7, "<", "LT_I", 5, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, - {7, ">", "GT_I", 5, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, - - {7, "<=", "LE_IF", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, - {7, ">=", "GE_IF", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, - {7, "<", "LT_IF", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, - {7, ">", "GT_IF", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, - - {7, "<=", "LE_FI", 5, ASSOC_LEFT, &type_float, &type_integer, &type_integer}, - {7, ">=", "GE_FI", 5, ASSOC_LEFT, &type_float, &type_integer, &type_integer}, - {7, "<", "LT_FI", 5, ASSOC_LEFT, &type_float, &type_integer, &type_integer}, - {7, ">", "GT_FI", 5, ASSOC_LEFT, &type_float, &type_integer, &type_integer}, - - {7, "==", "EQ_IF", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, - {7, "==", "EQ_FI", 5, ASSOC_LEFT, &type_float, &type_integer, &type_float}, - - //------------------------------------- - //string manipulation. - {7, "+", "ADD_SF", 4, ASSOC_LEFT, &type_string, &type_float, &type_string}, - {7, "-", "SUB_S", 4, ASSOC_LEFT, &type_string, &type_string, &type_float}, - {7, "", "STOREP_C", 1, ASSOC_RIGHT, &type_string, &type_float, &type_float}, - {7, "", "LOADP_C", 1, ASSOC_LEFT, &type_string, &type_void, &type_float}, - //------------------------------------- - - - -{7, "*", "MUL_IF", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, -{7, "*", "MUL_FI", 5, ASSOC_LEFT, &type_float, &type_integer, &type_float}, -{7, "*", "MUL_VI", 5, ASSOC_LEFT, &type_vector, &type_integer, &type_vector}, -{7, "*", "MUL_IV", 5, ASSOC_LEFT, &type_integer, &type_vector, &type_vector}, - -{7, "/", "DIV_IF", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, -{7, "/", "DIV_FI", 5, ASSOC_LEFT, &type_float, &type_integer, &type_float}, - -{7, "&", "BITAND_IF", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, -{7, "|", "BITOR_IF", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, -{7, "&", "BITAND_FI", 5, ASSOC_LEFT, &type_float, &type_integer, &type_float}, -{7, "|", "BITOR_FI", 5, ASSOC_LEFT, &type_float, &type_integer, &type_float}, - -{7, "&&", "AND_I", 7, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, -{7, "||", "OR_I", 7, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, -{7, "&&", "AND_IF", 7, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, -{7, "||", "OR_IF", 7, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, -{7, "&&", "AND_FI", 7, ASSOC_LEFT, &type_float, &type_integer, &type_integer}, -{7, "||", "OR_FI", 7, ASSOC_LEFT, &type_float, &type_integer, &type_integer}, -{7, "!=", "NE_IF", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, -{7, "!=", "NE_FI", 5, ASSOC_LEFT, &type_float, &type_float, &type_integer}, - - - - - - -{7, "<>", "GSTOREP_I", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, -{7, "<>", "GSTOREP_F", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, -{7, "<>", "GSTOREP_ENT", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, -{7, "<>", "GSTOREP_FLD", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, -{7, "<>", "GSTOREP_S", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, -{7, "<>", "GSTOREP_FNC", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, -{7, "<>", "GSTOREP_V", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, - -{7, "<>", "GADDRESS", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, - -{7, "<>", "GLOAD_I", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, -{7, "<>", "GLOAD_F", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, -{7, "<>", "GLOAD_FLD", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, -{7, "<>", "GLOAD_ENT", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, -{7, "<>", "GLOAD_S", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, -{7, "<>", "GLOAD_FNC", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, - -{7, "<>", "BOUNDCHECK", -1, ASSOC_LEFT, &type_integer, NULL, NULL}, - -{7, "", "UNUSED", 6, ASSOC_RIGHT, &type_void, &type_void, &type_void}, -{7, "", "PUSH", -1, ASSOC_RIGHT, &type_float, &type_void, &type_pointer}, -{7, "", "POP", -1, ASSOC_RIGHT, &type_float, &type_void, &type_void}, - -{7, "", "SWITCH_I", -1, ASSOC_LEFT, &type_void, NULL, &type_void}, -{7, "<>", "GLOAD_S", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, - -{6, "", "IF_F", -1, ASSOC_RIGHT, &type_float, NULL, &type_void}, -{6, "","IFNOT_F", -1, ASSOC_RIGHT, &type_float, NULL, &type_void}, - -/* emulated ops begin here */ - {7, "<>", "OP_EMULATED", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, - - - {7, "|=", "BITSET_I", 6, ASSOC_RIGHT, &type_integer, &type_integer, &type_integer}, - {7, "|=", "BITSETP_I", 6, ASSOC_RIGHT, &type_pointer, &type_integer, &type_integer}, - - - {7, "*=", "MULSTORE_I", 6, ASSOC_RIGHT_RESULT, &type_integer, &type_integer, &type_integer}, - {7, "/=", "DIVSTORE_I", 6, ASSOC_RIGHT_RESULT, &type_integer, &type_integer, &type_integer}, - {7, "+=", "ADDSTORE_I", 6, ASSOC_RIGHT_RESULT, &type_integer, &type_integer, &type_integer}, - {7, "-=", "SUBSTORE_I", 6, ASSOC_RIGHT_RESULT, &type_integer, &type_integer, &type_integer}, - - {7, "*=", "MULSTOREP_I", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_integer, &type_integer}, - {7, "/=", "DIVSTOREP_I", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_integer, &type_integer}, - {7, "+=", "ADDSTOREP_I", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_integer, &type_integer}, - {7, "-=", "SUBSTOREP_I", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_integer, &type_integer}, - - {7, "*=", "MULSTORE_IF", 6, ASSOC_RIGHT_RESULT, &type_integer, &type_float, &type_float}, - {7, "*=", "MULSTOREP_IF", 6, ASSOC_RIGHT_RESULT, &type_intpointer, &type_float, &type_float}, - {7, "/=", "DIVSTORE_IF", 6, ASSOC_RIGHT_RESULT, &type_integer, &type_float, &type_float}, - {7, "/=", "DIVSTOREP_IF", 6, ASSOC_RIGHT_RESULT, &type_intpointer, &type_float, &type_float}, - {7, "+=", "ADDSTORE_IF", 6, ASSOC_RIGHT_RESULT, &type_integer, &type_float, &type_float}, - {7, "+=", "ADDSTOREP_IF", 6, ASSOC_RIGHT_RESULT, &type_intpointer, &type_float, &type_float}, - {7, "-=", "SUBSTORE_IF", 6, ASSOC_RIGHT_RESULT, &type_integer, &type_float, &type_float}, - {7, "-=", "SUBSTOREP_IF", 6, ASSOC_RIGHT_RESULT, &type_intpointer, &type_float, &type_float}, - - {7, "*=", "MULSTORE_FI", 6, ASSOC_RIGHT_RESULT, &type_float, &type_integer, &type_float}, - {7, "*=", "MULSTOREP_FI", 6, ASSOC_RIGHT_RESULT, &type_floatpointer, &type_integer, &type_float}, - {7, "/=", "DIVSTORE_FI", 6, ASSOC_RIGHT_RESULT, &type_float, &type_integer, &type_float}, - {7, "/=", "DIVSTOREP_FI", 6, ASSOC_RIGHT_RESULT, &type_floatpointer, &type_integer, &type_float}, - {7, "+=", "ADDSTORE_FI", 6, ASSOC_RIGHT_RESULT, &type_float, &type_integer, &type_float}, - {7, "+=", "ADDSTOREP_FI", 6, ASSOC_RIGHT_RESULT, &type_floatpointer, &type_integer, &type_float}, - {7, "-=", "SUBSTORE_FI", 6, ASSOC_RIGHT_RESULT, &type_float, &type_integer, &type_float}, - {7, "-=", "SUBSTOREP_FI", 6, ASSOC_RIGHT_RESULT, &type_floatpointer, &type_integer, &type_float}, - - {7, "*=", "MULSTORE_VI", 6, ASSOC_RIGHT_RESULT, &type_vector, &type_integer, &type_vector}, - {7, "*=", "MULSTOREP_VI", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_integer, &type_vector}, - - {7, "=", "LOADA_STRUCT", 6, ASSOC_LEFT, &type_float, &type_integer, &type_float}, - - {7, "=", "STOREP_P", 6, ASSOC_RIGHT, &type_pointer, &type_pointer, &type_pointer}, - - {0, NULL} -}; - - -pbool OpAssignsToC(unsigned int op) -{ - // calls, switches and cases DON'T - if(pr_opcodes[op].type_c == &type_void) - return false; - if(op >= OP_SWITCH_F && op <= OP_CALL8H) - return false; - if(op >= OP_RAND0 && op <= OP_RANDV2) - return false; - // they use a and b, but have 3 types - // safety - if(op >= OP_BITSET && op <= OP_BITCLRP) - return false; - /*if(op >= OP_STORE_I && op <= OP_STORE_FI) - return false; <- add STOREP_*?*/ - if(op == OP_STOREP_C || op == OP_LOADP_C) - return false; - if(op >= OP_MULSTORE_F && op <= OP_SUBSTOREP_V) - return false; - return true; -} -pbool OpAssignsToB(unsigned int op) -{ - if(op >= OP_BITSET && op <= OP_BITCLRP) - return true; - if(op >= OP_STORE_I && op <= OP_STORE_FI) - return true; - if(op == OP_STOREP_C || op == OP_LOADP_C) - return true; - if(op >= OP_MULSTORE_F && op <= OP_SUBSTOREP_V) - return true; - if(op >= OP_STORE_F && op <= OP_STOREP_FNC) - return true; - return false; -} -/*pbool OpAssignedTo(QCC_def_t *v, unsigned int op) -{ - if(OpAssignsToC(op)) - { - } - else if(OpAssignsToB(op)) - { - } - return false; -} -*/ -#undef ASSOC_RIGHT_RESULT - -#define TOP_PRIORITY 7 -#define FUNC_PRIORITY 1 -#define UNARY_PRIORITY 1 -#define NOT_PRIORITY 5 -//conditional and/or -#define CONDITION_PRIORITY 7 - - -//this system cuts out 10/120 -//these evaluate as top first. -QCC_opcode_t *opcodeprioritized[TOP_PRIORITY+1][128] = -{ - { //don't use -/* &pr_opcodes[OP_DONE], - &pr_opcodes[OP_RETURN], - - &pr_opcodes[OP_NOT_F], - &pr_opcodes[OP_NOT_V], - &pr_opcodes[OP_NOT_S], - &pr_opcodes[OP_NOT_ENT], - &pr_opcodes[OP_NOT_FNC], - - &pr_opcodes[OP_IF], - &pr_opcodes[OP_IFNOT], - &pr_opcodes[OP_CALL0], - &pr_opcodes[OP_CALL1], - &pr_opcodes[OP_CALL2], - &pr_opcodes[OP_CALL3], - &pr_opcodes[OP_CALL4], - &pr_opcodes[OP_CALL5], - &pr_opcodes[OP_CALL6], - &pr_opcodes[OP_CALL7], - &pr_opcodes[OP_CALL8], - &pr_opcodes[OP_STATE], - &pr_opcodes[OP_GOTO], - - &pr_opcodes[OP_IFNOTS], - &pr_opcodes[OP_IFS], - - &pr_opcodes[OP_NOT_I], -*/ NULL - }, { //1 - - &pr_opcodes[OP_LOAD_F], - &pr_opcodes[OP_LOAD_V], - &pr_opcodes[OP_LOAD_S], - &pr_opcodes[OP_LOAD_ENT], - &pr_opcodes[OP_LOAD_FLD], - &pr_opcodes[OP_LOAD_FNC], - &pr_opcodes[OP_LOAD_I], - &pr_opcodes[OP_LOAD_P], - &pr_opcodes[OP_ADDRESS], - NULL - }, { //2 -/* //conversion. don't use - &pr_opcodes[OP_C_ITOF], - &pr_opcodes[OP_C_FTOI], - &pr_opcodes[OP_CP_ITOF], - &pr_opcodes[OP_CP_FTOI], -*/ NULL - }, { //3 - &pr_opcodes[OP_MUL_F], - &pr_opcodes[OP_MUL_V], - &pr_opcodes[OP_MUL_FV], - &pr_opcodes[OP_MUL_IV], - &pr_opcodes[OP_MUL_VF], - &pr_opcodes[OP_MUL_VI], - &pr_opcodes[OP_MUL_I], - &pr_opcodes[OP_MUL_FI], - &pr_opcodes[OP_MUL_IF], - - &pr_opcodes[OP_DIV_F], - &pr_opcodes[OP_DIV_I], - &pr_opcodes[OP_DIV_FI], - &pr_opcodes[OP_DIV_IF], - &pr_opcodes[OP_DIV_VF], - - &pr_opcodes[OP_BITAND_F], - &pr_opcodes[OP_BITAND_I], - &pr_opcodes[OP_BITAND_IF], - &pr_opcodes[OP_BITAND_FI], - - &pr_opcodes[OP_BITOR_F], - &pr_opcodes[OP_BITOR_I], - &pr_opcodes[OP_BITOR_IF], - &pr_opcodes[OP_BITOR_FI], - - &pr_opcodes[OP_XOR_I], - &pr_opcodes[OP_RSHIFT_I], - &pr_opcodes[OP_LSHIFT_I], - - NULL - }, { //4 - - &pr_opcodes[OP_ADD_F], - &pr_opcodes[OP_ADD_V], - &pr_opcodes[OP_ADD_I], - &pr_opcodes[OP_ADD_FI], - &pr_opcodes[OP_ADD_IF], - &pr_opcodes[OP_ADD_SF], - - &pr_opcodes[OP_SUB_F], - &pr_opcodes[OP_SUB_V], - &pr_opcodes[OP_SUB_I], - &pr_opcodes[OP_SUB_FI], - &pr_opcodes[OP_SUB_IF], - &pr_opcodes[OP_SUB_S], - NULL - }, { //5 - - &pr_opcodes[OP_EQ_F], - &pr_opcodes[OP_EQ_V], - &pr_opcodes[OP_EQ_S], - &pr_opcodes[OP_EQ_E], - &pr_opcodes[OP_EQ_FNC], - &pr_opcodes[OP_EQ_I], - &pr_opcodes[OP_EQ_IF], - &pr_opcodes[OP_EQ_FI], - - &pr_opcodes[OP_NE_F], - &pr_opcodes[OP_NE_V], - &pr_opcodes[OP_NE_S], - &pr_opcodes[OP_NE_E], - &pr_opcodes[OP_NE_FNC], - &pr_opcodes[OP_NE_I], - &pr_opcodes[OP_NE_IF], - &pr_opcodes[OP_NE_FI], - - &pr_opcodes[OP_LE_F], - &pr_opcodes[OP_LE_I], - &pr_opcodes[OP_LE_IF], - &pr_opcodes[OP_LE_FI], - &pr_opcodes[OP_GE_F], - &pr_opcodes[OP_GE_I], - &pr_opcodes[OP_GE_IF], - &pr_opcodes[OP_GE_FI], - &pr_opcodes[OP_LT_F], - &pr_opcodes[OP_LT_I], - &pr_opcodes[OP_LT_IF], - &pr_opcodes[OP_LT_FI], - &pr_opcodes[OP_GT_F], - &pr_opcodes[OP_GT_I], - &pr_opcodes[OP_GT_IF], - &pr_opcodes[OP_GT_FI], - - NULL - }, { //6 - &pr_opcodes[OP_STOREP_P], - - &pr_opcodes[OP_STORE_F], - &pr_opcodes[OP_STORE_V], - &pr_opcodes[OP_STORE_S], - &pr_opcodes[OP_STORE_ENT], - &pr_opcodes[OP_STORE_FLD], - &pr_opcodes[OP_STORE_FNC], - &pr_opcodes[OP_STORE_I], - &pr_opcodes[OP_STORE_IF], - &pr_opcodes[OP_STORE_FI], - &pr_opcodes[OP_STORE_P], - - &pr_opcodes[OP_STOREP_F], - &pr_opcodes[OP_STOREP_V], - &pr_opcodes[OP_STOREP_S], - &pr_opcodes[OP_STOREP_ENT], - &pr_opcodes[OP_STOREP_FLD], - &pr_opcodes[OP_STOREP_FNC], - &pr_opcodes[OP_STOREP_I], - &pr_opcodes[OP_STOREP_IF], - &pr_opcodes[OP_STOREP_FI], - - &pr_opcodes[OP_DIVSTORE_F], - &pr_opcodes[OP_DIVSTORE_I], - &pr_opcodes[OP_DIVSTORE_FI], - &pr_opcodes[OP_DIVSTORE_IF], - &pr_opcodes[OP_DIVSTOREP_F], - &pr_opcodes[OP_DIVSTOREP_I], - &pr_opcodes[OP_DIVSTOREP_IF], - &pr_opcodes[OP_DIVSTOREP_FI], - &pr_opcodes[OP_MULSTORE_F], - &pr_opcodes[OP_MULSTORE_VF], - &pr_opcodes[OP_MULSTORE_VI], - &pr_opcodes[OP_MULSTORE_I], - &pr_opcodes[OP_MULSTORE_IF], - &pr_opcodes[OP_MULSTORE_FI], - &pr_opcodes[OP_MULSTOREP_F], - &pr_opcodes[OP_MULSTOREP_VF], - &pr_opcodes[OP_MULSTOREP_VI], - &pr_opcodes[OP_MULSTOREP_I], - &pr_opcodes[OP_MULSTOREP_IF], - &pr_opcodes[OP_MULSTOREP_FI], - &pr_opcodes[OP_ADDSTORE_F], - &pr_opcodes[OP_ADDSTORE_V], - &pr_opcodes[OP_ADDSTORE_I], - &pr_opcodes[OP_ADDSTORE_IF], - &pr_opcodes[OP_ADDSTORE_FI], - &pr_opcodes[OP_ADDSTOREP_F], - &pr_opcodes[OP_ADDSTOREP_V], - &pr_opcodes[OP_ADDSTOREP_I], - &pr_opcodes[OP_ADDSTOREP_IF], - &pr_opcodes[OP_ADDSTOREP_FI], - &pr_opcodes[OP_SUBSTORE_F], - &pr_opcodes[OP_SUBSTORE_V], - &pr_opcodes[OP_SUBSTORE_I], - &pr_opcodes[OP_SUBSTORE_IF], - &pr_opcodes[OP_SUBSTORE_FI], - &pr_opcodes[OP_SUBSTOREP_F], - &pr_opcodes[OP_SUBSTOREP_V], - &pr_opcodes[OP_SUBSTOREP_I], - &pr_opcodes[OP_SUBSTOREP_IF], - &pr_opcodes[OP_SUBSTOREP_FI], - - &pr_opcodes[OP_BITSET], - &pr_opcodes[OP_BITSET_I], -// &pr_opcodes[OP_BITSET_IF], -// &pr_opcodes[OP_BITSET_FI], - &pr_opcodes[OP_BITSETP], - &pr_opcodes[OP_BITSETP_I], -// &pr_opcodes[OP_BITSETP_IF], -// &pr_opcodes[OP_BITSETP_FI], - &pr_opcodes[OP_BITCLR], - &pr_opcodes[OP_BITCLRP], - - NULL - }, { //7 - &pr_opcodes[OP_AND_F], - &pr_opcodes[OP_AND_I], - &pr_opcodes[OP_AND_IF], - &pr_opcodes[OP_AND_FI], - &pr_opcodes[OP_OR_F], - &pr_opcodes[OP_OR_I], - &pr_opcodes[OP_OR_IF], - &pr_opcodes[OP_OR_FI], - NULL - } -}; - -pbool QCC_OPCodeValid(QCC_opcode_t *op) -{ - int num; - num = op - pr_opcodes; - - switch(qcc_targetformat) - { - case QCF_STANDARD: - case QCF_KK7: - case QCF_QTEST: - if (num < OP_MULSTORE_F) - return true; - return false; - case QCF_HEXEN2: - if (num >= OP_SWITCH_V && num <= OP_SWITCH_FNC) //these were assigned numbers but were never actually implemtented in standard h2. - return false; -// if (num >= OP_MULSTORE_F && num <= OP_SUBSTOREP_V) -// return false; - if (num <= OP_CALL8H) //CALLXH are fixed up. This is to provide more dynamic switching...?? - return true; - return false; - case QCF_FTE: - case QCF_FTEDEBUG: - //no emulated opcodes - if (num >= OP_NUMREALOPS) - return false; - return true; - case QCF_DARKPLACES: - //all id opcodes. - if (num < OP_MULSTORE_F) - return true; - - //no emulated opcodes - if (num >= OP_NUMREALOPS) - return false; - - //extended opcodes. - //DPFIXME: this is a list of the extended opcodes. I was conservative regarding supported ones. - // at the time of writing, these are the ones that look like they'll work just fine in Blub\0's patch. - // the ones that looked too permissive with bounds checks, or would give false positives are disabled. - // if the DP guys want I can change them as desired. - switch(num) - { - //maths and conditionals (simple opcodes that read from specific globals and write to a global) - case OP_ADD_I: - case OP_ADD_IF: - case OP_ADD_FI: - case OP_SUB_I: - case OP_SUB_IF: - case OP_SUB_FI: - case OP_MUL_I: - case OP_MUL_IF: - case OP_MUL_FI: - case OP_MUL_VI: - case OP_DIV_VF: - case OP_DIV_I: - case OP_DIV_IF: - case OP_DIV_FI: - case OP_BITAND_I: - case OP_BITOR_I: - case OP_BITAND_IF: - case OP_BITOR_IF: - case OP_BITAND_FI: - case OP_BITOR_FI: - case OP_GE_I: - case OP_LE_I: - case OP_GT_I: - case OP_LT_I: - case OP_AND_I: - case OP_OR_I: - case OP_GE_IF: - case OP_LE_IF: - case OP_GT_IF: - case OP_LT_IF: - case OP_AND_IF: - case OP_OR_IF: - case OP_GE_FI: - case OP_LE_FI: - case OP_GT_FI: - case OP_LT_FI: - case OP_AND_FI: - case OP_OR_FI: - case OP_NOT_I: - case OP_EQ_I: - case OP_EQ_IF: - case OP_EQ_FI: - case OP_NE_I: - case OP_NE_IF: - case OP_NE_FI: - return true; - - //stores into a pointer (generated from 'ent.field=XXX') - case OP_STOREP_I: //no worse than the other OP_STOREP_X functions - //reads from an entity field - case OP_LOAD_I: //no worse than the other OP_LOAD_X functions. - case OP_LOAD_P: - return true; - - //stores into the globals array. - //they can change any global dynamically, but thats no security risk. - //fteqcc will not automatically generate these. - //fteqw does not support them either. - case OP_GSTOREP_I: - case OP_GSTOREP_F: - case OP_GSTOREP_ENT: - case OP_GSTOREP_FLD: - case OP_GSTOREP_S: - case OP_GSTOREP_FNC: - case OP_GSTOREP_V: - return true; - - //this opcode looks weird - case OP_GADDRESS://floatc = globals[inta + floatb] (fte does not support) - return true; - - //fteqcc will not automatically generate these - //fteqw does not support them either, for that matter. - case OP_GLOAD_I://c = globals[inta] - case OP_GLOAD_F://note: fte does not support these - case OP_GLOAD_FLD: - case OP_GLOAD_ENT: - case OP_GLOAD_S: - case OP_GLOAD_FNC: - return true; - case OP_GLOAD_V: - return false; //DPFIXME: this is commented out in the patch I was given a link to... because the opcode wasn't defined. - - //these are reportedly functional. - case OP_CALL8H: - case OP_CALL7H: - case OP_CALL6H: - case OP_CALL5H: - case OP_CALL4H: - case OP_CALL3H: - case OP_CALL2H: - case OP_CALL1H: - return true; - - case OP_RAND0: - case OP_RAND1: - case OP_RAND2: - case OP_RANDV0: - case OP_RANDV1: - case OP_RANDV2: - return true; - - case OP_BITSET: // b |= a - case OP_BITCLR: // b &= ~a - case OP_BITSETP: // *b |= a - case OP_BITCLRP: // *b &= ~a - return false; //FIXME: I do not fully follow the controversy over these. - - case OP_SWITCH_F: - case OP_SWITCH_V: - case OP_SWITCH_S: - case OP_SWITCH_E: - case OP_SWITCH_FNC: - case OP_CASE: - case OP_CASERANGE: - return true; - - //assuming the pointers here are fine, the return values are a little strange. - //but its fine - case OP_ADDSTORE_F: - case OP_ADDSTORE_V: - case OP_ADDSTOREP_F: // e.f += f - case OP_ADDSTOREP_V: // e.v += v - case OP_SUBSTORE_F: - case OP_SUBSTORE_V: - case OP_SUBSTOREP_F: // e.f += f - case OP_SUBSTOREP_V: // e.v += v - return true; - - case OP_LOADA_I: - case OP_LOADA_F: - case OP_LOADA_FLD: - case OP_LOADA_ENT: - case OP_LOADA_S: - case OP_LOADA_FNC: - case OP_LOADA_V: - return false; //DPFIXME: DP does not bounds check these properly. I won't generate them. - - case OP_CONV_ITOF: - case OP_CONV_FTOI: - return true; //these look fine. - - case OP_STOREP_C: // store a char in a string - return false; //DPFIXME: dp's bounds check may give false positives with expected uses. - - case OP_MULSTORE_F: - case OP_MULSTORE_VF: - case OP_MULSTOREP_F: - case OP_MULSTOREP_VF: // e.v *= f - case OP_DIVSTORE_F: - case OP_DIVSTOREP_F: - case OP_STORE_IF: - case OP_STORE_FI: - case OP_STORE_P: - case OP_STOREP_IF: // store a value to a pointer - case OP_STOREP_FI: - case OP_IFNOT_S: - case OP_IF_S: - return true; - - case OP_IFNOT_F: //added, but not in dp yet - case OP_IF_F: - return false; - - case OP_CP_ITOF: - case OP_CP_FTOI: - return false; //DPFIXME: These are not bounds checked at all. - case OP_GLOBALADDRESS: - return true; //DPFIXME: DP will reject these pointers if they are ever used. - case OP_POINTER_ADD: - return true; //just maths. - - case OP_ADD_SF: //(char*)c = (char*)a + (float)b - case OP_SUB_S: //(float)c = (char*)a - (char*)b - return true; - case OP_LOADP_C: //load character from a string - return false; //DPFIXME: DP looks like it'll reject these or wrongly allow. - - case OP_LOADP_I: - case OP_LOADP_F: - case OP_LOADP_FLD: - case OP_LOADP_ENT: - case OP_LOADP_S: - case OP_LOADP_FNC: - case OP_LOADP_V: - return true; - - case OP_XOR_I: - case OP_RSHIFT_I: - case OP_LSHIFT_I: - return true; - - case OP_FETCH_GBL_F: - case OP_FETCH_GBL_S: - case OP_FETCH_GBL_E: - case OP_FETCH_GBL_FNC: - case OP_FETCH_GBL_V: - return false; //DPFIXME: DP will not bounds check this properly, it is too permissive. - case OP_CSTATE: - case OP_CWSTATE: - return false; //DP does not support this hexenc opcode. - case OP_THINKTIME: - return true; //but it does support this one. - - default: //anything I forgot to mention is new. - return false; - } - } - return false; -} - -#define EXPR_WARN_ABOVE_1 2 -#define EXPR_DISALLOW_COMMA 4 -QCC_def_t *QCC_PR_Expression (int priority, int exprflags); -int QCC_AStatementJumpsTo(int targ, int first, int last); -pbool QCC_StatementIsAJump(int stnum, int notifdest); - -temp_t *functemps; //floats/strings/funcs/ents... - -//=========================================================================== - - -/* -============ -PR_Statement - -Emits a primitive statement, returning the var it places it's value in -============ -*/ -QCC_def_t *QCC_PR_Statement ( QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var_b, QCC_dstatement_t **outstatement); -static int QCC_ShouldConvert(QCC_def_t *var, etype_t wanted) -{ - /*no conversion needed*/ - if (var->type->type == wanted) - return 0; - if (var->type->type == ev_integer && wanted == ev_function) - return 0; - if (var->type->type == ev_integer && wanted == ev_pointer) - return 0; - /*stuff needs converting*/ - if (var->type->type == ev_pointer && var->type->aux_type) - { - if (var->type->aux_type->type == ev_float && wanted == ev_integer) - return OP_CP_FTOI; - - if (var->type->aux_type->type == ev_integer && wanted == ev_float) - return OP_CP_ITOF; - } - else - { - if (var->type->type == ev_float && wanted == ev_integer) - return OP_CONV_FTOI; - - if (var->type->type == ev_integer && wanted == ev_float) - return OP_CONV_ITOF; - } - - /*impossible*/ - return -1; -} -QCC_def_t *QCC_SupplyConversion(QCC_def_t *var, etype_t wanted, pbool fatal) -{ - extern char *basictypenames[]; - int o; - - if (pr_classtype && var->type->type == ev_field && wanted != ev_field) - { - if (pr_classtype) - { //load self.var into a temp - QCC_def_t *self; - self = QCC_PR_GetDef(type_entity, "self", NULL, true, 0, false); - switch(wanted) - { - case ev_float: - return QCC_PR_Statement(pr_opcodes+OP_LOAD_F, self, var, NULL); - case ev_string: - return QCC_PR_Statement(pr_opcodes+OP_LOAD_S, self, var, NULL); - case ev_function: - return QCC_PR_Statement(pr_opcodes+OP_LOAD_FNC, self, var, NULL); - case ev_vector: - return QCC_PR_Statement(pr_opcodes+OP_LOAD_V, self, var, NULL); - case ev_entity: - return QCC_PR_Statement(pr_opcodes+OP_LOAD_ENT, self, var, NULL); - default: - QCC_Error(ERR_INTERNAL, "Inexplicit field load failed, try explicit"); - } - } - } - - o = QCC_ShouldConvert(var, wanted); - - if (o == 0) //type already matches - return var; - if (flag_typeexplicit) - QCC_PR_ParseErrorPrintDef(ERR_TYPEMISMATCH, var, "Implicit type mismatch. Needed %s, got %s.", basictypenames[wanted], var->type->name); - if (o < 0) - { - if (fatal) - QCC_PR_ParseErrorPrintDef(ERR_TYPEMISMATCH, var, "Implicit type mismatch. Needed %s, got %s.", basictypenames[wanted], var->type->name); - else - return var; - } - - return QCC_PR_Statement(&pr_opcodes[o], var, NULL, NULL); //conversion return value -} -QCC_def_t *QCC_MakeTranslateStringConst(char *value); -QCC_def_t *QCC_MakeStringConst(char *value); -QCC_def_t *QCC_MakeFloatConst(float value); -QCC_def_t *QCC_MakeIntConst(int value); -QCC_def_t *QCC_MakeVectorConst(float a, float b, float c); - -typedef struct freeoffset_s { - struct freeoffset_s *next; - gofs_t ofs; - unsigned int size; -} freeoffset_t; - -freeoffset_t *freeofs; - -//assistant functions. This can safly be bipassed with the old method for more complex things. -gofs_t QCC_GetFreeOffsetSpace(unsigned int size) -{ - int ofs; - if (opt_locals_marshalling) - { - freeoffset_t *fofs, *prev; - for (fofs = freeofs, prev = NULL; fofs; fofs=fofs->next) - { - if (fofs->size == size) - { - if (prev) - prev->next = fofs->next; - else - freeofs = fofs->next; - - return fofs->ofs; - } - prev = fofs; - } - for (fofs = freeofs, prev = NULL; fofs; fofs=fofs->next) - { - if (fofs->size > size) - { - fofs->size -= size; - fofs->ofs += size; - - return fofs->ofs-size; - } - prev = fofs; - } - } - - ofs = numpr_globals; - numpr_globals+=size; - - if (numpr_globals >= MAX_REGS) - { - if (!opt_overlaptemps || !opt_locals_marshalling) - QCC_Error(ERR_TOOMANYGLOBALS, "numpr_globals exceeded MAX_REGS - you'll need to use more optimisations"); - else - QCC_Error(ERR_TOOMANYGLOBALS, "numpr_globals exceeded MAX_REGS"); - } - - return ofs; -} - -void QCC_FreeOffset(gofs_t ofs, unsigned int size) -{ - freeoffset_t *fofs; - if (ofs+size == numpr_globals) - { //fixme: is this a bug? - numpr_globals -= size; - return; - } - - for (fofs = freeofs; fofs; fofs=fofs->next) - { - //fixme: if this means the last block becomes free, free them all. - if (fofs->ofs == ofs + size) - { - fofs->ofs -= size; - fofs->size += size; - return; - } - if (fofs->ofs+fofs->size == ofs) - { - fofs->size += size; - return; - } - } - - fofs = qccHunkAlloc(sizeof(freeoffset_t)); - fofs->next = freeofs; - fofs->ofs = ofs; - fofs->size = size; - - freeofs = fofs; - return; -} - -static QCC_def_t *QCC_GetTemp(QCC_type_t *type) -{ -//#define CRAZYTEMPOPTS //not worth it. saves 2 temps with hexen2 (without even touching numpr_globals) - QCC_def_t *var_c; - temp_t *t; -#ifdef CRAZYTEMPOPTS - temp_t *best = NULL; -#endif - - var_c = (void *)qccHunkAlloc (sizeof(QCC_def_t)); - memset (var_c, 0, sizeof(QCC_def_t)); - var_c->type = type; - var_c->name = "temp"; - - if (opt_overlaptemps) //don't exceed. This lets us allocate a huge block, and still be able to compile smegging big funcs. - { - for (t = functemps; t; t = t->next) - { - if (!t->used && t->size == type->size) - { -#ifdef CRAZYTEMPOPTS - best = t; - if (t->scope == pr_scope) -#endif - break; - } - } -#ifdef CRAZYTEMPOPTS - t = best; -#endif - if (t && t->scope && t->scope != pr_scope) - QCC_Error(ERR_INTERNAL, "Internal error temp has scope not equal to current scope"); - - if (!t) - { - //allocate a new one - t = qccHunkAlloc(sizeof(temp_t)); - t->size = type->size; - t->next = functemps; - functemps = t; - - t->ofs = QCC_GetFreeOffsetSpace(t->size); - - numtemps+=t->size; - } - else - optres_overlaptemps+=t->size; - //use a previous one. - var_c->ofs = t->ofs; - var_c->temp = t; - t->lastfunc = pr_scope; - } - else if (opt_locals_marshalling) - { - //allocate a new one - t = qccHunkAlloc(sizeof(temp_t)); - t->size = type->size; - - t->next = functemps; - functemps = t; - - t->ofs = QCC_GetFreeOffsetSpace(t->size); - - numtemps+=t->size; - - var_c->ofs = t->ofs; - var_c->temp = t; - t->lastfunc = pr_scope; - } - else - { - // we're not going to reallocate any temps so allocate permanently - var_c->ofs = QCC_GetFreeOffsetSpace(type->size); - numtemps+=type->size; - } - - var_c->s_file = s_file; - var_c->s_line = pr_source_line; - - if (var_c->temp) - var_c->temp->used = true; - - return var_c; -} - -//nothing else references this temp. -static void QCC_FreeTemp(QCC_def_t *t) -{ - if (t && t->temp) - t->temp->used = false; -} - -static void QCC_UnFreeTemp(QCC_def_t *t) -{ - if (t->temp) - t->temp->used = true; -} - -//We've just parsed a statement. -//We can gaurentee that any used temps are now not used. -#ifdef _DEBUG -static void QCC_FreeTemps(void) -{ - temp_t *t; - - if (def_ret.temp && def_ret.temp->used) - { - QCC_PR_ParseWarning(WARN_DEBUGGING, "Return value still in use in %s", pr_scope->name); - def_ret.temp->used = false; - } - - t = functemps; - while(t) - { - if (t->used && !pr_error_count) //don't print this after an error jump out. - QCC_PR_ParseWarning(WARN_DEBUGGING, "Internal: temp(ofs %i) was not released in %s. This implies miscompilation.", t->ofs, pr_scope->name); - t->used = false; - t = t->next; - } -} -#else -#define QCC_FreeTemps() -#endif -void QCC_PurgeTemps(void) -{ - functemps = NULL; -} - -//temps that are still in use over a function call can be considered dodgy. -//we need to remap these to locally defined temps, on return from the function so we know we got them all. -static void QCC_LockActiveTemps(void) -{ - temp_t *t; - - t = functemps; - while(t) - { - if (t->used) - t->scope = pr_scope; - t = t->next; - } - -} - -static void QCC_LockTemp(QCC_def_t *d) -{ - if (d->temp && d->temp->used) - d->temp->scope = pr_scope; -} -static void QCC_ForceLockTempForOffset(int ofs) -{ - temp_t *t; - for (t = functemps; t; t = t->next) - if(t->ofs == ofs /* && t->used */) - t->scope = pr_scope; -} - -static void QCC_RemapLockedTemp(temp_t *t, int firststatement, int laststatement) -{ -#ifdef WRITEASM - char buffer[128]; -#endif - - QCC_def_t *def; - int newofs; - QCC_dstatement_t *st; - int i; - - newofs = 0; - for (i = firststatement, st = &statements[i]; i < laststatement; i++, st++) - { - if (pr_opcodes[st->op].type_a && st->a == t->ofs) - { - if (!newofs) - { - newofs = QCC_GetFreeOffsetSpace(t->size); - numtemps+=t->size; - - def = QCC_PR_DummyDef(type_float, NULL, pr_scope, t->size, newofs, false, false); - def->nextlocal = pr.localvars; - def->constant = false; -#ifdef WRITEASM - sprintf(buffer, "locked_%i", t->ofs); - def->name = qccHunkAlloc(strlen(buffer)+1); - strcpy(def->name, buffer); -#endif - pr.localvars = def; - } - st->a = newofs; - } - if (pr_opcodes[st->op].type_b && st->b == t->ofs) - { - if (!newofs) - { - newofs = QCC_GetFreeOffsetSpace(t->size); - numtemps+=t->size; - - def = QCC_PR_DummyDef(type_float, NULL, pr_scope, t->size, newofs, false, false); - def->nextlocal = pr.localvars; - def->constant = false; -#ifdef WRITEASM - sprintf(buffer, "locked_%i", t->ofs); - def->name = qccHunkAlloc(strlen(buffer)+1); - strcpy(def->name, buffer); -#endif - pr.localvars = def; - } - st->b = newofs; - } - if (pr_opcodes[st->op].type_c && st->c == t->ofs) - { - if (!newofs) - { - newofs = QCC_GetFreeOffsetSpace(t->size); - numtemps+=t->size; - - def = QCC_PR_DummyDef(type_float, NULL, pr_scope, t->size, newofs, false, false); - def->nextlocal = pr.localvars; - def->constant = false; -#ifdef WRITEASM - sprintf(buffer, "locked_%i", t->ofs); - def->name = qccHunkAlloc(strlen(buffer)+1); - strcpy(def->name, buffer); -#endif - pr.localvars = def; - } - st->c = newofs; - } - } -} - -static void QCC_RemapLockedTemps(int firststatement, int laststatement) -{ - temp_t *t; - - t = functemps; - while(t) - { - if (t->scope || opt_locals_marshalling) - { - QCC_RemapLockedTemp(t, firststatement, laststatement); - t->scope = NULL; - t->lastfunc = NULL; - } - t = t->next; - } -} - -static void QCC_fprintfLocals(FILE *f, gofs_t paramstart, gofs_t paramend) -{ - QCC_def_t *var; - temp_t *t; - int i; - - for (var = pr.localvars; var; var = var->nextlocal) - { - if (var->ofs >= paramstart && var->ofs < paramend) - continue; - if (var->arraysize) - fprintf(f, "local %s %s[%i];\n", TypeName(var->type), var->name, var->arraysize); - else - fprintf(f, "local %s %s;\n", TypeName(var->type), var->name); - } - - for (t = functemps, i = 0; t; t = t->next, i++) - { - if (t->lastfunc == pr_scope) - { - fprintf(f, "local %s temp_%i;\n", (t->size == 1)?"float":"vector", i); - } - } -} - -#ifdef WRITEASM -void QCC_WriteAsmFunction(QCC_def_t *sc, unsigned int firststatement, gofs_t firstparm); -static const char *QCC_VarAtOffset(unsigned int ofs, unsigned int size) -{ - static char message[1024]; - QCC_def_t *var; - //check the temps - temp_t *t; - int i; - - for (t = functemps, i = 0; t; t = t->next, i++) - { - if (ofs >= t->ofs && ofs < t->ofs + t->size) - { - if (size < t->size) - sprintf(message, "temp_%i_%c", t->ofs, 'x' + (ofs-t->ofs)%3); - else - sprintf(message, "temp_%i", t->ofs); - return message; - } - } - - for (var = pr.localvars; var; var = var->nextlocal) - { - if (var->scope && var->scope != pr_scope) - continue; //this should be an error - if (ofs >= var->ofs && ofs < var->ofs + var->type->size) - { - if (*var->name) - { - if (!STRCMP(var->name, "IMMEDIATE")) //continue, don't get bogged down by multiple bits of code - continue; - if (size < var->type->size) - sprintf(message, "%s_%c", var->name, 'x' + (ofs-var->ofs)%3); - else - sprintf(message, "%s", var->name); - return message; - } - } - } - - for (var = pr.def_head.next; var; var = var->next) - { - if (var->scope && var->scope != pr_scope) - continue; - - if (ofs >= var->ofs && ofs < var->ofs + var->type->size) - { - if (*var->name) - { - if (!STRCMP(var->name, "IMMEDIATE")) - { - switch(var->type->type) - { - case ev_string: - sprintf(message, "\"%.1020s\"", &strings[((int *)qcc_pr_globals)[var->ofs]]); - return message; - case ev_integer: - sprintf(message, "%i", ((int *)qcc_pr_globals)[var->ofs]); - return message; - case ev_float: - sprintf(message, "%f", qcc_pr_globals[var->ofs]); - return message; - case ev_vector: - sprintf(message, "'%f %f %f'", qcc_pr_globals[var->ofs], qcc_pr_globals[var->ofs+1], qcc_pr_globals[var->ofs+2]); - return message; - default: - sprintf(message, "IMMEDIATE"); - return message; - } - } - if (size < var->type->size) - sprintf(message, "%s_%c", var->name, 'x' + (ofs-var->ofs)%3); - else - sprintf(message, "%s", var->name); - return message; - } - } - } - - if (size >= 3) - { - if (ofs >= OFS_RETURN && ofs < OFS_PARM0) - sprintf(message, "return"); - else if (ofs >= OFS_PARM0 && ofs < RESERVED_OFS) - sprintf(message, "parm%i", (ofs-OFS_PARM0)/3); - else - sprintf(message, "offset_%i", ofs); - } - else - { - if (ofs >= OFS_RETURN && ofs < OFS_PARM0) - sprintf(message, "return_%c", 'x' + ofs-OFS_RETURN); - else if (ofs >= OFS_PARM0 && ofs < RESERVED_OFS) - sprintf(message, "parm%i_%c", (ofs-OFS_PARM0)/3, 'x' + (ofs-OFS_PARM0)%3); - else - sprintf(message, "offset_%i", ofs); - } - return message; -} -#endif - -QCC_dstatement_t *QCC_PR_SimpleStatement( int op, int var_a, int var_b, int var_c, int force); - -QCC_def_t *QCC_PR_Statement (QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var_b, QCC_dstatement_t **outstatement) -{ - QCC_dstatement_t *statement; - QCC_def_t *var_c=NULL, *temp=NULL; - - if (outstatement == (QCC_dstatement_t **)0xffffffff) - outstatement = NULL; - else if (op->priority != -1 && op->priority != CONDITION_PRIORITY) - { - if (op->associative!=ASSOC_LEFT) - { - if (op->type_a != &type_pointer) - var_b = QCC_SupplyConversion(var_b, (*op->type_a)->type, false); - } - else - { - if (var_a) - var_a = QCC_SupplyConversion(var_a, (*op->type_a)->type, false); - if (var_b) - var_b = QCC_SupplyConversion(var_b, (*op->type_b)->type, false); - } - } - - if (var_a) - { - var_a->references++; - QCC_FreeTemp(var_a); - } - if (var_b) - { - var_b->references++; - QCC_FreeTemp(var_b); - } - - if (keyword_class && var_a && var_b) - { - if (var_a->type->type == ev_entity && var_b->type->type == ev_entity) - if (var_a->type != var_b->type) - if (strcmp(var_a->type->name, var_b->type->name)) - QCC_PR_ParseWarning(0, "Implicit cast from '%s' to '%s'", var_a->type->name, var_b->type->name); - } - - //maths operators - if (opt_constantarithmatic) - { - if (var_a && var_a->constant) - { - if (var_b && var_b->constant) - { - //both are constants - switch (op - pr_opcodes) //improve some of the maths. - { - case OP_LOADA_F: - case OP_LOADA_V: - case OP_LOADA_S: - case OP_LOADA_ENT: - case OP_LOADA_FLD: - case OP_LOADA_FNC: - case OP_LOADA_I: - { - QCC_def_t *nd; - nd = (void *)qccHunkAlloc (sizeof(QCC_def_t)); - memset (nd, 0, sizeof(QCC_def_t)); - nd->type = var_a->type; - nd->ofs = var_a->ofs + G_INT(var_b->ofs); - nd->temp = var_a->temp; - nd->constant = true; - nd->name = var_a->name; - return nd; - } - break; - case OP_BITOR_F: - optres_constantarithmatic++; - return QCC_MakeFloatConst((float)((int)G_FLOAT(var_a->ofs) | (int)G_FLOAT(var_b->ofs))); - case OP_BITAND_F: - optres_constantarithmatic++; - return QCC_MakeFloatConst((float)((int)G_FLOAT(var_a->ofs) & (int)G_FLOAT(var_b->ofs))); - case OP_MUL_F: - optres_constantarithmatic++; - return QCC_MakeFloatConst(G_FLOAT(var_a->ofs) * G_FLOAT(var_b->ofs)); - case OP_DIV_F: - optres_constantarithmatic++; - return QCC_MakeFloatConst(G_FLOAT(var_a->ofs) / G_FLOAT(var_b->ofs)); - case OP_ADD_F: - optres_constantarithmatic++; - return QCC_MakeFloatConst(G_FLOAT(var_a->ofs) + G_FLOAT(var_b->ofs)); - case OP_SUB_F: - optres_constantarithmatic++; - return QCC_MakeFloatConst(G_FLOAT(var_a->ofs) - G_FLOAT(var_b->ofs)); - - case OP_BITOR_I: - optres_constantarithmatic++; - return QCC_MakeIntConst(G_INT(var_a->ofs) | G_INT(var_b->ofs)); - case OP_BITAND_I: - optres_constantarithmatic++; - return QCC_MakeIntConst(G_INT(var_a->ofs) & G_INT(var_b->ofs)); - case OP_MUL_I: - optres_constantarithmatic++; - return QCC_MakeIntConst(G_INT(var_a->ofs) * G_INT(var_b->ofs)); - case OP_DIV_I: - optres_constantarithmatic++; - return QCC_MakeIntConst(G_INT(var_a->ofs) / G_INT(var_b->ofs)); - case OP_ADD_I: - optres_constantarithmatic++; - return QCC_MakeIntConst(G_INT(var_a->ofs) + G_INT(var_b->ofs)); - case OP_SUB_I: - optres_constantarithmatic++; - return QCC_MakeIntConst(G_INT(var_a->ofs) - G_INT(var_b->ofs)); - - case OP_AND_F: - optres_constantarithmatic++; - return QCC_MakeIntConst(G_INT(var_a->ofs) && G_INT(var_b->ofs)); - case OP_OR_F: - optres_constantarithmatic++; - return QCC_MakeIntConst(G_INT(var_a->ofs) || G_INT(var_b->ofs)); - case OP_MUL_V: //mul_f is actually a dot-product - optres_constantarithmatic++; - return QCC_MakeFloatConst( G_FLOAT(var_a->ofs) * G_FLOAT(var_b->ofs+0) + - G_FLOAT(var_a->ofs) * G_FLOAT(var_b->ofs+1) + - G_FLOAT(var_a->ofs) * G_FLOAT(var_b->ofs+2)); - case OP_MUL_FV: - optres_constantarithmatic++; - return QCC_MakeVectorConst( G_FLOAT(var_a->ofs) * G_FLOAT(var_b->ofs+0), - G_FLOAT(var_a->ofs) * G_FLOAT(var_b->ofs+1), - G_FLOAT(var_a->ofs) * G_FLOAT(var_b->ofs+2)); - case OP_MUL_VF: - optres_constantarithmatic++; - return QCC_MakeVectorConst( G_FLOAT(var_a->ofs+0) * G_FLOAT(var_b->ofs), - G_FLOAT(var_a->ofs+1) * G_FLOAT(var_b->ofs), - G_FLOAT(var_a->ofs+2) * G_FLOAT(var_b->ofs)); - case OP_ADD_V: - optres_constantarithmatic++; - return QCC_MakeVectorConst( G_FLOAT(var_a->ofs+0) + G_FLOAT(var_b->ofs+0), - G_FLOAT(var_a->ofs+1) + G_FLOAT(var_b->ofs+1), - G_FLOAT(var_a->ofs+2) + G_FLOAT(var_b->ofs+2)); - case OP_SUB_V: - optres_constantarithmatic++; - return QCC_MakeVectorConst( G_FLOAT(var_a->ofs+0) - G_FLOAT(var_b->ofs+0), - G_FLOAT(var_a->ofs+1) - G_FLOAT(var_b->ofs+1), - G_FLOAT(var_a->ofs+2) - G_FLOAT(var_b->ofs+2)); - } - } - else - { - //a is const, b is not - switch (op - pr_opcodes) - { - case OP_CONV_FTOI: - optres_constantarithmatic++; - return QCC_MakeIntConst(G_FLOAT(var_a->ofs)); - case OP_CONV_ITOF: - optres_constantarithmatic++; - return QCC_MakeFloatConst(G_INT(var_a->ofs)); - case OP_BITOR_F: - case OP_OR_F: - case OP_ADD_F: - if (G_FLOAT(var_a->ofs) == 0) - { - optres_constantarithmatic++; - QCC_UnFreeTemp(var_b); - return var_b; - } - break; - case OP_MUL_F: - if (G_FLOAT(var_a->ofs) == 1) - { - optres_constantarithmatic++; - QCC_UnFreeTemp(var_b); - return var_b; - } - break; - case OP_BITAND_F: - case OP_AND_F: - if (G_FLOAT(var_a->ofs) != 0) - { - optres_constantarithmatic++; - QCC_UnFreeTemp(var_b); - return var_b; - } - break; - - case OP_BITOR_I: - case OP_OR_I: - case OP_ADD_I: - if (G_INT(var_a->ofs) == 0) - { - optres_constantarithmatic++; - QCC_UnFreeTemp(var_b); - return var_b; - } - break; - case OP_MUL_I: - if (G_INT(var_a->ofs) == 1) - { - optres_constantarithmatic++; - QCC_UnFreeTemp(var_b); - return var_b; - } - break; - case OP_BITAND_I: - case OP_AND_I: - if (G_INT(var_a->ofs) != 0) - { - optres_constantarithmatic++; - QCC_UnFreeTemp(var_b); - return var_b; - } - break; - } - } - } - else if (var_b && var_b->constant) - { - //b is const, a is not - switch (op - pr_opcodes) - { - case OP_LOADA_F: - case OP_LOADA_V: - case OP_LOADA_S: - case OP_LOADA_ENT: - case OP_LOADA_FLD: - case OP_LOADA_FNC: - case OP_LOADA_I: - { - QCC_def_t *nd; - nd = (void *)qccHunkAlloc (sizeof(QCC_def_t)); - memset (nd, 0, sizeof(QCC_def_t)); - nd->type = var_a->type; - nd->ofs = var_a->ofs + G_INT(var_b->ofs); - nd->temp = var_a->temp; - nd->constant = false; - nd->name = var_a->name; - return nd; - } - break; - case OP_BITOR_F: - case OP_OR_F: - case OP_SUB_F: - case OP_ADD_F: - if (G_FLOAT(var_b->ofs) == 0) - { - optres_constantarithmatic++; - QCC_UnFreeTemp(var_a); - return var_a; - } - break; - case OP_DIV_F: - case OP_MUL_F: - if (G_FLOAT(var_b->ofs) == 1) - { - optres_constantarithmatic++; - QCC_UnFreeTemp(var_a); - return var_a; - } - break; - //no bitand_f, I don't trust the casts - case OP_AND_F: - if (G_FLOAT(var_b->ofs) != 0) - { - optres_constantarithmatic++; - QCC_UnFreeTemp(var_a); - return var_a; - } - break; - - case OP_BITOR_I: - case OP_OR_I: - case OP_SUB_I: - case OP_ADD_I: - if (G_INT(var_b->ofs) == 0) - { - optres_constantarithmatic++; - QCC_UnFreeTemp(var_a); - return var_a; - } - break; - case OP_DIV_I: - case OP_MUL_I: - if (G_INT(var_b->ofs) == 1) - { - optres_constantarithmatic++; - QCC_UnFreeTemp(var_a); - return var_a; - } - break; - case OP_BITAND_I: - if (G_INT(var_b->ofs) == 0xffffffff) - { - optres_constantarithmatic++; - QCC_UnFreeTemp(var_a); - return var_a; - } - case OP_AND_I: - if (G_INT(var_b->ofs) == 0) - { - optres_constantarithmatic++; - QCC_UnFreeTemp(var_a); - return var_a; - } - break; - } - } - } - - switch (op - pr_opcodes) - { - case OP_LOADA_F: - case OP_LOADA_V: - case OP_LOADA_S: - case OP_LOADA_ENT: - case OP_LOADA_FLD: - case OP_LOADA_FNC: - case OP_LOADA_I: - break; - case OP_AND_F: - if (var_a->ofs == var_b->ofs) - QCC_PR_ParseWarning(WARN_CONSTANTCOMPARISON, "Parameter offsets for && are the same"); - if (var_a->constant || var_b->constant) - QCC_PR_ParseWarning(WARN_CONSTANTCOMPARISON, "Result of comparison is constant"); - break; - case OP_OR_F: - if (var_a->ofs == var_b->ofs) - QCC_PR_ParseWarning(WARN_CONSTANTCOMPARISON, "Parameters for || are the same"); - if (var_a->constant || var_b->constant) - QCC_PR_ParseWarning(WARN_CONSTANTCOMPARISON, "Result of comparison is constant"); - break; - case OP_EQ_F: - case OP_EQ_S: - case OP_EQ_E: - case OP_EQ_FNC: -// if (opt_shortenifnots) -// if (var_b->constant && ((int*)qcc_pr_globals)[var_b->ofs]==0) // (a == 0) becomes (!a) -// op = &pr_opcodes[(op - pr_opcodes) - OP_EQ_F + OP_NOT_F]; - case OP_EQ_V: - - case OP_NE_F: - case OP_NE_V: - case OP_NE_S: - case OP_NE_E: - case OP_NE_FNC: - - case OP_LE_F: - case OP_GE_F: - case OP_LT_F: - case OP_GT_F: - if ((var_a->constant && var_b->constant && !var_a->temp && !var_b->temp) || var_a->ofs == var_b->ofs) - QCC_PR_ParseWarning(WARN_CONSTANTCOMPARISON, "Result of comparison is constant"); - break; - case OP_IF_S: - case OP_IFNOT_S: - case OP_IF_F: - case OP_IFNOT_F: - case OP_IF_I: - case OP_IFNOT_I: -// if (var_a->type->type == ev_function && !var_a->temp) -// QCC_PR_ParseWarning(WARN_CONSTANTCOMPARISON, "Result of comparison is constant"); - if (var_a->constant && !var_a->temp) - QCC_PR_ParseWarning(WARN_CONSTANTCOMPARISON, "Result of comparison is constant"); - break; - default: - break; - } - - if (numstatements) - { //optimise based on last statement. - if (op - pr_opcodes == OP_IFNOT_I) - { - if (opt_shortenifnots && var_a && (statements[numstatements-1].op == OP_NOT_F || statements[numstatements-1].op == OP_NOT_FNC || statements[numstatements-1].op == OP_NOT_ENT)) - { - if (statements[numstatements-1].c == var_a->ofs) - { - static QCC_def_t nvara; - if (statements[numstatements-1].op == OP_NOT_F) - op = &pr_opcodes[OP_IF_F]; - else - op = &pr_opcodes[OP_IF_I]; - numstatements--; - QCC_FreeTemp(var_a); - memcpy(&nvara, var_a, sizeof(nvara)); - nvara.ofs = statements[numstatements].a; - var_a = &nvara; - - optres_shortenifnots++; - } - } - } - else if (op - pr_opcodes == OP_IFNOT_F) - { - if (opt_shortenifnots && var_a && statements[numstatements-1].op == OP_NOT_F) - { - if (statements[numstatements-1].c == var_a->ofs) - { - static QCC_def_t nvara; - op = &pr_opcodes[OP_IF_F]; - numstatements--; - QCC_FreeTemp(var_a); - memcpy(&nvara, var_a, sizeof(nvara)); - nvara.ofs = statements[numstatements].a; - var_a = &nvara; - - optres_shortenifnots++; - } - } - } - else if (op - pr_opcodes == OP_IFNOT_S) - { - if (opt_shortenifnots && var_a && statements[numstatements-1].op == OP_NOT_S) - { - if (statements[numstatements-1].c == var_a->ofs) - { - static QCC_def_t nvara; - op = &pr_opcodes[OP_IF_S]; - numstatements--; - QCC_FreeTemp(var_a); - memcpy(&nvara, var_a, sizeof(nvara)); - nvara.ofs = statements[numstatements].a; - var_a = &nvara; - - optres_shortenifnots++; - } - } - } - else if (((unsigned) ((op - pr_opcodes) - OP_STORE_F) < 6) || (op-pr_opcodes) == OP_STORE_P) - { - // remove assignments if what should be assigned is the 3rd operand of the previous statement? - // don't if it's a call, callH, switch or case - // && var_a->ofs >RESERVED_OFS) - if (OpAssignsToC(statements[numstatements-1].op) && - opt_assignments && var_a && var_a->ofs == statements[numstatements-1].c) - { - if (var_a->type->type == var_b->type->type) - { - if (var_a->temp) - { - statement = &statements[numstatements-1]; - statement->c = var_b->ofs; - - if (var_a->type->type != var_b->type->type) - QCC_PR_ParseWarning(0, "store type mismatch"); - var_b->references++; - var_a->references--; - QCC_FreeTemp(var_a); - optres_assignments++; - - simplestore=true; - - QCC_UnFreeTemp(var_b); - return var_b; - } - } - } - } - } - simplestore=false; - - statement = &statements[numstatements]; - numstatements++; - - if (!QCC_OPCodeValid(op)) - { - switch(op - pr_opcodes) - { - case OP_LOADA_STRUCT: - /*emit this anyway. if it reaches runtime then you messed up. - this is valid only if you do &foo[0]*/ - break; - - - case OP_IF_S: - var_c = QCC_PR_GetDef(type_string, "string_null", NULL, true, 0, false); - numstatements--; - var_a = QCC_PR_Statement(&pr_opcodes[OP_NE_S], var_a, var_c, NULL); - statement = &statements[numstatements]; - numstatements++; - - QCC_FreeTemp(var_a); - op = &pr_opcodes[OP_IF_I]; - break; - - case OP_IFNOT_S: - var_c = QCC_PR_GetDef(type_string, "string_null", NULL, true, 0, false); - numstatements--; - var_a = QCC_PR_Statement(&pr_opcodes[OP_NE_S], var_a, var_c, NULL); - statement = &statements[numstatements]; - numstatements++; - - QCC_FreeTemp(var_a); - op = &pr_opcodes[OP_IFNOT_I]; - break; - - case OP_IF_F: - var_c = QCC_MakeFloatConst(0); - numstatements--; - var_a = QCC_PR_Statement(&pr_opcodes[OP_NE_F], var_a, var_c, NULL); - statement = &statements[numstatements]; - numstatements++; - - QCC_FreeTemp(var_a); - op = &pr_opcodes[OP_IF_I]; - break; - - case OP_IFNOT_F: - var_c = QCC_MakeFloatConst(0); - numstatements--; - var_a = QCC_PR_Statement(&pr_opcodes[OP_NE_F], var_a, var_c, NULL); - statement = &statements[numstatements]; - numstatements++; - - QCC_FreeTemp(var_a); - op = &pr_opcodes[OP_IFNOT_I]; - break; - - case OP_ADDSTORE_F: - op = &pr_opcodes[OP_ADD_F]; - var_c = var_b; - var_b = var_a; - var_a = var_c; - var_c = var_a; - break; - case OP_ADDSTORE_I: - op = &pr_opcodes[OP_ADD_I]; - var_c = var_b; - var_b = var_a; - var_a = var_c; - var_c = var_a; - break; - case OP_ADDSTORE_FI: - op = &pr_opcodes[OP_ADD_FI]; - var_c = var_b; - var_b = var_a; - var_a = var_c; - var_c = var_a; - break; - case OP_ADDSTORE_IF: - op = &pr_opcodes[OP_ADD_IF]; - var_c = var_b; - var_b = var_a; - var_a = var_c; - var_c = var_a; - break; - - case OP_SUBSTORE_F: - op = &pr_opcodes[OP_SUB_F]; - var_c = var_b; - var_b = var_a; - var_a = var_c; - var_c = var_a; - break; - case OP_SUBSTORE_FI: - op = &pr_opcodes[OP_SUB_FI]; - var_c = var_b; - var_b = var_a; - var_a = var_c; - var_c = var_a; - break; - case OP_SUBSTORE_IF: - op = &pr_opcodes[OP_SUB_IF]; - var_c = var_b; - var_b = var_a; - var_a = var_c; - var_c = var_a; - break; - - case OP_DIVSTORE_F: - op = &pr_opcodes[OP_DIV_F]; - var_c = var_b; - var_b = var_a; - var_a = var_c; - var_c = var_a; - break; - case OP_DIVSTORE_FI: - op = &pr_opcodes[OP_DIV_FI]; - var_c = var_b; - var_b = var_a; - var_a = var_c; - var_c = var_a; - break; - case OP_DIVSTORE_IF: - op = &pr_opcodes[OP_DIV_IF]; - var_c = var_b; - var_b = var_a; - var_a = var_c; - var_c = var_a; - break; - - case OP_MULSTORE_F: - op = &pr_opcodes[OP_MUL_F]; - var_c = var_b; - var_b = var_a; - var_a = var_c; - var_c = var_a; - break; - case OP_MULSTORE_IF: - op = &pr_opcodes[OP_MUL_IF]; - var_c = var_b; - var_b = var_a; - var_a = var_c; - var_c = var_a; - break; - case OP_MULSTORE_FI: - op = &pr_opcodes[OP_MUL_FI]; - var_c = var_b; - var_b = var_a; - var_a = var_c; - var_c = var_a; - break; - - case OP_ADDSTORE_V: - op = &pr_opcodes[OP_ADD_V]; - var_c = var_b; - var_b = var_a; - var_a = var_c; - var_c = var_a; - break; - - case OP_SUBSTORE_V: - op = &pr_opcodes[OP_SUB_V]; - var_c = var_b; - var_b = var_a; - var_a = var_c; - var_c = var_a; - break; - - case OP_MULSTORE_VF: - op = &pr_opcodes[OP_MUL_VF]; - var_c = var_b; - var_b = var_a; - var_a = var_c; - var_c = var_a; - break; - case OP_MULSTORE_VI: - op = &pr_opcodes[OP_MUL_VI]; - var_c = var_b; - var_b = var_a; - var_a = var_c; - var_c = var_a; - break; - - case OP_BITSET_I: - op = &pr_opcodes[OP_BITOR_I]; - var_c = var_b; - var_b = var_a; - var_a = var_c; - var_c = var_a; - break; - case OP_BITSET: - op = &pr_opcodes[OP_BITOR_F]; - var_c = var_b; - var_b = var_a; - var_a = var_c; - var_c = var_a; - break; - - case OP_STOREP_P: - op = &pr_opcodes[OP_STOREP_I]; - break; - - case OP_BITCLR: - //b = var, a = bit field. - - QCC_UnFreeTemp(var_a); - QCC_UnFreeTemp(var_b); - - numstatements--; - var_c = QCC_PR_Statement(&pr_opcodes[OP_BITAND_F], var_b, var_a, NULL); - QCC_FreeTemp(var_c); - statement = &statements[numstatements]; - numstatements++; - - QCC_FreeTemp(var_a); - QCC_FreeTemp(var_b); - - op = &pr_opcodes[OP_SUB_F]; - var_a = var_b; - var_b = var_c; - var_c = var_a; - break; - - case OP_SUBSTOREP_FI: - case OP_SUBSTOREP_IF: - case OP_ADDSTOREP_FI: - case OP_ADDSTOREP_IF: - case OP_MULSTOREP_FI: - case OP_MULSTOREP_IF: - case OP_DIVSTOREP_FI: - case OP_DIVSTOREP_IF: - - case OP_MULSTOREP_VF: - case OP_MULSTOREP_VI: - case OP_SUBSTOREP_V: - case OP_ADDSTOREP_V: - - case OP_SUBSTOREP_F: - case OP_SUBSTOREP_I: - case OP_ADDSTOREP_I: - case OP_ADDSTOREP_F: - case OP_MULSTOREP_F: - case OP_DIVSTOREP_F: - case OP_BITSETP: - case OP_BITSETP_I: - case OP_BITCLRP: -// QCC_PR_ParseWarning(0, "XSTOREP_F emulation is still experimental"); - QCC_UnFreeTemp(var_a); - QCC_UnFreeTemp(var_b); - //don't chain these... this expansion is not the same. - { - int st; - int need_lock = false; - if (var_b->temp) - { - for (st = numstatements-2; st>=0; st--) - { - if (statements[st].op == OP_ADDRESS) - if (statements[st].c == var_b->ofs) - break; - - if ((statements[st].op >= OP_CALL0 && statements[st].op <= OP_CALL8) || (statements[st].op >= OP_CALL1H && statements[st].op <= OP_CALL8H)) - need_lock = true; - - if (statements[st].c == var_b->ofs) - { - st = -1; - break; - } - } - } - else - st = -1; - - var_c = QCC_GetTemp(*op->type_c); - if (st < 0) - { - /*generate new OP_LOADP instruction*/ - statement->op = ((*op->type_c)->type==ev_vector)?OP_LOADP_V:OP_LOADP_F; - statement->a = var_b->ofs; - statement->b = var_c->ofs; - statement->c = 0; - } - else - { - /*it came from an OP_ADDRESS - st says the instruction*/ - if (need_lock) - { - QCC_ForceLockTempForOffset(statements[st].a); - QCC_ForceLockTempForOffset(statements[st].b); - QCC_LockTemp(var_c); /*that temp needs to be preserved over calls*/ - } - - /*generate new OP_ADDRESS instruction - FIXME: the arguments may have changed since the original instruction*/ - statement_linenums[statement-statements] = statement_linenums[st]; - statement->op = OP_ADDRESS; - statement->a = statements[st].a; - statement->b = statements[st].b; - statement->c = statements[st].c; - - /*convert old one to an OP_LOAD*/ - statement_linenums[st] = pr_source_line; - statements[st].op = ((*op->type_c)->type==ev_vector)?OP_LOAD_V:OP_LOAD_F; - statements[st].a = statements[st].a; - statements[st].b = statements[st].b; - statements[st].c = var_c->ofs; - } - } - - statement = &statements[numstatements]; - numstatements++; - - statement_linenums[statement-statements] = pr_source_line; - switch(op - pr_opcodes) - { - case OP_SUBSTOREP_V: - statement->op = OP_SUB_V; - break; - case OP_ADDSTOREP_V: - statement->op = OP_ADD_V; - break; - case OP_MULSTOREP_VF: - statement->op = OP_MUL_VF; - break; - case OP_MULSTOREP_VI: - statement->op = OP_MUL_VI; - break; - case OP_SUBSTOREP_F: - statement->op = OP_SUB_F; - break; - case OP_SUBSTOREP_I: - statement->op = OP_SUB_I; - break; - case OP_SUBSTOREP_IF: - statement->op = OP_SUB_IF; - break; - case OP_SUBSTOREP_FI: - statement->op = OP_SUB_FI; - break; - case OP_ADDSTOREP_IF: - statement->op = OP_ADD_IF; - break; - case OP_ADDSTOREP_FI: - statement->op = OP_ADD_FI; - break; - case OP_MULSTOREP_IF: - statement->op = OP_MUL_IF; - break; - case OP_MULSTOREP_FI: - statement->op = OP_MUL_FI; - break; - case OP_DIVSTOREP_IF: - statement->op = OP_DIV_IF; - break; - case OP_DIVSTOREP_FI: - statement->op = OP_DIV_FI; - break; - case OP_ADDSTOREP_F: - statement->op = OP_ADD_F; - break; - case OP_ADDSTOREP_I: - statement->op = OP_ADD_I; - break; - case OP_MULSTOREP_F: - statement->op = OP_MUL_F; - break; - case OP_DIVSTOREP_F: - statement->op = OP_DIV_F; - break; - case OP_BITSETP: - statement->op = OP_BITOR_F; - break; - case OP_BITSETP_I: - statement->op = OP_BITOR_I; - break; - case OP_BITCLRP: - //float pointer float - temp = QCC_GetTemp(type_float); - statement->op = OP_BITAND_F; - statement->a = var_c ? var_c->ofs : 0; - statement->b = var_a ? var_a->ofs : 0; - statement->c = temp->ofs; - - statement = &statements[numstatements]; - numstatements++; - - statement_linenums[statement-statements] = pr_source_line; - statement->op = OP_SUB_F; - - //t = c & i - //c = c - t - break; - default: //no way will this be hit... - QCC_PR_ParseError(ERR_INTERNAL, "opcode invalid 3 times %i", op - pr_opcodes); - } - if (op - pr_opcodes == OP_BITCLRP) - { - statement->a = var_c ? var_c->ofs : 0; - statement->b = temp ? temp->ofs : 0; - statement->c = var_c->ofs; - QCC_FreeTemp(temp); - var_b = var_b; //this is the ptr. - QCC_FreeTemp(var_a); - var_a = var_c; //this is the value. - } - else - { - statement->a = var_c ? var_c->ofs : 0; - statement->b = var_a ? var_a->ofs : 0; - statement->c = var_c->ofs; - var_b = var_b; //this is the ptr. - QCC_FreeTemp(var_a); - var_a = var_c; //this is the value. - } - - op = &pr_opcodes[((*op->type_c)->type==ev_vector)?OP_STOREP_V:OP_STOREP_F]; - QCC_FreeTemp(var_c); - var_c = NULL; - QCC_FreeTemp(var_b); - - statement = &statements[numstatements]; - numstatements++; - break; - - default: - QCC_PR_ParseError(ERR_BADEXTENSION, "Opcode \"%s|%s\" not valid for target", op->name, op->opname); - break; - } - } - - if (outstatement) - *outstatement = statement; - - statement_linenums[statement-statements] = pr_source_line; - statement->op = op - pr_opcodes; - statement->a = var_a ? var_a->ofs : 0; - statement->b = var_b ? var_b->ofs : 0; - if (var_c != NULL) - { - statement->c = var_c->ofs; - } - else if (op->type_c == &type_void || op->associative==ASSOC_RIGHT || op->type_c == NULL) - { - var_c = NULL; - statement->c = 0; // ifs, gotos, and assignments - // don't need vars allocated - } - else - { // allocate result space - var_c = QCC_GetTemp(*op->type_c); - statement->c = var_c->ofs; - if (op->type_b == &type_field) - { - var_c->name = var_b->name; - var_c->s_file = var_b->s_file; - var_c->s_line = var_b->s_line; - } - } - - if ((op - pr_opcodes >= OP_LOAD_F && op - pr_opcodes <= OP_LOAD_FNC) || - op - pr_opcodes == OP_LOAD_I) - { - if (var_b->constant == 2) - var_c->constant = true; - } - - if (!var_c) - { - if (var_a) - QCC_UnFreeTemp(var_a); - return var_a; - } - return var_c; -} - -/* -============ -QCC_PR_SimpleStatement - -Emits a primitive statement, returning the var it places it's value in -============ -*/ -QCC_dstatement_t *QCC_PR_SimpleStatement( int op, int var_a, int var_b, int var_c, int force) -{ - QCC_dstatement_t *statement; - - if (!force && !QCC_OPCodeValid(pr_opcodes+op)) - { - QCC_PR_ParseError(ERR_BADEXTENSION, "Opcode \"%s|%s\" not valid for target\n", pr_opcodes[op].name, pr_opcodes[op].opname); - } - - statement_linenums[numstatements] = pr_source_line; - statement = &statements[numstatements]; - - numstatements++; - statement->op = op; - statement->a = var_a; - statement->b = var_b; - statement->c = var_c; - return statement; -} - -void QCC_PR_Statement3 ( QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var_b, QCC_def_t *var_c, int force) -{ - QCC_dstatement_t *statement; - - if (!force && !QCC_OPCodeValid(op)) - { -// outputversion = op->extension; -// if (noextensions) - QCC_PR_ParseError(ERR_BADEXTENSION, "Opcode \"%s|%s\" not valid for target\n", op->name, op->opname); - } - - statement = &statements[numstatements]; - numstatements++; - - statement_linenums[statement-statements] = pr_source_line; - statement->op = op - pr_opcodes; - statement->a = var_a ? var_a->ofs : 0; - statement->b = var_b ? var_b->ofs : 0; - statement->c = var_c ? var_c->ofs : 0; -} - -/* -============ -PR_ParseImmediate - -Looks for a preexisting constant -============ -*/ -QCC_def_t *QCC_PR_ParseImmediate (void) -{ - QCC_def_t *cn; - - if (pr_immediate_type == type_float) - { - cn = QCC_MakeFloatConst(pr_immediate._float); - QCC_PR_Lex (); - return cn; - } - if (pr_immediate_type == type_integer) - { - cn = QCC_MakeIntConst(pr_immediate._int); - QCC_PR_Lex (); - return cn; - } - - if (pr_immediate_type == type_string) - { - char tmp[8192]; - strcpy(tmp, pr_immediate_string); - - for(;;) - { - QCC_PR_Lex (); - if (pr_token_type == tt_immediate && pr_token_type == tt_immediate) - strcat(tmp, pr_immediate_string); - else - break; - } - - cn = QCC_MakeStringConst(tmp); - return cn; - } - -// check for a constant with the same value - for (cn=pr.def_head.next ; cn ; cn=cn->next) //FIXME - hashtable. - { - if (!cn->initialized) - continue; - if (!cn->constant) - continue; - if (cn->type != pr_immediate_type) - continue; - if (pr_immediate_type == type_string) - { - if (!STRCMP(G_STRING(cn->ofs), pr_immediate_string) ) - { - QCC_PR_Lex (); - return cn; - } - } - else if (pr_immediate_type == type_float) - { - if ( G_FLOAT(cn->ofs) == pr_immediate._float ) - { - QCC_PR_Lex (); - return cn; - } - } - else if (pr_immediate_type == type_integer) - { - if ( G_INT(cn->ofs) == pr_immediate._int ) - { - QCC_PR_Lex (); - return cn; - } - } - else if (pr_immediate_type == type_vector) - { - if ( ( G_FLOAT(cn->ofs) == pr_immediate.vector[0] ) - && ( G_FLOAT(cn->ofs+1) == pr_immediate.vector[1] ) - && ( G_FLOAT(cn->ofs+2) == pr_immediate.vector[2] ) ) - { - QCC_PR_Lex (); - return cn; - } - } - else - QCC_PR_ParseError (ERR_BADIMMEDIATETYPE, "weird immediate type"); - } - -// allocate a new one - cn = (void *)qccHunkAlloc (sizeof(QCC_def_t)); - cn->next = NULL; - pr.def_tail->next = cn; - pr.def_tail = cn; - - cn->type = pr_immediate_type; - cn->name = "IMMEDIATE"; - cn->constant = true; - cn->initialized = 1; - cn->scope = NULL; // always share immediates - -// copy the immediate to the global area - cn->ofs = QCC_GetFreeOffsetSpace(type_size[pr_immediate_type->type]); - - if (pr_immediate_type == type_string) - pr_immediate.string = QCC_CopyString (pr_immediate_string); - - memcpy (qcc_pr_globals + cn->ofs, &pr_immediate, 4*type_size[pr_immediate_type->type]); - - QCC_PR_Lex (); - - return cn; -} - - -void QCC_PrecacheSound (QCC_def_t *e, int ch) -{ - char *n; - int i; - - if (e->type->type != ev_string) - return; - - if (!e->ofs || e->temp || !e->constant) - return; - n = G_STRING(e->ofs); - if (!*n) - return; - for (i=0 ; i= '1' && ch <= '9') - precache_sounds_block[i] = ch - '0'; - else - precache_sounds_block[i] = 1; - numsounds++; -} - -void QCC_PrecacheModel (QCC_def_t *e, int ch) -{ - char *n; - int i; - - if (e->type->type != ev_string) - return; - - if (!e->ofs || e->temp || !e->constant) - return; - n = G_STRING(e->ofs); - if (!*n) - return; - for (i=0 ; i= '1' && ch <= '9') - precache_models_block[i] = ch - '0'; - else - precache_models_block[i] = 1; - } - return; - } - if (nummodels == MAX_MODELS) - return; -// QCC_Error ("PrecacheModels: nummodels == MAX_MODELS"); - strcpy (precache_models[i], n); - if (ch >= '1' && ch <= '9') - precache_models_block[i] = ch - '0'; - else - precache_models_block[i] = 1; - nummodels++; -} - -void QCC_SetModel (QCC_def_t *e) -{ - char *n; - int i; - - if (e->type->type != ev_string) - return; - - if (!e->ofs || e->temp || !e->constant) - return; - n = G_STRING(e->ofs); - if (!*n) - return; - for (i=0 ; itype->type != ev_string) - return; - - if (!e->ofs || e->temp || !e->constant) - return; - n = G_STRING(e->ofs); - if (!*n) - return; - for (i=0 ; i= '1' && ch <= '9') - precache_textures_block[i] = ch - '0'; - else - precache_textures_block[i] = 1; - numtextures++; -} - -void QCC_PrecacheFile (QCC_def_t *e, int ch) -{ - char *n; - int i; - - if (e->type->type != ev_string) - return; - - if (!e->ofs || e->temp || !e->constant) - return; - n = G_STRING(e->ofs); - if (!*n) - return; - for (i=0 ; i= '1' && ch <= '9') - precache_files_block[i] = ch - '0'; - else - precache_files_block[i] = 1; - numfiles++; -} - -void QCC_PrecacheFileOptimised (char *n, int ch) -{ - int i; - - for (i=0 ; i= '1' && ch <= '9') - precache_files_block[i] = ch - '0'; - else - precache_files_block[i] = 1; - numfiles++; -} - -QCC_def_t *QCC_PR_GenerateFunctionCall (QCC_def_t *func, QCC_def_t *arglist[], int argcount) //warning, the func could have no name set if it's a field call. -{ - QCC_def_t *d, *oldret, *oself; - int i; - QCC_type_t *t; - int extraparms=false; - int np; - int laststatement = numstatements; - - int callconvention; - QCC_dstatement_t *st; - - - func->timescalled++; - - if (QCC_OPCodeValid(&pr_opcodes[OP_CALL1H])) - callconvention = OP_CALL1H; //FTE extended - else - callconvention = OP_CALL1; //standard - - t = func->type; - - if (t->type == ev_variant) - { - t->aux_type = type_variant; - } - - if (t->type != ev_function && t->type != ev_variant) - { - QCC_PR_ParseErrorPrintDef (ERR_NOTAFUNCTION, func, "not a function"); - } - -// copy the arguments to the global parameter variables - if (t->type == ev_variant) - { - extraparms = true; - np = 0; - } - else if (t->num_parms < 0) - { - extraparms = true; - np = (t->num_parms * -1) - 1; - } - else - np = t->num_parms; - - if (strchr(func->name, ':') && laststatement && statements[laststatement-1].op == OP_LOAD_FNC && statements[laststatement-1].c == func->ofs) - { //we're entering OO code with a different self. - //eg: other.touch(self) - - //FIXME: problems could occur with hexen2 calling conventions when parm0/1 is 'self' - //thiscall. copy the right ent into 'self' (if it's not the same offset) - d = QCC_PR_GetDef(type_entity, "self", NULL, true, 0, false); - if (statements[laststatement-1].a != d->ofs) - { - oself = QCC_GetTemp(type_entity); - //oself = self - QCC_PR_SimpleStatement(OP_STORE_ENT, d->ofs, oself->ofs, 0, false); - //self = other - QCC_PR_SimpleStatement(OP_STORE_ENT, statements[laststatement-1].a, d->ofs, 0, false); - - //if the args refered to self, update them to refer to oself instead - //(as self is now set to 'other') - for (i = 0; i < argcount; i++) - { - if (arglist[i]->ofs == d->ofs) - { - arglist[i] = oself; - } - } - } - else - { - //it was self.func() anyway - oself = NULL; - d = NULL; - } - } - else - { //regular func call - oself = NULL; - d = NULL; - } - -// write the arguments (except for first two if hexenc) - for (i = 0; i < argcount; i++) - { - if (i>=MAX_PARMS) - d = extra_parms[i - MAX_PARMS]; - else - d = &def_parms[i]; - - if (callconvention == OP_CALL1H) - if (i < 2) - { - //first two args are passed in the call opcode, so don't need to be copied - arglist[i]->references++; - d->references++; - /*don't free these temps yet, free them after the return check*/ - continue; - } - - if (arglist[i]->type->size == 3 || !opt_nonvec_parms) - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_STORE_V], arglist[i], d, (QCC_dstatement_t **)0xffffffff)); - else - { - d->type = arglist[i]->type; - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_STORE_F], arglist[i], d, (QCC_dstatement_t **)0xffffffff)); - optres_nonvec_parms++; - } - } - - //if the return value was in use, save it off now, so that it doesn't get clobbered - if (def_ret.temp->used) - oldret = QCC_GetTemp(def_ret.type); - else - oldret = NULL; - - /*can free temps used for arguments now*/ - if (callconvention == OP_CALL1H) - { - for (i = 0; i < argcount && i < 2; i++) - QCC_FreeTemp(arglist[i]); - } - - if (oldret && !def_ret.temp->used) - { - QCC_FreeTemp(oldret); - oldret = NULL; - } - else if (def_ret.temp->used) - { - if (def_ret.type->size == 3) - QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_V], &def_ret, oldret, (void*)0xffffffff)); - else - QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_F], &def_ret, oldret, (void*)0xffffffff)); - QCC_UnFreeTemp(oldret); - QCC_UnFreeTemp(&def_ret); - QCC_PR_ParseWarning(WARN_FIXEDRETURNVALUECONFLICT, "Return value conflict - output is inefficient"); - } - else - oldret = NULL; - - //we dont need to lock the local containing the function index because its thrown away after the call anyway - //(if a function is called in the argument list then it'll be locked as part of that call) - QCC_FreeTemp(func); - QCC_LockActiveTemps(); //any temps before are likly to be used with the return value. - QCC_UnFreeTemp(func); - - //generate the call - if (argcount>MAX_PARMS) - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[callconvention-1+MAX_PARMS], func, 0, (QCC_dstatement_t **)&st)); - else if (argcount) - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[callconvention-1+argcount], func, 0, (QCC_dstatement_t **)&st)); - else - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_CALL0], func, 0, (QCC_dstatement_t **)&st)); - - if (callconvention == OP_CALL1H) - { - if (argcount) - { - st->b = arglist[0]->ofs; -// QCC_FreeTemp(param[0]); - if (argcount>1) - { - st->c = arglist[1]->ofs; -// QCC_FreeTemp(param[1]); - } - } - } - - //restore the class owner - if (oself) - QCC_PR_SimpleStatement(OP_STORE_ENT, oself->ofs, d->ofs, 0, false); - - if (oldret) - { - if (oldret->temp && !oldret->temp->used) - QCC_PR_ParseWarning(0, "Ret was freed\n"); - - //if we preserved the ofs_ret global, restore it here - if (t->type == ev_variant) - { - d = QCC_GetTemp(type_variant); - QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_STORE_F, &def_ret, d, (void*)0xffffffff)); - } - else - { - d = QCC_GetTemp(t->aux_type); - if (t->aux_type->size == 3) - QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_STORE_V, &def_ret, d, (void*)0xffffffff)); - else - QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_STORE_F, &def_ret, d, (void*)0xffffffff)); - } - if (def_ret.type->size == 3) - QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_STORE_V, oldret, &def_ret, (void*)0xffffffff)); - else - QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_STORE_F, oldret, &def_ret, (void*)0xffffffff)); - QCC_FreeTemp(oldret); - QCC_UnFreeTemp(&def_ret); - QCC_UnFreeTemp(d); - - return d; - } - - if (t->type == ev_variant) - def_ret.type = type_variant; - else - def_ret.type = t->aux_type; - if (def_ret.temp->used) - QCC_PR_ParseWarning(WARN_FIXEDRETURNVALUECONFLICT, "Return value conflict - output is inefficient"); - def_ret.temp->used = true; - - return &def_ret; -} - -/* -============ -PR_ParseFunctionCall -============ -*/ -QCC_def_t *QCC_PR_ParseFunctionCall (QCC_def_t *func) //warning, the func could have no name set if it's a field call. -{ - QCC_def_t *e, *d, *old = {0}, *oself, *out; // warning: ‘old’ may be used uninitialized in this function - int arg; - QCC_type_t *t, *p; - int extraparms=false; - int np; - - int callconvention; - - QCC_def_t *param[MAX_PARMS+MAX_EXTRA_PARMS]; - - func->timescalled++; - - if (QCC_OPCodeValid(&pr_opcodes[OP_CALL1H])) - callconvention = OP_CALL1H; //FTE extended - else - callconvention = OP_CALL1; //standard - - t = func->type; - - if (t->type == ev_variant) - { - t->aux_type = type_variant; - } - - if (t->type != ev_function && t->type != ev_variant) - { - QCC_PR_ParseErrorPrintDef (ERR_NOTAFUNCTION, func, "not a function"); - } - - if (!t->num_parms&&t->type != ev_variant) //intrinsics. These base functions have variable arguments. I would check for (...) args too, but that might be used for extended builtin functionality. (this code wouldn't compile otherwise) - { - if (!strcmp(func->name, "sizeof")) - { - QCC_type_t *t; - if (!func->initialized) - func->initialized = 3; - func->references++; - t = QCC_PR_ParseType(false, false); - QCC_PR_Expect(")"); - return QCC_MakeIntConst(t->size * 4); - } - if (!strcmp(func->name, "_")) - { - if (!func->initialized) - func->initialized = 3; - func->references++; - if (pr_token_type == tt_immediate && pr_immediate_type->type == ev_string) - { - d = QCC_MakeTranslateStringConst(pr_immediate_string); - QCC_PR_Lex(); - } - else - QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHPARM, func, "_() intrinsic accepts only a string immediate", 1); - QCC_PR_Expect(")"); - return d; - } - if (!strcmp(func->name, "random")) - { - old = NULL; - if (!func->initialized) - func->initialized = 3; - func->references++; - if (!QCC_PR_CheckToken(")")) - { - e = QCC_PR_Expression (TOP_PRIORITY, EXPR_DISALLOW_COMMA); - if (e->type->type != ev_float) - QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHPARM, func, "type mismatch on parm %i", 1); - if (!QCC_PR_CheckToken(")")) - { - QCC_PR_Expect(","); - d = QCC_PR_Expression (TOP_PRIORITY, EXPR_DISALLOW_COMMA); - if (d->type->type != ev_float) - QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHPARM, func, "type mismatch on parm %i", 2); - QCC_PR_Expect(")"); - } - else - d = NULL; - } - else - { - e = NULL; - d = NULL; - } - - out = &def_ret; - if (QCC_OPCodeValid(&pr_opcodes[OP_RAND0])) - { - if(qcc_targetformat != QCF_HEXEN2) - out = QCC_GetTemp(type_float); - else if (out->temp->used) - { - old = QCC_GetTemp(out->type); - if (def_ret.type->size == 3) - QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_V], out, old, NULL)); - else - QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_F], out, old, NULL)); - QCC_UnFreeTemp(old); - QCC_UnFreeTemp(out); - QCC_PR_ParseWarning(WARN_FIXEDRETURNVALUECONFLICT, "Return value conflict - output is inefficient"); - } - else - old = NULL; - - if (e) - { - if (d) - QCC_PR_SimpleStatement(OP_RAND2, e->ofs, d->ofs, out->ofs, false); - else - QCC_PR_SimpleStatement(OP_RAND1, e->ofs, 0, out->ofs, false); - } - else - QCC_PR_SimpleStatement(OP_RAND0, 0, 0, out->ofs, false); - } - else - { - if (out->temp->used) - { - old = QCC_GetTemp(out->type); - if (def_ret.type->size == 3) - QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_V], out, old, NULL)); - else - QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_F], out, old, NULL)); - QCC_UnFreeTemp(old); - QCC_UnFreeTemp(out); - QCC_PR_ParseWarning(WARN_FIXEDRETURNVALUECONFLICT, "Return value conflict - output is inefficient"); - } - else - old = NULL; - - if (e) - { - if (d) - { - QCC_dstatement_t *st; - QCC_def_t *t; - QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0, false); - - if ((!d->constant || !e->constant) && G_FLOAT(d->ofs) >= G_FLOAT(d->ofs)) - { - t = QCC_PR_Statement(&pr_opcodes[OP_GT_F], d, e, NULL); - QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_IFNOT_I], t, 0, &st)); - st->b = 3; - - t = QCC_PR_Statement(&pr_opcodes[OP_SUB_F], d, e, NULL); - QCC_PR_SimpleStatement(OP_MUL_F, OFS_RETURN, t->ofs, OFS_RETURN, false); - QCC_FreeTemp(t); - QCC_PR_SimpleStatement(OP_ADD_F, OFS_RETURN, e->ofs, OFS_RETURN, false); - - QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_GOTO], 0, 0, &st)); - st->a = 3; - } - - t = QCC_PR_Statement(&pr_opcodes[OP_SUB_F], e, d, NULL); - QCC_PR_SimpleStatement(OP_MUL_F, OFS_RETURN, t->ofs, OFS_RETURN, false); - QCC_FreeTemp(t); - QCC_PR_SimpleStatement(OP_ADD_F, OFS_RETURN, d->ofs, OFS_RETURN, false); - } - else - { - QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0, false); - QCC_PR_SimpleStatement(OP_MUL_F, OFS_RETURN, e->ofs, OFS_RETURN, false); - } - } - else - QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0, false); - } - - if (e) - { - QCC_FreeTemp(e); - e->references++; - } - if (d) - { - d->references++; - QCC_FreeTemp(d); - } - - if (old) - { - d = QCC_GetTemp(type_float); - QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_STORE_F, &def_ret, d, NULL)); - if (def_ret.type->size == 3) - QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_STORE_V, old, &def_ret, NULL)); - else - QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_STORE_F, old, &def_ret, NULL)); - QCC_FreeTemp(old); - QCC_UnFreeTemp(d); - QCC_UnFreeTemp(&def_ret); - - return d; - } - - if (out == &def_ret) - { - if (out->temp->used) - QCC_PR_ParseWarning(0, "Return value conflict - output is likly to be invalid"); - out->temp->used = true; - out->type = type_float; - } - return out; - - } - if (!strcmp(func->name, "randomv")) - { - out = NULL; - if (!func->initialized) - func->initialized = 3; - func->references++; - if (!QCC_PR_CheckToken(")")) - { - e = QCC_PR_Expression (TOP_PRIORITY, EXPR_DISALLOW_COMMA); - if (e->type->type != ev_vector) - QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHPARM, func, "type mismatch on parm %i", 1); - if (!QCC_PR_CheckToken(")")) - { - QCC_PR_Expect(","); - d = QCC_PR_Expression (TOP_PRIORITY, EXPR_DISALLOW_COMMA); - if (d->type->type != ev_vector) - QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHPARM, func, "type mismatch on parm %i", 2); - QCC_PR_Expect(")"); - } - else - d = NULL; - } - else - { - e = NULL; - d = NULL; - } - - if (QCC_OPCodeValid(&pr_opcodes[OP_RANDV0])) - { - if(def_ret.temp->used) - out = QCC_GetTemp(type_vector); - else - out = &def_ret; - - if (e) - { - if (d) - QCC_PR_SimpleStatement(OP_RANDV2, e->ofs, d->ofs, out->ofs, false); - else - QCC_PR_SimpleStatement(OP_RANDV1, e->ofs, 0, out->ofs, false); - } - else - QCC_PR_SimpleStatement(OP_RANDV0, 0, 0, out->ofs, false); - } - else - { - if (def_ret.temp->used) - { - old = QCC_GetTemp(def_ret.type); - if (def_ret.type->size == 3) - QCC_PR_Statement(&pr_opcodes[OP_STORE_V], &def_ret, old, NULL); - else - QCC_PR_Statement(&pr_opcodes[OP_STORE_F], &def_ret, old, NULL); - QCC_UnFreeTemp(old); - QCC_UnFreeTemp(&def_ret); - QCC_PR_ParseWarning(WARN_FIXEDRETURNVALUECONFLICT, "Return value conflict - output is inefficient"); - } - else - old = NULL; - - if (e) - { - if (d) - { - QCC_def_t *t; - QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0, false); - - if ((!d->constant || !e->constant) && G_FLOAT(d->ofs) >= G_FLOAT(d->ofs)) - { - t = QCC_GetTemp(type_float); - QCC_PR_SimpleStatement(OP_GT_F, d->ofs+2, e->ofs+2, t->ofs, false); - QCC_PR_SimpleStatement(OP_IFNOT_I, t->ofs, 3, 0, false); - - QCC_PR_SimpleStatement(OP_SUB_F, d->ofs+2, e->ofs+2, t->ofs, false); - QCC_PR_SimpleStatement(OP_MUL_F, OFS_RETURN, t->ofs, OFS_RETURN+2, false); - QCC_FreeTemp(t); - QCC_PR_SimpleStatement(OP_ADD_F, OFS_RETURN, e->ofs+2, OFS_RETURN+2, false); - - QCC_PR_SimpleStatement(OP_GOTO, 3, 0, 0, false); - } - - t = QCC_GetTemp(type_float); - QCC_PR_SimpleStatement(OP_SUB_F, d->ofs+2, e->ofs+2, t->ofs, false); - QCC_PR_SimpleStatement(OP_MUL_F, OFS_RETURN, t->ofs, OFS_RETURN+2, false); - QCC_FreeTemp(t); - QCC_PR_SimpleStatement(OP_ADD_F, OFS_RETURN, d->ofs+2, OFS_RETURN+2, false); - - - - QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0, false); - - if ((!d->constant || !e->constant) && G_FLOAT(d->ofs) >= G_FLOAT(d->ofs)) - { - t = QCC_GetTemp(type_float); - QCC_PR_SimpleStatement(OP_GT_F, d->ofs+1, e->ofs+1, t->ofs, false); - QCC_PR_SimpleStatement(OP_IFNOT_I, t->ofs, 3, 0, false); - - QCC_PR_SimpleStatement(OP_SUB_F, d->ofs+1, e->ofs+1, t->ofs, false); - QCC_PR_SimpleStatement(OP_MUL_F, OFS_RETURN, t->ofs, OFS_RETURN+1, false); - QCC_FreeTemp(t); - QCC_PR_SimpleStatement(OP_ADD_F, OFS_RETURN, e->ofs+1, OFS_RETURN+1, false); - - QCC_PR_SimpleStatement(OP_GOTO, 3, 0, 0, false); - } - - t = QCC_GetTemp(type_float); - QCC_PR_SimpleStatement(OP_SUB_F, d->ofs+1, e->ofs+1, t->ofs, false); - QCC_PR_SimpleStatement(OP_MUL_F, OFS_RETURN, t->ofs, OFS_RETURN+1, false); - QCC_FreeTemp(t); - QCC_PR_SimpleStatement(OP_ADD_F, OFS_RETURN, d->ofs+1, OFS_RETURN+1, false); - - - QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0, false); - - if ((!d->constant || !e->constant) && G_FLOAT(d->ofs) >= G_FLOAT(d->ofs)) - { - t = QCC_GetTemp(type_float); - QCC_PR_SimpleStatement(OP_GT_F, d->ofs, e->ofs, t->ofs, false); - QCC_PR_SimpleStatement(OP_IFNOT_I, t->ofs, 3, 0, false); - - QCC_PR_SimpleStatement(OP_SUB_F, d->ofs, e->ofs, t->ofs, false); - QCC_PR_SimpleStatement(OP_MUL_F, OFS_RETURN, t->ofs, OFS_RETURN, false); - QCC_FreeTemp(t); - QCC_PR_SimpleStatement(OP_ADD_F, OFS_RETURN, e->ofs, OFS_RETURN, false); - - QCC_PR_SimpleStatement(OP_GOTO, 3, 0, 0, false); - } - - t = QCC_GetTemp(type_float); - QCC_PR_SimpleStatement(OP_SUB_F, d->ofs, e->ofs, t->ofs, false); - QCC_PR_SimpleStatement(OP_MUL_F, OFS_RETURN, t->ofs, OFS_RETURN, false); - QCC_FreeTemp(t); - QCC_PR_SimpleStatement(OP_ADD_F, OFS_RETURN, d->ofs, OFS_RETURN, false); - } - else - { - QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0, false); - QCC_PR_SimpleStatement(OP_MUL_F, OFS_RETURN, e->ofs, OFS_RETURN+2, false); - QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0, false); - QCC_PR_SimpleStatement(OP_MUL_F, OFS_RETURN, e->ofs, OFS_RETURN+1, false); - QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0, false); - QCC_PR_SimpleStatement(OP_MUL_F, OFS_RETURN, e->ofs, OFS_RETURN, false); - } - } - else - { - QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0, false); - QCC_PR_SimpleStatement(OP_STORE_F, OFS_RETURN, OFS_RETURN+2, 0, false); - QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0, false); - QCC_PR_SimpleStatement(OP_STORE_F, OFS_RETURN, OFS_RETURN+1, 0, false); - QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0, false); - } - } - - - if (e) - { - QCC_FreeTemp(e); - e->references++; - } - if (d) - { - d->references++; - QCC_FreeTemp(d); - } - - if (old) - { - d = QCC_GetTemp(type_vector); - QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_STORE_V, &def_ret, d, NULL)); - if (def_ret.type->size == 3) - { - QCC_PR_Statement(pr_opcodes+OP_STORE_V, old, &def_ret, NULL); - } - else - { - QCC_PR_Statement(pr_opcodes+OP_STORE_F, old, &def_ret, NULL); - } - QCC_FreeTemp(old); - QCC_UnFreeTemp(d); - QCC_UnFreeTemp(&def_ret); - - return d; - } - - if (def_ret.temp->used) - QCC_PR_ParseWarning(0, "Return value conflict - output is likly to be invalid"); - def_ret.temp->used = true; - def_ret.type = type_vector; - return &def_ret; - } - else if (!strcmp(func->name, "spawn")) - { - QCC_type_t *rettype; - if (QCC_PR_CheckToken(")")) - { - rettype = type_entity; - } - else - { - rettype = QCC_TypeForName(QCC_PR_ParseName()); - if (!rettype || rettype->type != ev_entity) - QCC_PR_ParseError(ERR_NOTANAME, "Spawn operator with undefined class"); - - QCC_PR_Expect(")"); - } - - - if (def_ret.temp->used) - { - old = QCC_GetTemp(def_ret.type); - if (def_ret.type->size == 3) - QCC_PR_Statement(&pr_opcodes[OP_STORE_V], &def_ret, old, NULL); - else - QCC_PR_Statement(&pr_opcodes[OP_STORE_F], &def_ret, old, NULL); - QCC_UnFreeTemp(old); - QCC_UnFreeTemp(&def_ret); - QCC_PR_ParseWarning(WARN_FIXEDRETURNVALUECONFLICT, "Return value conflict - output is inefficient"); - } - else - old = NULL; - /* - if (def_ret.temp->used) - QCC_PR_ParseWarning(0, "Return value conflict - output is likly to be invalid"); - def_ret.temp->used = true; - */ - - if (rettype != type_entity) - { - char genfunc[2048]; - sprintf(genfunc, "Class*%s", rettype->name); - func = QCC_PR_GetDef(type_function, genfunc, NULL, true, 0, false); - func->references++; - } - QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0, false); - - if (old) - { - d = QCC_GetTemp(rettype); - QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_STORE_ENT, &def_ret, d, NULL)); - if (def_ret.type->size == 3) - QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_STORE_V, old, &def_ret, NULL)); - else - QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_STORE_F, old, &def_ret, NULL)); - QCC_FreeTemp(old); - QCC_UnFreeTemp(d); - QCC_UnFreeTemp(&def_ret); - - return d; - } - - def_ret.type = rettype; - return &def_ret; - } - else if (!strcmp(func->name, "entnum") && !QCC_PR_CheckToken(")")) - { - //t = (a/%1) / (nextent(world)/%1) - //a/%1 does a (int)entity to float conversion type thing - if (!func->initialized) - func->initialized = 3; - func->references++; - - e = QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA); - QCC_PR_Expect(")"); - e = QCC_PR_Statement(&pr_opcodes[OP_DIV_F], e, QCC_MakeIntConst(1), (QCC_dstatement_t **)0xffffffff); - - d = QCC_PR_GetDef(NULL, "nextent", NULL, false, 0, false); - if (!d) - QCC_PR_ParseError(0, "the nextent builtin is not defined"); - QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_F], e, &def_parms[0], (QCC_dstatement_t **)0xffffffff)); - d = QCC_PR_Statement(&pr_opcodes[OP_CALL0], d, NULL, NULL); - d = QCC_PR_Statement(&pr_opcodes[OP_DIV_F], d, QCC_MakeIntConst(1), (QCC_dstatement_t **)0xffffffff); - - e = QCC_PR_Statement(&pr_opcodes[OP_DIV_F], e, d, (QCC_dstatement_t **)0xffffffff); - - return e; - } - } //so it's not an intrinsic. - - if (opt_precache_file) //should we strip out all precache_file calls? - { - if (!strncmp(func->name,"precache_file", 13)) - { - if (pr_token_type == tt_immediate && pr_immediate_type->type == ev_string && pr_scope && !strcmp(pr_scope->name, "main")) - { - optres_precache_file += strlen(pr_immediate_string); - QCC_PR_Lex(); - QCC_PR_Expect(")"); - QCC_PrecacheFileOptimised (pr_immediate_string, func->name[13]); - def_ret.type = type_void; - return &def_ret; - } - } - } - -// copy the arguments to the global parameter variables - arg = 0; - if (t->type == ev_variant) - { - extraparms = true; - np = 0; - } - else if (t->num_parms < 0) - { - extraparms = true; - np = (t->num_parms * -1) - 1; - } - else - np = t->num_parms; - - //any temps referenced to build the parameters don't need to be locked. - if (!QCC_PR_CheckToken(")")) - { - p = t->param; - do - { - if (extraparms && arg >= MAX_PARMS) - QCC_PR_ParseErrorPrintDef (ERR_TOOMANYPARAMETERSVARARGS, func, "More than %i parameters on varargs function", MAX_PARMS); - else if (arg >= MAX_PARMS+MAX_EXTRA_PARMS) - QCC_PR_ParseErrorPrintDef (ERR_TOOMANYTOTALPARAMETERS, func, "More than %i parameters", MAX_PARMS+MAX_EXTRA_PARMS); - if (!extraparms && arg >= t->num_parms && !p) - { - QCC_PR_ParseWarning (WARN_TOOMANYPARAMETERSFORFUNC, "too many parameters"); - QCC_PR_ParsePrintDef(WARN_TOOMANYPARAMETERSFORFUNC, func); - } - - - //with vectorcalls, we store the vector into the args as individual floats - //this allows better reuse of vector constants. - //copy it into the offset now, because we can. - if (opt_vectorcalls && pr_token_type == tt_immediate && pr_immediate_type == type_vector && arg < MAX_PARMS && !def_parms[arg].temp->used) - { - e = &def_parms[arg]; - - e->ofs = OFS_PARM0+0; - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_STORE_F], QCC_MakeFloatConst(pr_immediate.vector[0]), e, (QCC_dstatement_t **)0xffffffff)); - e->ofs = OFS_PARM0+1; - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_STORE_F], QCC_MakeFloatConst(pr_immediate.vector[1]), e, (QCC_dstatement_t **)0xffffffff)); - e->ofs = OFS_PARM0+2; - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_STORE_F], QCC_MakeFloatConst(pr_immediate.vector[2]), e, (QCC_dstatement_t **)0xffffffff)); - e->ofs = OFS_PARM0; - e->type = type_vector; - - QCC_PR_Lex(); - } - else - e = QCC_PR_Expression (TOP_PRIORITY, EXPR_DISALLOW_COMMA); - - if (arg == 0 && func->name) - { - // save information for model and sound caching - if (!strncmp(func->name,"precache_", 9)) - { - if (!strncmp(func->name+9,"sound", 5)) - QCC_PrecacheSound (e, func->name[14]); - else if (!strncmp(func->name+9,"model", 5)) - QCC_PrecacheModel (e, func->name[14]); - else if (!strncmp(func->name+9,"texture", 7)) - QCC_PrecacheTexture (e, func->name[16]); - else if (!strncmp(func->name+9,"file", 4)) - QCC_PrecacheFile (e, func->name[13]); - } - } - - if (arg>=MAX_PARMS) - { - if (!extra_parms[arg - MAX_PARMS]) - { - d = (QCC_def_t *) qccHunkAlloc (sizeof(QCC_def_t)); - d->name = "extra parm"; - d->ofs = QCC_GetFreeOffsetSpace (3); - extra_parms[arg - MAX_PARMS] = d; - } - d = extra_parms[arg - MAX_PARMS]; - } - else - d = &def_parms[arg]; - - if (pr_classtype && e->type->type == ev_field && p->type != ev_field) - { //convert. - oself = QCC_PR_GetDef(type_entity, "self", NULL, true, 0, false); - switch(e->type->aux_type->type) - { - case ev_string: - e = QCC_PR_Statement(pr_opcodes+OP_LOAD_S, oself, e, NULL); - break; - case ev_integer: - e = QCC_PR_Statement(pr_opcodes+OP_LOAD_I, oself, e, NULL); - break; - case ev_float: - e = QCC_PR_Statement(pr_opcodes+OP_LOAD_F, oself, e, NULL); - break; - case ev_function: - e = QCC_PR_Statement(pr_opcodes+OP_LOAD_FNC, oself, e, NULL); - break; - case ev_vector: - e = QCC_PR_Statement(pr_opcodes+OP_LOAD_V, oself, e, NULL); - break; - case ev_entity: - e = QCC_PR_Statement(pr_opcodes+OP_LOAD_ENT, oself, e, NULL); - break; - default: - QCC_Error(ERR_INTERNAL, "Bad member type. Try forced expansion"); - } - } - - if (p) - { - if (typecmp(e->type, p)) - /*if (e->type->type != ev_integer && p->type != ev_function) - if (e->type->type != ev_function && p->type != ev_integer) - if ( e->type->type != p->type )*/ - { - if (p->type == ev_integer && e->type->type == ev_float) //convert float -> int... is this a constant? - e = QCC_PR_Statement(pr_opcodes+OP_CONV_FTOI, e, NULL, NULL); - else if (p->type == ev_float && e->type->type == ev_integer) //convert float -> int... is this a constant? - e = QCC_PR_Statement(pr_opcodes+OP_CONV_ITOF, e, NULL, NULL); - else if ((p->type == ev_function && p->type == ev_string) && e->type->type == ev_integer && e->constant && !((int*)qcc_pr_globals)[e->ofs]) - { //you're allowed to use int 0 to pass a null function pointer - //this is basically because __NULL__ is defined as ~0 (int 0) - } - else if (p->type != ev_variant && e->type->type != ev_variant) //can cast to variant whatever happens - { - if (flag_laxcasts || (p->type == ev_function && e->type->type == ev_function)) - { - QCC_PR_ParseWarning(WARN_LAXCAST, "type mismatch on parm %i - (%s should be %s)", arg+1, TypeName(e->type), TypeName(p)); - QCC_PR_ParsePrintDef(WARN_LAXCAST, func); - } - else - QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHPARM, func, "type mismatch on parm %i - (%s should be %s)", arg+1, TypeName(e->type), TypeName(p)); - } - } - - d->type = p; - - p=p->next; - } - // a vector copy will copy everything - else - d->type = type_void; - - if (arg == 1 && !STRCMP(func->name, "setmodel")) - { - QCC_SetModel(e); - } - - param[arg] = e; -/* if (e->type->size>1) - QCC_PR_Statement (&pr_opcodes[OP_STORE_V], e, d, (QCC_dstatement_t **)0xffffffff); - else - QCC_PR_Statement (&pr_opcodes[OP_STORE_F], e, d, (QCC_dstatement_t **)0xffffffff); - */ - arg++; - } while (QCC_PR_CheckToken (",")); - - if (t->num_parms != -1 && arg < np) - QCC_PR_ParseWarning (WARN_TOOFEWPARAMS, "too few parameters on call to %s", func->name); - QCC_PR_Expect (")"); - } - else if (np) - { - QCC_PR_ParseWarning (WARN_TOOFEWPARAMS, "%s: Too few parameters", func->name); - QCC_PR_ParsePrintDef (WARN_TOOFEWPARAMS, func); - } - - return QCC_PR_GenerateFunctionCall(func, param, arg); -} - -int constchecks; -int varchecks; -int typechecks; -QCC_def_t *QCC_MakeIntConst(int value) -{ - QCC_def_t *cn; - -// check for a constant with the same value - for (cn=pr.def_head.next ; cn ; cn=cn->next) - { - varchecks++; - if (!cn->initialized) - continue; - if (!cn->constant) - continue; - constchecks++; - if (cn->type != type_integer) - continue; - typechecks++; - - if ( G_INT(cn->ofs) == value ) - { - return cn; - } - } - -// allocate a new one - cn = (void *)qccHunkAlloc (sizeof(QCC_def_t)); - cn->next = NULL; - pr.def_tail->next = cn; - pr.def_tail = cn; - - cn->type = type_integer; - cn->name = "IMMEDIATE"; - cn->constant = true; - cn->initialized = 1; - cn->scope = NULL; // always share immediates - cn->arraysize = 0; - - if (!value) - G_INT(cn->ofs) = 0; - else - { - // copy the immediate to the global area - cn->ofs = QCC_GetFreeOffsetSpace (type_size[type_integer->type]); - - G_INT(cn->ofs) = value; - } - - - return cn; -} - -QCC_def_t *QCC_MakeVectorConst(float a, float b, float c) -{ - QCC_def_t *cn; - -// check for a constant with the same value - for (cn=pr.def_head.next ; cn ; cn=cn->next) - { - varchecks++; - if (!cn->initialized) - continue; - if (!cn->constant) - continue; - constchecks++; - if (cn->type != type_vector) - continue; - typechecks++; - - if ( G_FLOAT(cn->ofs+0) == a && - G_FLOAT(cn->ofs+1) == b && - G_FLOAT(cn->ofs+2) == c) - { - return cn; - } - } - -// allocate a new one - cn = (void *)qccHunkAlloc (sizeof(QCC_def_t)); - cn->next = NULL; - pr.def_tail->next = cn; - pr.def_tail = cn; - - cn->type = type_vector; - cn->name = "IMMEDIATE"; - cn->constant = true; - cn->initialized = 1; - cn->scope = NULL; // always share immediates - cn->arraysize = 0; - -// copy the immediate to the global area - cn->ofs = QCC_GetFreeOffsetSpace (type_size[type_vector->type]); - - G_FLOAT(cn->ofs+0) = a; - G_FLOAT(cn->ofs+1) = b; - G_FLOAT(cn->ofs+2) = c; - - return cn; -} - -extern hashtable_t floatconstdefstable; -QCC_def_t *QCC_MakeFloatConst(float value) -{ - QCC_def_t *cn; - - union { - float f; - int i; - } fi; - - fi.f = value; - - cn = Hash_GetKey(&floatconstdefstable, fi.i); - if (cn) - return cn; - -// allocate a new one - cn = (void *)qccHunkAlloc (sizeof(QCC_def_t)); - cn->next = NULL; - pr.def_tail->next = cn; - pr.def_tail = cn; - - cn->s_file = s_file; - cn->s_line = pr_source_line; - cn->type = type_float; - cn->name = "IMMEDIATE"; - cn->constant = true; - cn->initialized = 1; - cn->scope = NULL; // always share immediates - cn->arraysize = 0; - -// copy the immediate to the global area - cn->ofs = QCC_GetFreeOffsetSpace (type_size[type_integer->type]); - - Hash_AddKey(&floatconstdefstable, fi.i, cn, qccHunkAlloc(sizeof(bucket_t))); - - G_FLOAT(cn->ofs) = value; - - - return cn; -} - -extern hashtable_t stringconstdefstable, stringconstdefstable_trans; -int dotranslate_count; -static QCC_def_t *QCC_MakeStringConstInternal(char *value, pbool translate) -{ - QCC_def_t *cn; - int string; - - cn = pHash_Get(translate?&stringconstdefstable_trans:&stringconstdefstable, value); - if (cn) - return cn; - -// allocate a new one - if(translate) - { - char buf[64]; - sprintf(buf, "dotranslate_%i", ++dotranslate_count); - cn = (void *)qccHunkAlloc (sizeof(QCC_def_t) + strlen(buf)+1); - cn->name = (char*)(cn+1); - strcpy(cn->name, buf); - } - else - { - cn = (void *)qccHunkAlloc (sizeof(QCC_def_t)); - cn->name = "IMMEDIATE"; - } - cn->next = NULL; - pr.def_tail->next = cn; - pr.def_tail = cn; - - cn->type = type_string; - cn->constant = true; - cn->initialized = 1; - cn->scope = NULL; // always share immediates - cn->arraysize = 0; - -// copy the immediate to the global area - cn->ofs = QCC_GetFreeOffsetSpace (type_size[type_integer->type]); - - string = QCC_CopyString (value); - - pHash_Add(translate?&stringconstdefstable_trans:&stringconstdefstable, strings+string, cn, qccHunkAlloc(sizeof(bucket_t))); - - G_INT(cn->ofs) = string; - - return cn; -} - -QCC_def_t *QCC_MakeStringConst(char *value) -{ - return QCC_MakeStringConstInternal(value, false); -} -QCC_def_t *QCC_MakeTranslateStringConst(char *value) -{ - return QCC_MakeStringConstInternal(value, true); -} - -QCC_type_t *QCC_PointerTypeTo(QCC_type_t *type) -{ - QCC_type_t *newtype; - newtype = QCC_PR_NewType("ptr", ev_pointer, false); - newtype->aux_type = type; - return newtype; -} - -int basictypefield[ev_union+1]; -char *basictypenames[] = { - "void", - "string", - "float", - "vector", - "entity", - "field", - "function", - "pointer", - "integer", - "struct", - "union" -}; - -QCC_def_t *QCC_MemberInParentClass(char *name, QCC_type_t *clas) -{ //if a member exists, return the member field (rather than mapped-to field) - QCC_type_t *mt; - QCC_def_t *def; - int p, np; - char membername[2048]; - - if (!clas) - { - def = QCC_PR_GetDef(NULL, name, NULL, 0, 0, false); - if (def && def->type->type == ev_field) //the member existed as a normal entity field. - return def; - return NULL; - } - - np = clas->num_parms; - for (p = 0, mt = clas->param; p < np; p++, mt = mt->next) - { - if (strcmp(mt->name, name)) - continue; - - //the parent has it. - - sprintf(membername, "%s::"MEMBERFIELDNAME, clas->name, mt->name); - def = QCC_PR_GetDef(NULL, membername, NULL, false, 0, false); - return def; - } - - return QCC_MemberInParentClass(name, clas->parentclass); -} - -//create fields for the types, instanciate the members to the fields. -//we retouch the parents each time to guarentee polymorphism works. -//FIXME: virtual methods will not work properly. Need to trace down to see if a parent already defined it -void QCC_PR_EmitFieldsForMembers(QCC_type_t *clas) -{ -//we created fields for each class when we defined the actual classes. -//we need to go through each member and match it to the offset of it's parent class, if overloaded, or create a new field if not.. - -//basictypefield is cleared before we do this -//we emit the parent's fields first (every time), thus ensuring that we don't reuse parent fields on a child class. - - char membername[2048]; - int p, np, a; - unsigned int o; - QCC_type_t *mt, *ft; - QCC_def_t *f, *m; - if (clas->parentclass != type_entity) //parents MUST have all their fields set or inheritance would go crazy. - QCC_PR_EmitFieldsForMembers(clas->parentclass); - - np = clas->num_parms; - mt = clas->param; - for (p = 0; p < np; p++, mt = mt->next) - { - sprintf(membername, "%s::"MEMBERFIELDNAME, clas->name, mt->name); - m = QCC_PR_GetDef(NULL, membername, NULL, false, 0, false); - - f = QCC_MemberInParentClass(mt->name, clas->parentclass); - if (f) - { - if (m->arraysize) - QCC_Error(ERR_INTERNAL, "FTEQCC does not support overloaded arrays of members"); - a=0; - for (o = 0; o < m->type->size; o++) - ((int *)qcc_pr_globals)[o+a*mt->size+m->ofs] = ((int *)qcc_pr_globals)[o+f->ofs]; - continue; - } - - for (a = 0; a < (m->arraysize?m->arraysize:1); a++) - { - /*if it was already set, don't go recursive and generate 500 fields for a one-member class that was intheritted from 500 times*/ - if (((int *)qcc_pr_globals)[o+a*mt->size+m->ofs]) - continue; - - //we need the type in here so saved games can still work without saving ints as floats. (would be evil) - ft = QCC_PR_NewType(basictypenames[mt->type], ev_field, false); - ft->aux_type = QCC_PR_NewType(basictypenames[mt->type], mt->type, false); - ft->aux_type->aux_type = type_void; - ft->size = ft->aux_type->size; - ft = QCC_PR_FindType(ft); - sprintf(membername, "__f_%s_%i", ft->name, ++basictypefield[mt->type]); - f = QCC_PR_GetDef(ft, membername, NULL, true, 0, true); - - for (o = 0; o < m->type->size; o++) - ((int *)qcc_pr_globals)[o+a*mt->size+m->ofs] = ((int *)qcc_pr_globals)[o+f->ofs]; - - f->references++; - } - } -} - -void QCC_PR_EmitClassFunctionTable(QCC_type_t *clas, QCC_type_t *childclas, QCC_def_t *ed, QCC_def_t **constructor) -{ //go through clas, do the virtual thing only if the child class does not override. - - char membername[2048]; - QCC_type_t *type; - QCC_type_t *oc; - int p; - - QCC_def_t *point, *member; - QCC_def_t *virt; - - if (clas->parentclass) - QCC_PR_EmitClassFunctionTable(clas->parentclass, childclas, ed, constructor); - - type = clas->param; - for (p = 0; p < clas->num_parms; p++, type = type->next) - { - for (oc = childclas; oc != clas; oc = oc->parentclass) - { - sprintf(membername, "%s::"MEMBERFIELDNAME, oc->name, type->name); - if (QCC_PR_GetDef(NULL, membername, NULL, false, 0, false)) - break; //a child class overrides. - } - if (oc != clas) - continue; - - if (type->type == ev_function) //FIXME: inheritance will not install all the member functions. - { - sprintf(membername, "%s::"MEMBERFIELDNAME, clas->name, type->name); - member = QCC_PR_GetDef(NULL, membername, NULL, false, 0, false); - if (!member) - { - QCC_PR_Warning(0, NULL, 0, "Member function %s was not defined", membername); - continue; - } - if (!strcmp(type->name, clas->name)) - { - *constructor = member; - } - point = QCC_PR_Statement(&pr_opcodes[OP_ADDRESS], ed, member, NULL); - sprintf(membername, "%s::%s", clas->name, type->name); - virt = QCC_PR_GetDef(type, membername, NULL, false, 0, false); - QCC_PR_Statement(&pr_opcodes[OP_STOREP_FNC], virt, point, NULL); - } - } -} - -//take all functions in the type, and parent types, and make sure the links all work properly. -void QCC_PR_EmitClassFromFunction(QCC_def_t *scope, char *tname) -{ - QCC_type_t *basetype; - - QCC_dfunction_t *df; - - QCC_def_t *virt; - QCC_def_t *ed, *oself, *self; - QCC_def_t *constructor = NULL; - -// int func; - - basetype = QCC_TypeForName(tname); - if (!basetype) - QCC_PR_ParseError(ERR_INTERNAL, "Type %s was not defined...", tname); - - if (numfunctions >= MAX_FUNCTIONS) - QCC_Error(ERR_INTERNAL, "Too many function defs"); - - pr_scope = NULL; - memset(basictypefield, 0, sizeof(basictypefield)); - QCC_PR_EmitFieldsForMembers(basetype); - - - - - pr_scope = scope; - - df = &functions[numfunctions]; - numfunctions++; - - df->s_file = 0; - df->s_name = 0; - df->first_statement = numstatements; - df->parm_size[0] = 1; - df->numparms = 0; - df->parm_start = numpr_globals; - - G_FUNCTION(scope->ofs) = df - functions; - - //locals here... - ed = QCC_PR_GetDef(type_entity, "ent", pr_scope, true, 0, false); - - virt = QCC_PR_GetDef(type_function, "spawn", NULL, false, 0, false); - if (!virt) - QCC_Error(ERR_INTERNAL, "spawn function was not defined\n"); - QCC_PR_SimpleStatement(OP_CALL0, virt->ofs, 0, 0, false); //calling convention doesn't come into it. - - QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_ENT], &def_ret, ed, NULL)); - - ed->references = 1; //there may be no functions. - - - QCC_PR_EmitClassFunctionTable(basetype, basetype, ed, &constructor); - - if (constructor) - { //self = ent; - self = QCC_PR_GetDef(type_entity, "self", NULL, false, 0, false); - oself = QCC_PR_GetDef(type_entity, "oself", scope, true, 0, false); - QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_ENT], self, oself, NULL)); - QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_ENT], ed, self, NULL)); //return to our old self. boom boom. - QCC_PR_SimpleStatement(OP_CALL0, constructor->ofs, 0, 0, false); - QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_ENT], oself, self, NULL)); - } - - QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_RETURN], ed, NULL, NULL)); //apparently we do actually have to return something. *sigh*... - QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_DONE], NULL, NULL, NULL)); - - - - QCC_WriteAsmFunction(scope, df->first_statement, df->parm_start); - pr.localvars = NULL; - - - locals_end = numpr_globals + basetype->size; - df->locals = locals_end - df->parm_start; -} - -QCC_def_t *QCC_PR_ParseArrayPointer (QCC_def_t *d, pbool allowarrayassign) -{ - QCC_type_t *t; - QCC_def_t *idx; - QCC_def_t *tmp; - QCC_dstatement_t *st; - pbool allowarray; - - t = d->type; - idx = NULL; - while(1) - { - allowarray = false; - if (idx) - allowarray = t->arraysize>0; - else if (!idx) - { - allowarray = d->arraysize || - (d->type->type == ev_pointer) || - (d->type->type == ev_string) || - (d->type->type == ev_vector); - } - - if (allowarray && QCC_PR_CheckToken("[")) - { - tmp = QCC_PR_Expression (TOP_PRIORITY, 0); - QCC_PR_Expect("]"); - - /*if its a pointer that got dereferenced, follow the type*/ - if (!idx && t->type == ev_pointer && !d->arraysize) - t = t->aux_type; - - if (!idx && d->type->type == ev_pointer) - { - /*no bounds checks on pointer dereferences*/ - } - else if (!idx && d->type->type == ev_string && !d->arraysize) - { - /*automatic runtime bounds checks on strings, I'm not going to check this too much...*/ - } - else if (!idx && d->type->type == ev_vector && !d->arraysize) - { - if (tmp->constant) - { - unsigned int i; - if (tmp->type->type == ev_integer) - i = G_INT(tmp->ofs); - else if (tmp->type->type == ev_float) - i = G_FLOAT(tmp->ofs); - if (i < 0 || i >= 3) - QCC_PR_ParseErrorPrintDef(0, d, "(vector) array index out of bounds"); - } - else if (QCC_OPCodeValid(&pr_opcodes[OP_LOADA_F])) - { - tmp = QCC_SupplyConversion(tmp, ev_integer, true); - QCC_PR_SimpleStatement (OP_BOUNDCHECK, tmp->ofs, 3, 0, false); - } - t = type_float; - } - else if (!((!idx)?d->arraysize:t->arraysize)) - { - QCC_PR_ParseErrorPrintDef(0, d, "array index on non-array"); - } - else if (tmp->constant) - { - unsigned int i; - if (tmp->type->type == ev_integer) - i = G_INT(tmp->ofs); - else if (tmp->type->type == ev_float) - i = G_FLOAT(tmp->ofs); - if (i < 0 || i >= ((!idx)?d->arraysize:t->arraysize)) - QCC_PR_ParseErrorPrintDef(0, d, "(constant) array index out of bounds"); - } - else - { - if (QCC_OPCodeValid(&pr_opcodes[OP_LOADA_F])) - { - tmp = QCC_SupplyConversion(tmp, ev_integer, true); - QCC_PR_SimpleStatement (OP_BOUNDCHECK, tmp->ofs, ((!idx)?d->arraysize:t->arraysize), 0, false); - } - } - if (t->size != 1 && (idx || QCC_OPCodeValid(&pr_opcodes[OP_LOADA_F]))) /*don't multiply by type size if the instruction/emulation will do that instead*/ - { - if (tmp->type->type == ev_float) - tmp = QCC_PR_Statement(&pr_opcodes[OP_MUL_F], tmp, QCC_MakeFloatConst(t->size), NULL); - else - tmp = QCC_PR_Statement(&pr_opcodes[OP_MUL_I], tmp, QCC_MakeIntConst(t->size), NULL); - } - - /*calc the new index*/ - if (idx) - idx = QCC_PR_Statement(&pr_opcodes[OP_ADD_I], idx, QCC_SupplyConversion(tmp, ev_integer, true), NULL); - else - idx = tmp; - } - else if ((t->type == ev_pointer || t->type == ev_struct || t->type == ev_union) && (QCC_PR_CheckToken(".") || QCC_PR_CheckToken("->"))) - { - if (!idx && t->type == ev_pointer && !d->arraysize) - t = t->aux_type; - - for (t = t->param; t; t = t->next) - { - if (QCC_PR_CheckName(t->name)) - { - break; - } - - } - if (!t) - QCC_PR_ParseError(0, "%s is not a member", pr_token); - - tmp = QCC_MakeIntConst(t->ofs); - if (idx) - idx = QCC_PR_Statement(&pr_opcodes[OP_ADD_I], idx, tmp, NULL); - else - idx = tmp; - } - else - break; - } - - if (idx) - { - if (d->type->type == ev_pointer) - { - switch (t->type) - { - case ev_pointer: - d = QCC_PR_Statement(&pr_opcodes[OP_LOADP_I], d, QCC_SupplyConversion(idx, ev_integer, true), NULL); - break; - case ev_float: - d = QCC_PR_Statement(&pr_opcodes[OP_LOADP_F], d, QCC_SupplyConversion(idx, ev_integer, true), NULL); - break; - case ev_integer: - d = QCC_PR_Statement(&pr_opcodes[OP_LOADP_I], d, QCC_SupplyConversion(idx, ev_integer, true), NULL); - break; - case ev_string: - d = QCC_PR_Statement(&pr_opcodes[OP_LOADP_S], d, QCC_SupplyConversion(idx, ev_integer, true), NULL); - break; - case ev_vector: - d = QCC_PR_Statement(&pr_opcodes[OP_LOADP_V], d, QCC_SupplyConversion(idx, ev_integer, true), NULL); - break; - case ev_entity: - d = QCC_PR_Statement(&pr_opcodes[OP_LOADP_ENT], d, QCC_SupplyConversion(idx, ev_integer, true), NULL); - break; - case ev_field: - d = QCC_PR_Statement(&pr_opcodes[OP_LOADP_FLD], d, QCC_SupplyConversion(idx, ev_integer, true), NULL); - break; - case ev_function: - d = QCC_PR_Statement(&pr_opcodes[OP_LOADP_FNC], d, QCC_SupplyConversion(idx, ev_integer, true), NULL); - break; - case ev_struct: - case ev_union: - d = QCC_PR_Statement(&pr_opcodes[OP_LOADP_I], d, QCC_SupplyConversion(idx, ev_integer, true), NULL); - break; - - default: - QCC_PR_ParseError(ERR_NOVALIDOPCODES, "No op available. Try assembler"); - } - d->type = t; - } - else if (d->type->type == ev_string && d->arraysize == 0) - { - d = QCC_PR_Statement(&pr_opcodes[OP_LOADP_C], d, QCC_SupplyConversion(idx, ev_float, true), NULL); - } - else if (d->type->type == ev_vector && d->arraysize == 0) - { - d = QCC_PR_Statement(&pr_opcodes[OP_LOADA_F], d, QCC_SupplyConversion(idx, ev_integer, true), NULL); - d->type = type_float; - } - else if (QCC_OPCodeValid(&pr_opcodes[OP_LOADA_F])) - { - /*don't care about assignments. the code can convert an OP_LOADA_F to an OP_ADDRESS on assign*/ - /*source type is a struct, or its an array, or something that can otherwise be directly accessed*/ - switch(t->type) - { - case ev_pointer: - d = QCC_PR_Statement(&pr_opcodes[OP_LOADA_I], d, QCC_SupplyConversion(idx, ev_integer, true), NULL); - break; - case ev_float: - d = QCC_PR_Statement(&pr_opcodes[OP_LOADA_F], d, QCC_SupplyConversion(idx, ev_integer, true), NULL); - break; - case ev_integer: - d = QCC_PR_Statement(&pr_opcodes[OP_LOADA_I], d, QCC_SupplyConversion(idx, ev_integer, true), NULL); - break; - case ev_string: - d = QCC_PR_Statement(&pr_opcodes[OP_LOADA_S], d, QCC_SupplyConversion(idx, ev_integer, true), NULL); - break; - case ev_vector: - d = QCC_PR_Statement(&pr_opcodes[OP_LOADA_V], d, QCC_SupplyConversion(idx, ev_integer, true), NULL); - break; - case ev_entity: - d = QCC_PR_Statement(&pr_opcodes[OP_LOADA_ENT], d, QCC_SupplyConversion(idx, ev_integer, true), NULL); - break; - case ev_field: - d = QCC_PR_Statement(&pr_opcodes[OP_LOADA_FLD], d, QCC_SupplyConversion(idx, ev_integer, true), NULL); - break; - case ev_function: - d = QCC_PR_Statement(&pr_opcodes[OP_LOADA_FNC], d, QCC_SupplyConversion(idx, ev_integer, true), NULL); - break; - case ev_struct: - case ev_union: - //FIXME... - d = QCC_PR_Statement(&pr_opcodes[OP_LOADA_STRUCT], d, QCC_SupplyConversion(idx, ev_integer, true), NULL); - break; - default: - QCC_PR_ParseError(ERR_NOVALIDOPCODES, "No op available. Try assembler"); - } - d->type = t; - } - else if (idx->constant) - { - int cidx; - idx = QCC_SupplyConversion(idx, ev_integer, true); - cidx = G_INT(idx->ofs); - - d->references++; - tmp = (void *)qccHunkAlloc (sizeof(QCC_def_t)); - memcpy (tmp, d, sizeof(QCC_def_t)); - tmp->arraysize = 0; - tmp->ofs = d->ofs + (cidx * type_size[d->type->type]); - d = tmp; - - //d can be assigned to freely - } - else if (allowarrayassign && QCC_PR_CheckToken("=")) - { - /*if its assigned to, generate a functioncall to do the store*/ - QCC_def_t *args[2], *funcretr, *rhs; - - d->references++; - funcretr = QCC_PR_GetDef(type_function, qcva("ArraySet*%s", d->name), NULL, true, 0, false); - - rhs = QCC_PR_Expression(TOP_PRIORITY, 0); - if (rhs->type->type != d->type->type) - QCC_PR_ParseErrorPrintDef(ERR_TYPEMISMATCH, d, "Type Mismatch on array assignment"); - - args[0] = QCC_SupplyConversion(idx, ev_float, true); - args[1] = rhs; - qcc_usefulstatement=true; - d = QCC_PR_GenerateFunctionCall(funcretr, args, 2); - d->type = t; - - return d; - } - else if (QCC_OPCodeValid(&pr_opcodes[OP_FETCH_GBL_F])) - { - if (!d->arraysize) - QCC_PR_ParseErrorPrintDef(ERR_TYPEMISMATCH, d, "array lookup on non-array"); - - if (d->temp) - QCC_PR_ParseErrorPrintDef(ERR_TYPEMISMATCH, d, "array lookup on a temp"); - - /*hexen2 format has opcodes to read arrays (but has no way to write)*/ - switch(t->type) - { - case ev_float: - d = QCC_PR_Statement(&pr_opcodes[OP_FETCH_GBL_F], d, QCC_SupplyConversion(idx, ev_float, true), &st); //get pointer to precise def. -// st->a = d->ofs; - break; - case ev_vector: - d = QCC_PR_Statement(&pr_opcodes[OP_FETCH_GBL_V], d, QCC_SupplyConversion(idx, ev_float, true), &st); //get pointer to precise def. -// st->a = d->ofs; - break; - case ev_string: - d = QCC_PR_Statement(&pr_opcodes[OP_FETCH_GBL_S], d, QCC_SupplyConversion(idx, ev_float, true), &st); //get pointer to precise def. -// st->a = d->ofs; - break; - case ev_entity: - d = QCC_PR_Statement(&pr_opcodes[OP_FETCH_GBL_E], d, QCC_SupplyConversion(idx, ev_float, true), &st); //get pointer to precise def. -// st->a = d->ofs; - break; - case ev_function: - d = QCC_PR_Statement(&pr_opcodes[OP_FETCH_GBL_FNC], d, QCC_SupplyConversion(idx, ev_float, true), &st); //get pointer to precise def. -// st->a = d->ofs; - break; - default: - QCC_PR_ParseError(ERR_NOVALIDOPCODES, "No op available. Try assembler"); - d = NULL; - break; - } - d->type = t; - } - else - { - /*emulate the array access using a function call to do the read for us*/ - QCC_def_t *args[1], *funcretr; - - d->references++; - - /*make sure the function type that we're calling exists*/ - def_parms[0].type = type_float; - funcretr = QCC_PR_GetDef(type_function, qcva("ArrayGet*%s", d->name), NULL, true, 0, false); - - args[0] = QCC_SupplyConversion(idx, ev_float, true); - d = QCC_PR_GenerateFunctionCall(funcretr, args, 1); - d->type = t; - } - - /*parse recursively*/ - d = QCC_PR_ParseArrayPointer(d, allowarrayassign); - } - - return d; -} - -/* -============ -PR_ParseValue - -Returns the global ofs for the current token -============ -*/ -QCC_def_t *QCC_PR_ParseValue (QCC_type_t *assumeclass, pbool allowarrayassign) -{ - QCC_def_t *d, *od, *tmp; - QCC_type_t *t; - char *name; - - char membername[2048]; - -// if the token is an immediate, allocate a constant for it - if (pr_token_type == tt_immediate) - return QCC_PR_ParseImmediate (); - - if (QCC_PR_CheckToken("[")) //reacc support - { //looks like a funky vector. :) - vec3_t v; - pr_immediate_type = type_vector; - v[0] = pr_immediate._float; - QCC_PR_Lex(); - v[1] = pr_immediate._float; - QCC_PR_Lex(); - v[2] = pr_immediate._float; - pr_immediate.vector[0] = v[0]; - pr_immediate.vector[1] = v[1]; - pr_immediate.vector[2] = v[2]; - pr_immediate_type = type_vector; - d = QCC_PR_ParseImmediate(); - QCC_PR_Expect("]"); - return d; - } - name = QCC_PR_ParseName (); - - if (assumeclass && assumeclass->parentclass) // 'testvar' becomes 'self::testvar' - { //try getting a member. - QCC_type_t *type; - type = assumeclass; - d = NULL; - while(type != type_entity && type) - { - sprintf(membername, "%s::"MEMBERFIELDNAME, type->name, name); - d = QCC_PR_GetDef (NULL, membername, pr_scope, false, 0, false); - if (d) - break; - - type = type->parentclass; - } - if (!d) - d = QCC_PR_GetDef (NULL, name, pr_scope, false, 0, false); - } - else - { - // look through the defs - d = QCC_PR_GetDef (NULL, name, pr_scope, false, 0, false); - } - - if (!d) - { - if ( (!strcmp(name, "random" )) || - (!strcmp(name, "randomv")) || - (!strcmp(name, "sizeof")) || - (!strcmp(name, "entnum")) || - (!strcmp(name, "_"))) //intrinsics, any old function with no args will do. - { - d = QCC_PR_GetDef (type_function, name, NULL, true, 0, false); - d->initialized = 0; - } - else if (keyword_class && !strcmp(name, "this")) - { - if (!pr_classtype) - QCC_PR_ParseError(ERR_NOTANAME, "Cannot use 'this' outside of an OO function\n"); - od = QCC_PR_GetDef(NULL, "self", NULL, true, 0, false); - d = QCC_PR_DummyDef(pr_classtype, "this", pr_scope, 0, od->ofs, true, false); - } - else if (keyword_class && !strcmp(name, "super")) - { - if (!pr_classtype) - QCC_PR_ParseError(ERR_NOTANAME, "Cannot use 'super' outside of an OO function\n"); - od = QCC_PR_GetDef(NULL, "self", NULL, true, 0, false); - d = QCC_PR_DummyDef(pr_classtype, "super", pr_scope, 0, od->ofs, true, false); - } - else - { - d = QCC_PR_GetDef (type_variant, name, pr_scope, true, 0, false); - if (!d) - QCC_PR_ParseError (ERR_UNKNOWNVALUE, "Unknown value \"%s\"", name); - else - { - QCC_PR_ParseWarning (ERR_UNKNOWNVALUE, "Unknown value \"%s\".", name); - } - } - } - - d = QCC_PR_ParseArrayPointer(d, allowarrayassign); - - t = d->type; - if (keyword_class && t->type == ev_entity && t->parentclass && (QCC_PR_CheckToken(".") || QCC_PR_CheckToken("->"))) - { - QCC_def_t *field; - if (QCC_PR_CheckToken("(")) - { - field = QCC_PR_Expression(TOP_PRIORITY, 0); - QCC_PR_Expect(")"); - } - else - field = QCC_PR_ParseValue(d->type, false); - if (field->type->type == ev_field) - { - if (!field->type->aux_type) - { - QCC_PR_ParseWarning(ERR_INTERNAL, "Field with null aux_type"); - d = QCC_PR_Statement(&pr_opcodes[OP_LOAD_FLD], d, field, NULL); - } - else - { - switch(field->type->aux_type->type) - { - default: - QCC_PR_ParseError(ERR_INTERNAL, "Bad field type"); - break; - case ev_integer: - d = QCC_PR_Statement(&pr_opcodes[OP_LOAD_I], d, field, NULL); - break; - case ev_field: - d = QCC_PR_Statement(&pr_opcodes[OP_LOAD_FLD], d, field, NULL); - tmp = (void *)qccHunkAlloc (sizeof(QCC_def_t)); - memset (tmp, 0, sizeof(QCC_def_t)); - tmp->type = field->type->aux_type; - tmp->ofs = d->ofs; - tmp->temp = d->temp; - tmp->constant = false; - tmp->name = d->name; - d = tmp; - break; - case ev_float: - d = QCC_PR_Statement(&pr_opcodes[OP_LOAD_F], d, field, NULL); - break; - case ev_string: - d = QCC_PR_Statement(&pr_opcodes[OP_LOAD_S], d, field, NULL); - break; - case ev_vector: - d = QCC_PR_Statement(&pr_opcodes[OP_LOAD_V], d, field, NULL); - break; - case ev_function: - d = QCC_PR_Statement(&pr_opcodes[OP_LOAD_FNC], d, field, NULL); - tmp = (void *)qccHunkAlloc (sizeof(QCC_def_t)); - memset (tmp, 0, sizeof(QCC_def_t)); - tmp->type = field->type->aux_type; - tmp->ofs = d->ofs; - tmp->temp = d->temp; - tmp->constant = false; - tmp->name = d->name; - d = tmp; - break; - case ev_entity: - d = QCC_PR_Statement(&pr_opcodes[OP_LOAD_ENT], d, field, NULL); - break; - } - } - } - else - QCC_PR_IncludeChunk(".", false, NULL); - } - - return d; -} - - -/* -============ -PR_Term -============ -*/ -QCC_def_t *QCC_PR_Term (void) -{ - QCC_def_t *e, *e2; - etype_t t; - if (pr_token_type == tt_punct) //a little extra speed... - { - if (QCC_PR_CheckToken("++")) - { - qcc_usefulstatement=true; - e = QCC_PR_Term (); - if (e->constant) - QCC_PR_ParseWarning(WARN_ASSIGNMENTTOCONSTANT, "Assignment to constant %s", e->name); - if (e->temp) - QCC_PR_ParseWarning(WARN_ASSIGNMENTTOCONSTANT, "Hey! That's a temp! ++ operators cannot work on temps!"); - switch (e->type->type) - { - case ev_integer: - QCC_PR_Statement3(&pr_opcodes[OP_ADD_I], e, QCC_MakeIntConst(1), e, false); - break; - case ev_float: - QCC_PR_Statement3(&pr_opcodes[OP_ADD_F], e, QCC_MakeFloatConst(1), e, false); - break; - default: - QCC_PR_ParseError(ERR_BADPLUSPLUSOPERATOR, "++ operator on unsupported type"); - break; - } - return e; - } - else if (QCC_PR_CheckToken("--")) - { - qcc_usefulstatement=true; - e = QCC_PR_Term (); - if (e->constant) - QCC_PR_ParseWarning(WARN_ASSIGNMENTTOCONSTANT, "Assignment to constant %s", e->name); - if (e->temp) - QCC_PR_ParseWarning(WARN_ASSIGNMENTTOCONSTANT, "Hey! That's a temp! -- operators cannot work on temps!"); - switch (e->type->type) - { - case ev_integer: - QCC_PR_Statement3(&pr_opcodes[OP_SUB_I], e, QCC_MakeIntConst(1), e, false); - break; - case ev_float: - QCC_PR_Statement3(&pr_opcodes[OP_SUB_F], e, QCC_MakeFloatConst(1), e, false); - break; - default: - QCC_PR_ParseError(ERR_BADPLUSPLUSOPERATOR, "-- operator on unsupported type"); - break; - } - return e; - } - - if (QCC_PR_CheckToken ("!")) - { - e = QCC_PR_Expression (NOT_PRIORITY, EXPR_DISALLOW_COMMA|EXPR_WARN_ABOVE_1); - t = e->type->type; - if (t == ev_float) - e2 = QCC_PR_Statement (&pr_opcodes[OP_NOT_F], e, 0, NULL); - else if (t == ev_string) - e2 = QCC_PR_Statement (&pr_opcodes[OP_NOT_S], e, 0, NULL); - else if (t == ev_entity) - e2 = QCC_PR_Statement (&pr_opcodes[OP_NOT_ENT], e, 0, NULL); - else if (t == ev_vector) - e2 = QCC_PR_Statement (&pr_opcodes[OP_NOT_V], e, 0, NULL); - else if (t == ev_function) - e2 = QCC_PR_Statement (&pr_opcodes[OP_NOT_FNC], e, 0, NULL); - else if (t == ev_integer) - e2 = QCC_PR_Statement (&pr_opcodes[OP_NOT_FNC], e, 0, NULL); //functions are integer values too. - else if (t == ev_pointer) - e2 = QCC_PR_Statement (&pr_opcodes[OP_NOT_FNC], e, 0, NULL); //Pointers are too. - else - { - e2 = NULL; // shut up compiler warning; - QCC_PR_ParseError (ERR_BADNOTTYPE, "type mismatch for !"); - } - return e2; - } - - else if (QCC_PR_CheckToken ("&")) - { - int st = numstatements; - e = QCC_PR_Expression (UNARY_PRIORITY, EXPR_DISALLOW_COMMA); - t = e->type->type; - - if (st != numstatements) - //woo, something like ent.field? - { - if ((OP_LOAD_F <= statements[numstatements-1].op && statements[numstatements-1].op <= OP_LOAD_FNC) || statements[numstatements-1].op == OP_LOAD_I || statements[numstatements-1].op == OP_LOAD_P) - { - statements[numstatements-1].op = OP_ADDRESS; - e->type = QCC_PR_PointerType(e->type); - return e; - } - else if (OP_LOADA_F <= statements[numstatements-1].op && statements[numstatements-1].op <= OP_LOADA_I) - { - statements[numstatements-1].op = OP_GLOBALADDRESS; - e->type = QCC_PR_PointerType(e->type); - return e; - } - else if (OP_LOADP_F <= statements[numstatements-1].op && statements[numstatements-1].op <= OP_LOADP_I) - { - statements[numstatements-1].op = OP_POINTER_ADD; - e->type = QCC_PR_PointerType(e->type); - return e; - } - else //this is a restriction that could be lifted, I just want to make sure that I got all the bits first. - { - QCC_PR_ParseError (ERR_BADNOTTYPE, "type mismatch for '&' Must be singular expression or field reference"); - return e; - } - } -// QCC_PR_ParseWarning(0, "debug: &global"); - - if (!QCC_OPCodeValid(&pr_opcodes[OP_GLOBALADDRESS])) - QCC_PR_ParseError (ERR_BADEXTENSION, "Cannot use addressof operator ('&') on a global. Please use the FTE target."); - - e2 = QCC_PR_Statement (&pr_opcodes[OP_GLOBALADDRESS], e, 0, NULL); - e2->type = QCC_PR_PointerType(e->type); - return e2; - } - else if (QCC_PR_CheckToken ("*")) - { - e = QCC_PR_Expression (UNARY_PRIORITY, EXPR_DISALLOW_COMMA); - t = e->type->type; - - if (t == ev_string) - e2 = QCC_PR_Statement(&pr_opcodes[OP_LOADP_C], e, QCC_MakeFloatConst(0), NULL); - else if (t == ev_pointer) - { - switch(e->type->aux_type->type) - { - case ev_float: - e2 = QCC_PR_Statement (&pr_opcodes[OP_LOADP_F], e, 0, NULL); - break; - case ev_string: - e2 = QCC_PR_Statement (&pr_opcodes[OP_LOADP_S], e, 0, NULL); - break; - case ev_vector: - e2 = QCC_PR_Statement (&pr_opcodes[OP_LOADP_V], e, 0, NULL); - break; - case ev_entity: - e2 = QCC_PR_Statement (&pr_opcodes[OP_LOADP_ENT], e, 0, NULL); - break; - case ev_field: - e2 = QCC_PR_Statement (&pr_opcodes[OP_LOADP_FLD], e, 0, NULL); - break; - case ev_function: - e2 = QCC_PR_Statement (&pr_opcodes[OP_LOADP_FLD], e, 0, NULL); - break; - case ev_integer: - e2 = QCC_PR_Statement (&pr_opcodes[OP_LOADP_I], e, 0, NULL); - break; - case ev_pointer: - e2 = QCC_PR_Statement (&pr_opcodes[OP_LOADP_I], e, 0, NULL); - break; - - default: - QCC_PR_ParseError (ERR_BADNOTTYPE, "type mismatch for * (unrecognised type)"); - e2 = NULL; - break; - } - e2->type = e->type->aux_type; - } - else - QCC_PR_ParseError (ERR_BADNOTTYPE, "type mismatch for *"); - return e2; - } - else if (QCC_PR_CheckToken ("-")) - { - e = QCC_PR_Expression (UNARY_PRIORITY, EXPR_DISALLOW_COMMA); - - switch(e->type->type) - { - case ev_float: - e2 = QCC_PR_Statement (&pr_opcodes[OP_SUB_F], QCC_MakeFloatConst(0), e, NULL); - break; - case ev_vector: - e2 = QCC_PR_Statement (&pr_opcodes[OP_SUB_V], QCC_MakeVectorConst(0, 0, 0), e, NULL); - break; - case ev_integer: - e2 = QCC_PR_Statement (&pr_opcodes[OP_SUB_I], QCC_MakeIntConst(0), e, NULL); - break; - default: - QCC_PR_ParseError (ERR_BADNOTTYPE, "type mismatch for -"); - e2 = NULL; - break; - } - return e2; - } - else if (QCC_PR_CheckToken ("+")) - { - e = QCC_PR_Expression (UNARY_PRIORITY, EXPR_DISALLOW_COMMA); - - switch(e->type->type) - { - case ev_float: - e2 = e; - break; - case ev_vector: - e2 = e; - break; - case ev_integer: - e2 = e; - break; - default: - QCC_PR_ParseError (ERR_BADNOTTYPE, "type mismatch for +"); - e2 = NULL; - break; - } - return e2; - } - - if (QCC_PR_CheckToken ("(")) - { - QCC_type_t *newtype; - newtype = QCC_PR_ParseType(false, true); - if (newtype) - { - QCC_PR_Expect (")"); - e = QCC_PR_Expression (UNARY_PRIORITY, EXPR_DISALLOW_COMMA); - - /*you may cast from a type to itself*/ - if (!typecmp(e->type, newtype)) - { - } - /*you may cast from const 0 to any type of same size for free (from either int or float for simplicity)*/ - else if (newtype->size == e->type->size && (e->type->type == ev_integer || e->type->type == ev_float) && e->constant && !G_INT(e->ofs)) - { - //direct cast - e2 = (void *)qccHunkAlloc (sizeof(QCC_def_t)); - memset (e2, 0, sizeof(QCC_def_t)); - - e2->type = newtype; - e2->ofs = e->ofs; - e2->constant = true; - e2->temp = e->temp; - return e2; - } - /*cast from int->float will convert*/ - else if (newtype->type == ev_float && e->type->type == ev_integer) - return QCC_PR_Statement (&pr_opcodes[OP_CONV_ITOF], e, 0, NULL); - /*cast from float->int will convert*/ - else if (newtype->type == ev_integer && e->type->type == ev_float) - return QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], e, 0, NULL); - /*you may freely cast between pointers (and ints, as this is explicit) (strings count as pointers - WARNING: some strings may not be expressable as pointers)*/ - else if ( - //pointers - ((newtype->type == ev_pointer || newtype->type == ev_string || newtype->type == ev_integer) && (e->type->type == ev_pointer || e->type->type == ev_string || e->type->type == ev_integer)) - //ents/classs - || (newtype->type == ev_entity && e->type->type == ev_entity) - //variants are fine too - || (newtype->type == ev_variant || e->type->type == ev_variant) - ) - { - //direct cast - e2 = (void *)qccHunkAlloc (sizeof(QCC_def_t)); - memset (e2, 0, sizeof(QCC_def_t)); - - e2->type = newtype; - e2->ofs = e->ofs; - e2->constant = true; - e2->temp = e->temp; - return e2; - } - else - QCC_PR_ParseError(0, "Bad type cast\n"); - } -/* else if (QCC_PR_CheckToken("*")) - { - QCC_PR_Expect (")"); - e = QCC_PR_Term(); - e2 = (void *)qccHunkAlloc (sizeof(QCC_def_t)); - memset (e2, 0, sizeof(QCC_def_t)); - e2->type = type_pointer; - e2->ofs = e->ofs; - e2->constant = true; - e2->temp = e->temp; - return e2; - } - else if (QCC_PR_CheckKeyword(keyword_float, "float")) //check for type casts - { - QCC_PR_Expect (")"); - e = QCC_PR_Term(); - if (e->type->type == ev_float) - return e; - else if (e->type->type == ev_integer) - return QCC_PR_Statement (&pr_opcodes[OP_CONV_ITOF], e, 0, NULL); - else if (e->type->type == ev_function) - return e; - // else - // QCC_PR_ParseError ("invalid typecast"); - - QCC_PR_ParseWarning (0, "Not all vars make sence as floats"); - - e2 = (void *)qccHunkAlloc (sizeof(QCC_def_t)); - memset (e2, 0, sizeof(QCC_def_t)); - e2->type = type_float; - e2->ofs = e->ofs; - e2->constant = true; - e2->temp = e->temp; - return e2; - } - else if (QCC_PR_CheckKeyword(keyword_class, "class")) - { - QCC_type_t *classtype = QCC_TypeForName(QCC_PR_ParseName()); - if (!classtype) - QCC_PR_ParseError(ERR_NOTANAME, "Class not defined for cast"); - - QCC_PR_Expect (")"); - e = QCC_PR_Term(); - e2 = (void *)qccHunkAlloc (sizeof(QCC_def_t)); - memset (e2, 0, sizeof(QCC_def_t)); - e2->type = classtype; - e2->ofs = e->ofs; - e2->constant = true; - e2->temp = e->temp; - return e2; - } - else if (QCC_PR_CheckKeyword(keyword_integer, "integer")) //check for type casts - { - QCC_PR_Expect (")"); - e = QCC_PR_Term(); - if (e->type->type == ev_integer) - return e; - else if (e->type->type == ev_float) - return QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], e, 0, NULL); - else - QCC_PR_ParseError (ERR_BADTYPECAST, "invalid typecast"); - } - else if (QCC_PR_CheckKeyword(keyword_int, "int")) //check for type casts - { - QCC_PR_Expect (")"); - e = QCC_PR_Term(); - if (e->type->type == ev_integer) - return e; - else if (e->type->type == ev_float) - return QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], e, 0, NULL); - else - QCC_PR_ParseError (ERR_BADTYPECAST, "invalid typecast"); - } -*/ else - { - pbool oldcond = conditional; - conditional = conditional?2:0; - e = QCC_PR_Expression (TOP_PRIORITY, 0); - QCC_PR_Expect (")"); - conditional = oldcond; - } - return e; - } - } - return QCC_PR_ParseValue (pr_classtype, true); -} - - -int QCC_canConv(QCC_def_t *from, etype_t to) -{ - if (from->type->type == to) - return 0; - - if (from->type->type == ev_vector && to == ev_float) - return 4; - - if (pr_classtype) - { - if (from->type->type == ev_field) - { - if (from->type->aux_type->type == to) - return 1; - } - } - - if (from->type->type == ev_variant) - return 3; - -/* if (from->type->type == ev_pointer && from->type->aux_type->type == to) - return 1; - - if (QCC_ShouldConvert(from, to)>=0) - return 1; -*/ - if (from->type->type == ev_integer && to == ev_function) - return 1; - - if (from->constant && from->arraysize == 0 && (from->type->type == ev_integer || from->type->type == ev_float) && !G_INT(from->ofs)) - return 2; - - return -100; -} -/* -============== -PR_Expression -============== -*/ - -QCC_def_t *QCC_PR_Expression (int priority, int exprflags) -{ - QCC_dstatement32_t *st; - QCC_opcode_t *op, *oldop; - - QCC_opcode_t *bestop; - int numconversions, c; - - int opnum; - - QCC_def_t *e, *e2; - etype_t type_a, type_b, type_c; - - if (priority == 0) - return QCC_PR_Term (); - - e = QCC_PR_Expression (priority-1, exprflags); - - while (1) - { - if (priority == FUNC_PRIORITY) - { - if (QCC_PR_CheckToken ("(") ) - { - qcc_usefulstatement=true; - e = QCC_PR_ParseFunctionCall (e); - } - if (QCC_PR_CheckToken ("?")) - { - QCC_dstatement32_t *fromj, *elsej; - QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_IFNOT_I], e, NULL, &fromj)); - e = QCC_PR_Expression(TOP_PRIORITY, 0); - e2 = QCC_GetTemp(e->type); - QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[(e2->type->size>=3)?OP_STORE_V:OP_STORE_F], e, e2, NULL)); - //e2 can be stomped upon until its reused anyway - QCC_UnFreeTemp(e2); - - QCC_PR_Expect(":"); - QCC_PR_Statement(&pr_opcodes[OP_GOTO], NULL, NULL, &elsej); - fromj->b = &statements[numstatements] - fromj; - e = QCC_PR_Expression(TOP_PRIORITY, 0); - - if (typecmp(e->type, e2->type) != 0) - QCC_PR_ParseError(0, "Ternary operator with mismatching types\n"); - QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[(e2->type->size>=3)?OP_STORE_V:OP_STORE_F], e, e2, NULL)); - QCC_UnFreeTemp(e2); - - elsej->a = &statements[numstatements] - elsej; - return e2; - } - } - - opnum=0; - - if (pr_token_type == tt_immediate) - { - if ((pr_immediate_type->type == ev_float && pr_immediate._float < 0) || - (pr_immediate_type->type == ev_integer && pr_immediate._int < 0)) //hehehe... was a minus all along... - { - QCC_PR_IncludeChunk(pr_token, true, NULL); - strcpy(pr_token, "+");//two negatives would make a positive. - pr_token_type = tt_punct; - } - } - - if (pr_token_type != tt_punct) - { - QCC_PR_ParseWarning(WARN_UNEXPECTEDPUNCT, "Expected punctuation"); - } - - //go straight for the correct priority. - for (op = opcodeprioritized[priority][opnum]; op; op = opcodeprioritized[priority][++opnum]) -// for (op=pr_opcodes ; op->name ; op++) - { -// if (op->priority != priority) -// continue; - if (!QCC_PR_CheckToken (op->name)) - continue; - st = NULL; - if ( op->associative!=ASSOC_LEFT ) - { - // if last statement is an indirect, change it to an address of - if (!simplestore && ((unsigned)(statements[numstatements-1].op - OP_LOAD_F) < 6 || statements[numstatements-1].op == OP_LOAD_I || statements[numstatements-1].op == OP_LOAD_P) && statements[numstatements-1].c == e->ofs) - { - qcc_usefulstatement=true; - statements[numstatements-1].op = OP_ADDRESS; - type_pointer->aux_type->type = e->type->type; - e->type = type_pointer; - } - //if last statement retrieved a value, switch it to retrieve a usable pointer. - if ( !simplestore && (unsigned)(statements[numstatements-1].op - OP_LOADA_F) < 7)// || statements[numstatements-1].op == OP_LOADA_C) - { - statements[numstatements-1].op = OP_GLOBALADDRESS; - type_pointer->aux_type->type = e->type->type; - e->type = type_pointer; - } - if ( !simplestore && (unsigned)(statements[numstatements-1].op - OP_LOADP_F) < 7 && statements[numstatements-1].c == e->ofs) - { - if (!statements[numstatements-1].b) - { - //if the loadp has no offset, remove the instruction and convert the dest of this instruction directly to the pointer's load address - //this kills the add 0. - e->ofs = statements[numstatements-1].a; - numstatements--; - } - else - { - statements[numstatements-1].op = OP_POINTER_ADD; - } - if (e->type != type_pointer) - { - type_pointer->aux_type->type = e->type->type; - e->type = type_pointer; - } - } - if ( !simplestore && statements[numstatements-1].op == OP_LOADP_C && e->ofs == statements[numstatements-1].c) - { - statements[numstatements-1].op = OP_ADD_SF; - e->type = type_string; - - //now we want to make sure that string = float can't work without it being a dereferenced pointer. (we don't want to allow storep_c without dereferece) - e2 = QCC_PR_Expression (priority, exprflags); - if (e2->type->type == ev_float) - op = &pr_opcodes[OP_STOREP_C]; - } - else - e2 = QCC_PR_Expression (priority, exprflags); - } - else - { - if (op->priority == 7 && opt_logicops) - { - optres_logicops++; - st = &statements[numstatements]; - if (*op->name == '&') //statement 3 because we don't want to optimise this into if from not ifnot - QCC_PR_Statement3(&pr_opcodes[OP_IFNOT_I], e, NULL, NULL, false); - else - QCC_PR_Statement3(&pr_opcodes[OP_IF_I], e, NULL, NULL, false); - } - - e2 = QCC_PR_Expression (priority-1, exprflags); - } - - // type check - type_a = e->type->type; - type_b = e2->type->type; - -// if (type_a == ev_pointer && type_b == ev_pointer) -// QCC_PR_ParseWarning(0, "Debug: pointer op pointer"); - - if (op->name[0] == '.')// field access gets type from field - { - if (e2->type->aux_type) - type_c = e2->type->aux_type->type; - else - type_c = -1; // not a field - } - else - type_c = ev_void; - - oldop = op; - bestop = NULL; - numconversions = 32767; - while (op) - { - if (!(type_c != ev_void && type_c != (*op->type_c)->type)) - { - if (!STRCMP (op->name , oldop->name)) //matches - { - //return values are never converted - what to? - // if (type_c != ev_void && type_c != op->type_c->type->type) - // { - // op++; - // continue; - // } - - if (op->associative!=ASSOC_LEFT) - {//assignment - if (op->type_a == &type_pointer) //ent var - { - /*FIXME: I don't like this code*/ - if (e->type->type != ev_pointer) - c = -200; //don't cast to a pointer. - else if ((*op->type_c)->type == ev_void && op->type_b == &type_pointer && e2->type->type == ev_pointer) - c = 0; //generic pointer... fixme: is this safe? make sure both sides are equivelent - else if (e->type->aux_type->type != (*op->type_b)->type) //if e isn't a pointer to a type_b - c = -200; //don't let the conversion work - else - c = QCC_canConv(e2, (*op->type_c)->type); - } - else - { - c=QCC_canConv(e2, (*op->type_b)->type); - if (type_a != (*op->type_a)->type) //in this case, a is the final assigned value - c = -300; //don't use this op, as we must not change var b's type - else if ((*op->type_a)->type == ev_pointer && e->type->aux_type->type != (*op->type_a)->aux_type->type) - c = -300; //don't use this op if its a pointer to a different type - } - } - else - { - if (op->type_a == &type_pointer) //ent var - { - if (e2->type->type != ev_pointer || e2->type->aux_type->type != (*op->type_b)->type) //if e isn't a pointer to a type_b - c = -200; //don't let the conversion work - else - c = 0; - } - else - { - c=QCC_canConv(e, (*op->type_a)->type); - c+=QCC_canConv(e2, (*op->type_b)->type); - } - } - - if (c>=0 && c < numconversions) - { - bestop = op; - numconversions=c; - if (c == 0)//can't get less conversions than 0... - break; - } - } - else - break; - } - op = opcodeprioritized[priority][++opnum]; - } - if (bestop == NULL) - { - if (oldop->priority == CONDITION_PRIORITY) - op = oldop; - else - { - if (flag_laxcasts) - { - op = oldop; - QCC_PR_ParseWarning(WARN_LAXCAST, "type mismatch for %s (%s and %s)", oldop->name, e->type->name, e2->type->name); - } - else - QCC_PR_ParseError (ERR_TYPEMISMATCH, "type mismatch for %s (%s and %s)", oldop->name, e->type->name, e2->type->name); - } - } - else - { - if (numconversions>3) - QCC_PR_ParseWarning(WARN_IMPLICITCONVERSION, "Implicit conversion"); - op = bestop; - } - -// if (type_a == ev_pointer && type_b != e->type->aux_type->type) -// QCC_PR_ParseError ("type mismatch for %s", op->name); - - if (st) - st->b = &statements[numstatements] - st; - - - if (op->associative!=ASSOC_LEFT) - { - qcc_usefulstatement = true; - if (e->constant || e->ofs < OFS_PARM0) - { - if (e->type->type == ev_function) - { - QCC_PR_ParseWarning(WARN_ASSIGNMENTTOCONSTANTFUNC, "Assignment to function %s", e->name); - QCC_PR_ParsePrintDef(WARN_ASSIGNMENTTOCONSTANTFUNC, e); - } - else - { - QCC_PR_ParseWarning(WARN_ASSIGNMENTTOCONSTANT, "Assignment to constant %s", e->name); - QCC_PR_ParsePrintDef(WARN_ASSIGNMENTTOCONSTANT, e); - } -#ifndef QCC - editbadfile(strings+s_file, pr_source_line); -#endif - } - if (conditional&1) - QCC_PR_ParseWarning(WARN_ASSIGNMENTINCONDITIONAL, "Assignment in conditional"); - - e = QCC_PR_Statement (op, e2, e, NULL); - } - else - e = QCC_PR_Statement (op, e, e2, NULL); - - if (type_c != ev_void/* && type_c != ev_string*/) // field access gets type from field - e->type = e2->type->aux_type; - - if (priority > 1 && exprflags & EXPR_WARN_ABOVE_1) - QCC_PR_ParseWarning(0, "You may wish to add brackets after that ! operator"); - - break; - } - if (!op) - { - if (e == NULL) - QCC_PR_ParseError(ERR_INTERNAL, "e == null"); - - - if (!STRCMP(pr_token, "++")) - { - //if the last statement was an ent.float (or something) - if (((unsigned)(statements[numstatements-1].op - OP_LOAD_F) < 6 || statements[numstatements-1].op == OP_LOAD_I) && statements[numstatements-1].c == e->ofs) - { //we have our load. - QCC_def_t *e3; -//the only inefficiency here is with an extra temp (we can't reuse the original) -//this is not a problem, as the optimise temps or locals marshalling can clean these up for us - qcc_usefulstatement=true; -//load -//add to temp -//store temp to offset -//return original loaded (which is not at the same offset as the pointer we store to) - e2 = QCC_GetTemp(type_float); - e3 = QCC_GetTemp(type_pointer); - QCC_PR_SimpleStatement(OP_ADDRESS, statements[numstatements-1].a, statements[numstatements-1].b, e3->ofs, false); - if (e->type->type == ev_float) - { - QCC_PR_Statement3(&pr_opcodes[OP_ADD_F], e, QCC_MakeFloatConst(1), e2, false); - QCC_PR_Statement3(&pr_opcodes[OP_STOREP_F], e2, e3, NULL, false); - } - else if (e->type->type == ev_integer) - { - QCC_PR_Statement3(&pr_opcodes[OP_ADD_I], e, QCC_MakeIntConst(1), e2, false); - QCC_PR_Statement3(&pr_opcodes[OP_STOREP_I], e2, e3, NULL, false); - } - else - { - QCC_PR_ParseError(ERR_PARSEERRORS, "-- suffix operator results in nonstandard behaviour. Use -=1 or prefix form instead"); - QCC_PR_IncludeChunk("-=1", false, NULL); - } - QCC_FreeTemp(e2); - QCC_FreeTemp(e3); - } - else if (e->type->type == ev_float) - { -//copy to temp -//add to original -//return temp (which == original) - QCC_PR_ParseWarning(WARN_INEFFICIENTPLUSPLUS, "++ suffix operator results in inefficient behaviour. Use +=1 or prefix form instead"); - qcc_usefulstatement=true; - - e2 = QCC_GetTemp(type_float); - QCC_PR_Statement3(&pr_opcodes[OP_STORE_F], e, e2, NULL, false); - QCC_PR_Statement3(&pr_opcodes[OP_ADD_F], e, QCC_MakeFloatConst(1), e, false); - QCC_FreeTemp(e); - e = e2; - } - else if (e->type->type == ev_integer) - { - QCC_PR_ParseWarning(WARN_INEFFICIENTPLUSPLUS, "++ suffix operator results in inefficient behaviour. Use +=1 or prefix form instead"); - qcc_usefulstatement=true; - - e2 = QCC_GetTemp(type_integer); - QCC_PR_Statement3(&pr_opcodes[OP_STORE_I], e, e2, NULL, false); - QCC_PR_Statement3(&pr_opcodes[OP_ADD_I], e, QCC_MakeIntConst(1), e, false); - QCC_FreeTemp(e); - e = e2; - } - else - { - QCC_PR_ParseWarning(WARN_NOTSTANDARDBEHAVIOUR, "++ suffix operator results in nonstandard behaviour. Use +=1 or prefix form instead"); - QCC_PR_IncludeChunk("+=1", false, NULL); - } - QCC_PR_Lex(); - } - else if (!STRCMP(pr_token, "--")) - { - if (((unsigned)(statements[numstatements-1].op - OP_LOAD_F) < 6 || statements[numstatements-1].op == OP_LOAD_I) && statements[numstatements-1].c == e->ofs) - { //we have our load. - QCC_def_t *e3; -//load -//add to temp -//store temp to offset -//return original loaded (which is not at the same offset as the pointer we store to) - e2 = QCC_GetTemp(type_float); - e3 = QCC_GetTemp(type_pointer); - QCC_PR_SimpleStatement(OP_ADDRESS, statements[numstatements-1].a, statements[numstatements-1].b, e3->ofs, false); - if (e->type->type == ev_float) - { - QCC_PR_Statement3(&pr_opcodes[OP_SUB_F], e, QCC_MakeFloatConst(1), e2, false); - QCC_PR_Statement3(&pr_opcodes[OP_STOREP_F], e2, e3, NULL, false); - } - else if (e->type->type == ev_integer) - { - QCC_PR_Statement3(&pr_opcodes[OP_SUB_I], e, QCC_MakeIntConst(1), e2, false); - QCC_PR_Statement3(&pr_opcodes[OP_STOREP_I], e2, e3, NULL, false); - } - else - { - QCC_PR_ParseError(ERR_PARSEERRORS, "-- suffix operator results in nonstandard behaviour. Use -=1 or prefix form instead"); - QCC_PR_IncludeChunk("-=1", false, NULL); - } - QCC_FreeTemp(e2); - QCC_FreeTemp(e3); - } - else if (e->type->type == ev_float) - { - QCC_PR_ParseWarning(WARN_INEFFICIENTPLUSPLUS, "-- suffix operator results in inefficient behaviour. Use -=1 or prefix form instead"); - qcc_usefulstatement=true; - - e2 = QCC_GetTemp(type_float); - QCC_PR_Statement3(&pr_opcodes[OP_STORE_F], e, e2, NULL, false); - QCC_PR_Statement3(&pr_opcodes[OP_SUB_F], e, QCC_MakeFloatConst(1), e, false); - QCC_FreeTemp(e); - e = e2; - } - else if (e->type->type == ev_integer) - { - QCC_PR_ParseWarning(WARN_INEFFICIENTPLUSPLUS, "-- suffix operator results in inefficient behaviour. Use -=1 or prefix form instead"); - qcc_usefulstatement=true; - - e2 = QCC_GetTemp(type_integer); - QCC_PR_Statement3(&pr_opcodes[OP_STORE_I], e, e2, NULL, false); - QCC_PR_Statement3(&pr_opcodes[OP_SUB_I], e, QCC_MakeIntConst(1), e, false); - QCC_FreeTemp(e); - e = e2; - } - else - { - QCC_PR_ParseWarning(WARN_NOTSTANDARDBEHAVIOUR, "-- suffix operator results in nonstandard behaviour. Use -=1 or prefix form instead"); - QCC_PR_IncludeChunk("-=1", false, NULL); - } - QCC_PR_Lex(); - } - break; // next token isn't at this priority level - } - } - if (e == NULL) - QCC_PR_ParseError(ERR_INTERNAL, "e == null"); - - if (!(exprflags&EXPR_DISALLOW_COMMA) && priority == TOP_PRIORITY && QCC_PR_CheckToken (",")) - { - if (!qcc_usefulstatement) - QCC_PR_ParseWarning(WARN_POINTLESSSTATEMENT, "Effectless statement"); - - QCC_FreeTemp(e); - qcc_usefulstatement = false; - e = QCC_PR_Expression(TOP_PRIORITY, exprflags); - } - - return e; -} - -int QCC_PR_IntConstExpr(void) -{ - QCC_def_t *def = QCC_PR_Expression(TOP_PRIORITY, 0); - if (def->constant) - { - def->references++; - if (def->type->type == ev_integer) - return G_INT(def->ofs); - if (def->type->type == ev_float) - { - int i = G_FLOAT(def->ofs); - if ((float)i == G_FLOAT(def->ofs)) - return i; - } - } - QCC_PR_ParseError(ERR_NOTACONSTANT, "Value is not an integer constant"); - return true; -} - -void QCC_PR_GotoStatement (QCC_dstatement_t *patch2, char *labelname) -{ - if (num_gotos >= max_gotos) - { - max_gotos += 8; - pr_gotos = realloc(pr_gotos, sizeof(*pr_gotos)*max_gotos); - } - - strncpy(pr_gotos[num_gotos].name, labelname, sizeof(pr_gotos[num_gotos].name) -1); - pr_gotos[num_gotos].lineno = pr_source_line; - pr_gotos[num_gotos].statementno = patch2 - statements; - - num_gotos++; -} - -pbool QCC_PR_StatementBlocksMatch(QCC_dstatement_t *p1, int p1count, QCC_dstatement_t *p2, int p2count) -{ - if (p1count != p2count) - return false; - - while(p1count>0) - { - if (p1->op != p2->op) - return false; - if (p1->a != p2->a) - return false; - if (p1->b != p2->b) - return false; - if (p1->c != p2->c) - return false; - p1++; - p2++; - p1count--; - } - - return true; -} - -/* -============ -PR_ParseStatement - -============ -*/ -void QCC_PR_ParseStatement (void) -{ - int continues; - int breaks; - int cases; - int i; - QCC_def_t *e, *e2; - QCC_dstatement_t *patch1, *patch2, *patch3; - int statementstart = pr_source_line; - - if (QCC_PR_CheckToken ("{")) - { - e = pr.localvars; - while (!QCC_PR_CheckToken("}")) - QCC_PR_ParseStatement (); - - if (pr_subscopedlocals) - { - for (e2 = pr.localvars; e2 != e; e2 = e2->nextlocal) - { - if (!e2->subscoped_away) - { - Hash_RemoveData(&localstable, e2->name, e2); - e2->subscoped_away = true; - } - } - } - return; - } - - if (QCC_PR_CheckKeyword(keyword_return, "return")) - { - /*if (pr_classtype) - { - e = QCC_PR_GetDef(NULL, "__oself", pr_scope, false, 0); - e2 = QCC_PR_GetDef(NULL, "self", NULL, false, 0); - QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_ENT], e, QCC_PR_DummyDef(pr_classtype, "self", pr_scope, 0, e2->ofs, false), NULL)); - }*/ - - if (QCC_PR_CheckToken (";")) - { - if (pr_scope->type->aux_type->type != ev_void) - QCC_PR_ParseWarning(WARN_MISSINGRETURNVALUE, "\'%s\' should return %s", pr_scope->name, pr_scope->type->aux_type->name); - if (opt_return_only) - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_DONE], 0, 0, NULL)); - else - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_RETURN], 0, 0, NULL)); - return; - } - e = QCC_PR_Expression (TOP_PRIORITY, 0); - e2 = QCC_SupplyConversion(e, pr_scope->type->aux_type->type, true); - if (e != e2) - { - QCC_PR_ParseWarning(WARN_CORRECTEDRETURNTYPE, "\'%s\' returned %s, expected %s, conversion supplied", pr_scope->name, e->type->name, pr_scope->type->aux_type->name); - e = e2; - } - QCC_PR_Expect (";"); - if (pr_scope->type->aux_type->type != e->type->type) - QCC_PR_ParseWarning(WARN_WRONGRETURNTYPE, "\'%s\' returned %s, expected %s", pr_scope->name, e->type->name, pr_scope->type->aux_type->name); - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_RETURN], e, 0, NULL)); - return; - } - if (QCC_PR_CheckKeyword(keyword_exit, "exit")) - { - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_DONE], 0, 0, NULL)); - QCC_PR_Expect (";"); - return; - } - - if (QCC_PR_CheckKeyword(keyword_while, "while")) - { - continues = num_continues; - breaks = num_breaks; - - QCC_PR_Expect ("("); - patch2 = &statements[numstatements]; - conditional = 1; - e = QCC_PR_Expression (TOP_PRIORITY, 0); - conditional = 0; - if (((e->constant && !e->temp) || !STRCMP(e->name, "IMMEDIATE")) && opt_compound_jumps) - { - optres_compound_jumps++; - if (!G_INT(e->ofs)) - { - QCC_PR_ParseWarning(0, "while(0)?"); - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_GOTO], 0, 0, &patch1)); - } - else - { - patch1 = NULL; - } - } - else - { - if (e->constant && !e->temp) - { - if (!G_FLOAT(e->ofs)) - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_GOTO], 0, 0, &patch1)); - else - patch1 = NULL; - } - else if (!typecmp( e->type, type_string) && flag_ifstring) //special case, as strings are now pointers, not offsets from string table - { - QCC_PR_ParseWarning(WARN_IFSTRING_USED, "while(string) can result in bizzare behaviour"); - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IFNOT_S], e, 0, &patch1)); - } - else if (!typecmp( e->type, type_float) && (flag_iffloat||QCC_OPCodeValid(&pr_opcodes[OP_IFNOT_F]))) //special case, as negative 0 is also zero - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IFNOT_F], e, 0, &patch1)); - else - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IFNOT_I], e, 0, &patch1)); - } - QCC_PR_Expect (")"); //after the line number is noted.. - QCC_PR_ParseStatement (); - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_GOTO], NULL, 0, &patch3)); - patch3->a = patch2 - patch3; - if (patch1) - { - if (patch1->op == OP_GOTO) - patch1->a = &statements[numstatements] - patch1; - else - patch1->b = &statements[numstatements] - patch1; - } - - if (breaks != num_breaks) - { - for(i = breaks; i < num_breaks; i++) - { - patch1 = &statements[pr_breaks[i]]; - statements[pr_breaks[i]].a = &statements[numstatements] - patch1; //jump to after the return-to-top goto - } - num_breaks = breaks; - } - if (continues != num_continues) - { - for(i = continues; i < num_continues; i++) - { - patch1 = &statements[pr_continues[i]]; - statements[pr_continues[i]].a = patch2 - patch1; //jump back to top - } - num_continues = continues; - } - return; - } - if (QCC_PR_CheckKeyword(keyword_for, "for")) - { - int old_numstatements; - int numtemp, i; - - int linenum[32]; - QCC_dstatement_t temp[sizeof(linenum)/sizeof(linenum[0])]; - - continues = num_continues; - breaks = num_breaks; - - QCC_PR_Expect("("); - if (!QCC_PR_CheckToken(";")) - { - QCC_FreeTemp(QCC_PR_Expression(TOP_PRIORITY, 0)); - QCC_PR_Expect(";"); - } - - patch2 = &statements[numstatements]; - if (!QCC_PR_CheckToken(";")) - { - conditional = 1; - e = QCC_PR_Expression(TOP_PRIORITY, 0); - conditional = 0; - QCC_PR_Expect(";"); - } - else - e = NULL; - - if (!QCC_PR_CheckToken(")")) - { - old_numstatements = numstatements; - QCC_FreeTemp(QCC_PR_Expression(TOP_PRIORITY, 0)); - - numtemp = numstatements - old_numstatements; - if (numtemp > sizeof(linenum)/sizeof(linenum[0])) - QCC_PR_ParseError(ERR_TOOCOMPLEX, "Update expression too large"); - numstatements = old_numstatements; - for (i = 0 ; i < numtemp ; i++) - { - linenum[i] = statement_linenums[numstatements + i]; - temp[i] = statements[numstatements + i]; - } - - QCC_PR_Expect(")"); - } - else - numtemp = 0; - - if (e) - QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_IFNOT_I], e, 0, &patch1)); - else - patch1 = NULL; - if (!QCC_PR_CheckToken(";")) - QCC_PR_ParseStatement(); //don't give the hanging ';' warning. - patch3 = &statements[numstatements]; - for (i = 0 ; i < numtemp ; i++) - { - statement_linenums[numstatements] = linenum[i]; - statements[numstatements++] = temp[i]; - } - QCC_PR_SimpleStatement(OP_GOTO, patch2 - &statements[numstatements], 0, 0, false); - if (patch1) - patch1->b = &statements[numstatements] - patch1; - - if (breaks != num_breaks) - { - for(i = breaks; i < num_breaks; i++) - { - patch1 = &statements[pr_breaks[i]]; - statements[pr_breaks[i]].a = &statements[numstatements] - patch1; - } - num_breaks = breaks; - } - if (continues != num_continues) - { - for(i = continues; i < num_continues; i++) - { - patch1 = &statements[pr_continues[i]]; - statements[pr_continues[i]].a = patch3 - patch1; - } - num_continues = continues; - } - - return; - } - if (QCC_PR_CheckKeyword(keyword_do, "do")) - { - continues = num_continues; - breaks = num_breaks; - - patch1 = &statements[numstatements]; - QCC_PR_ParseStatement (); - QCC_PR_Expect ("while"); - QCC_PR_Expect ("("); - conditional = 1; - e = QCC_PR_Expression (TOP_PRIORITY, 0); - conditional = 0; - - if (e->constant && !e->temp) - { - if (G_FLOAT(e->ofs)) - { - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_GOTO], NULL, 0, &patch2)); - patch2->a = patch1 - patch2; - } - } - else - { - if (!typecmp( e->type, type_string) && flag_ifstring) - { - QCC_PR_ParseWarning(WARN_IFSTRING_USED, "do {} while(string) can result in bizzare behaviour"); - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IF_S], e, NULL, &patch2)); - } - else if (!typecmp( e->type, type_float) && (flag_iffloat||QCC_OPCodeValid(&pr_opcodes[OP_IFNOT_F]))) - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IF_F], e, NULL, &patch2)); - else - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IF_I], e, NULL, &patch2)); - - patch2->b = patch1 - patch2; - } - - QCC_PR_Expect (")"); - QCC_PR_Expect (";"); - - if (breaks != num_breaks) - { - for(i = breaks; i < num_breaks; i++) - { - patch2 = &statements[pr_breaks[i]]; - statements[pr_breaks[i]].a = &statements[numstatements] - patch2; - } - num_breaks = breaks; - } - if (continues != num_continues) - { - for(i = continues; i < num_continues; i++) - { - patch2 = &statements[pr_continues[i]]; - statements[pr_continues[i]].a = patch1 - patch2; - } - num_continues = continues; - } - - return; - } - - if (QCC_PR_CheckKeyword(keyword_local, "local")) - { - QCC_type_t *functionsclasstype = pr_classtype; -// if (locals_end != numpr_globals) //is this breaking because of locals? -// QCC_PR_ParseWarning("local vars after temp vars\n"); - QCC_PR_ParseDefs (NULL); - pr_classtype = functionsclasstype; - locals_end = numpr_globals; - return; - } - - if (pr_token_type == tt_name) - if ((keyword_var && !STRCMP ("var", pr_token)) || - (keyword_string && !STRCMP ("string", pr_token)) || - (keyword_float && !STRCMP ("float", pr_token)) || - (keyword_entity && !STRCMP ("entity", pr_token)) || - (keyword_vector && !STRCMP ("vector", pr_token)) || - (keyword_integer && !STRCMP ("integer", pr_token)) || - (keyword_int && !STRCMP ("int", pr_token)) || - (keyword_class && !STRCMP ("class", pr_token)) || - (keyword_const && !STRCMP ("const", pr_token))) - { -// if (locals_end != numpr_globals) //is this breaking because of locals? -// QCC_PR_ParseWarning("local vars after temp vars\n"); - QCC_PR_ParseDefs (NULL); - locals_end = numpr_globals; - return; - } - - if (QCC_PR_CheckKeyword(keyword_state, "state")) - { - QCC_PR_Expect("["); - QCC_PR_ParseState(); - QCC_PR_Expect(";"); - return; - } - if (QCC_PR_CheckToken("#")) - { - char *name; - float frame = pr_immediate._float; - QCC_PR_Lex(); - name = QCC_PR_ParseName(); - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_STATE], QCC_MakeFloatConst(frame), QCC_PR_GetDef(type_function, name, NULL, false, 0, false), NULL)); - QCC_PR_Expect(";"); - return; - } - - if (QCC_PR_CheckKeyword(keyword_if, "if")) - { - pbool negate = QCC_PR_CheckKeyword(keyword_not, "not"); - - QCC_PR_Expect ("("); - conditional = 1; - e = QCC_PR_Expression (TOP_PRIORITY, 0); - conditional = 0; - -// negate = negate != 0; - - if (negate) - { - if (!typecmp( e->type, type_string) && flag_ifstring) - { - QCC_PR_ParseWarning(WARN_IFSTRING_USED, "if not(string) can result in bizzare behaviour"); - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IF_S], e, 0, &patch1)); - } - else if (!typecmp( e->type, type_float) && (flag_iffloat||QCC_OPCodeValid(&pr_opcodes[OP_IFNOT_F]))) - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IF_F], e, 0, &patch1)); - else - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IF_I], e, 0, &patch1)); - } - else - { - if (!typecmp( e->type, type_string) && flag_ifstring) - { - QCC_PR_ParseWarning(WARN_IFSTRING_USED, "if (string) can result in bizzare behaviour"); - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IFNOT_S], e, 0, &patch1)); - } - else if (!typecmp( e->type, type_float) && (flag_iffloat||QCC_OPCodeValid(&pr_opcodes[OP_IFNOT_F]))) - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IFNOT_F], e, 0, &patch1)); - else - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IFNOT_I], e, 0, &patch1)); - } - - QCC_PR_Expect (")"); //close bracket is after we save the statement to mem (so debugger does not show the if statement as being on the line after - - QCC_PR_ParseStatement (); - - if (QCC_PR_CheckKeyword (keyword_else, "else")) - { - int lastwasreturn; - lastwasreturn = statements[numstatements-1].op == OP_RETURN || statements[numstatements-1].op == OP_DONE || - statements[numstatements-1].op == OP_GOTO; - - //the last statement of the if was a return, so we don't need the goto at the end - if (lastwasreturn && opt_compound_jumps && !QCC_AStatementJumpsTo(numstatements, patch1-statements, numstatements)) - { -// QCC_PR_ParseWarning(0, "optimised the else"); - optres_compound_jumps++; - patch1->b = &statements[numstatements] - patch1; - QCC_PR_ParseStatement (); - } - else - { -// QCC_PR_ParseWarning(0, "using the else"); - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_GOTO], 0, 0, &patch2)); - patch1->b = &statements[numstatements] - patch1; - QCC_PR_ParseStatement (); - patch2->a = &statements[numstatements] - patch2; - - if (QCC_PR_StatementBlocksMatch(patch1+1, patch2-patch1, patch2+1, &statements[numstatements] - patch2)) - QCC_PR_ParseWarning(0, "Two identical blocks each side of an else"); - } - } - else - patch1->b = &statements[numstatements] - patch1; - - return; - } - if (QCC_PR_CheckKeyword(keyword_switch, "switch")) - { - int op; - int hcstyle; - int defaultcase = -1; - temp_t *et; - int oldst; - QCC_type_t *switchtype; - - breaks = num_breaks; - cases = num_cases; - - - QCC_PR_Expect ("("); - - conditional = 1; - e = QCC_PR_Expression (TOP_PRIORITY, 0); - conditional = 0; - - if (e == &def_ret) - { //copy it out, so our hack just below doesn't crash us -/* if (e->type->type == ev_vector) - e = QCC_PR_Statement(pr_opcodes+OP_STORE_V, e, QCC_GetTemp(type_vector), NULL); - else - e = QCC_PR_Statement(pr_opcodes+OP_STORE_F, e, QCC_GetTemp(type_float), NULL); - - if (e == &def_ret) //this shouldn't be happening - QCC_Error(ERR_INTERNAL, "internal error: switch: e == &def_ret"); -*/ - et = NULL; - } - else - { - et = e->temp; - e->temp = NULL; //so noone frees it until we finish this loop - } - - //expands - - //switch (CONDITION) - //{ - //case 1: - // break; - //case 2: - //default: - // break; - //} - - //to - - // x = CONDITION, goto start - // l1: - // goto end - // l2: - // def: - // goto end - // goto end P1 - // start: - // if (x == 1) goto l1; - // if (x == 2) goto l2; - // goto def - // end: - - //x is emitted in an opcode, stored as a register that we cannot access later. - //it should be possible to nest these. - - switchtype = e->type; - switch(switchtype->type) - { - case ev_float: - op = OP_SWITCH_F; - break; - case ev_entity: //whu??? - op = OP_SWITCH_E; - break; - case ev_vector: - op = OP_SWITCH_V; - break; - case ev_string: - op = OP_SWITCH_S; - break; - case ev_function: - op = OP_SWITCH_FNC; - break; - default: //err hmm. - op = 0; - break; - } - - if (op) - hcstyle = QCC_OPCodeValid(&pr_opcodes[op]); - else - hcstyle = false; - - - if (hcstyle) - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[op], e, 0, &patch1)); - else - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_GOTO], e, 0, &patch1)); - - QCC_PR_Expect (")"); //close bracket is after we save the statement to mem (so debugger does not show the if statement as being on the line after - - oldst = numstatements; - QCC_PR_ParseStatement (); - - //this is so that a missing goto at the end of your switch doesn't end up in the jumptable again - if (oldst == numstatements || !QCC_StatementIsAJump(numstatements-1, numstatements-1)) - { - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_GOTO], 0, 0, &patch2)); //the P1 statement/the theyforgotthebreak statement. -// QCC_PR_ParseWarning(0, "emitted goto"); - } - else - { - patch2 = NULL; -// QCC_PR_ParseWarning(0, "No goto"); - } - - if (hcstyle) - patch1->b = &statements[numstatements] - patch1; //the goto start part - else - patch1->a = &statements[numstatements] - patch1; //the goto start part - - if (e == &def_ret) - e->type = switchtype; //set it back to the type it was actually meant to be. - - for (i = cases; i < num_cases; i++) - { - if (!pr_casesdef[i]) - { - if (defaultcase >= 0) - QCC_PR_ParseError(ERR_MULTIPLEDEFAULTS, "Duplicated default case"); - defaultcase = i; - } - else - { - if (pr_casesdef[i]->type->type != e->type->type) - { - if (e->type->type == ev_integer && pr_casesdef[i]->type->type == ev_float) - pr_casesdef[i] = QCC_MakeIntConst((int)qcc_pr_globals[pr_casesdef[i]->ofs]); - else - QCC_PR_ParseWarning(WARN_SWITCHTYPEMISMATCH, "switch case type mismatch"); - } - if (pr_casesdef2[i]) - { - if (pr_casesdef2[i]->type->type != e->type->type) - { - if (e->type->type == ev_integer && pr_casesdef[i]->type->type == ev_float) - pr_casesdef2[i] = QCC_MakeIntConst((int)qcc_pr_globals[pr_casesdef2[i]->ofs]); - else - QCC_PR_ParseWarning(WARN_SWITCHTYPEMISMATCH, "switch caserange type mismatch"); - } - - if (hcstyle) - { - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_CASERANGE], pr_casesdef[i], pr_casesdef2[i], &patch3)); - patch3->c = &statements[pr_cases[i]] - patch3; - } - else - { - QCC_def_t *e3; - - if (e->type->type == ev_float) - { - e2 = QCC_PR_Statement (&pr_opcodes[OP_GE_F], e, pr_casesdef[i], NULL); - e3 = QCC_PR_Statement (&pr_opcodes[OP_LE_F], e, pr_casesdef2[i], NULL); - e2 = QCC_PR_Statement (&pr_opcodes[OP_AND_F], e2, e3, NULL); - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IF_I], e2, 0, &patch3)); - patch3->b = &statements[pr_cases[i]] - patch3; - } - else if (e->type->type == ev_integer) - { - e2 = QCC_PR_Statement (&pr_opcodes[OP_GE_I], e, pr_casesdef[i], NULL); - e3 = QCC_PR_Statement (&pr_opcodes[OP_LE_I], e, pr_casesdef2[i], NULL); - e2 = QCC_PR_Statement (&pr_opcodes[OP_AND_I], e2, e3, NULL); - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IF_I], e2, 0, &patch3)); - patch3->b = &statements[pr_cases[i]] - patch3; - } - else - QCC_PR_ParseWarning(WARN_SWITCHTYPEMISMATCH, "switch caserange MUST be a float or integer"); - } - } - else - { - if (hcstyle) - { - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_CASE], pr_casesdef[i], 0, &patch3)); - patch3->b = &statements[pr_cases[i]] - patch3; - } - else - { - if (!pr_casesdef[i]->constant || G_INT(pr_casesdef[i]->ofs)) - { - switch(e->type->type) - { - case ev_float: - e2 = QCC_PR_Statement (&pr_opcodes[OP_EQ_F], e, pr_casesdef[i], NULL); - break; - case ev_entity: //whu??? - e2 = QCC_PR_Statement (&pr_opcodes[OP_EQ_E], e, pr_casesdef[i], &patch1); - break; - case ev_vector: - e2 = QCC_PR_Statement (&pr_opcodes[OP_EQ_V], e, pr_casesdef[i], &patch1); - break; - case ev_string: - e2 = QCC_PR_Statement (&pr_opcodes[OP_EQ_S], e, pr_casesdef[i], &patch1); - break; - case ev_function: - e2 = QCC_PR_Statement (&pr_opcodes[OP_EQ_FNC], e, pr_casesdef[i], &patch1); - break; - case ev_field: - e2 = QCC_PR_Statement (&pr_opcodes[OP_EQ_FNC], e, pr_casesdef[i], &patch1); - break; - case ev_integer: - e2 = QCC_PR_Statement (&pr_opcodes[OP_EQ_I], e, pr_casesdef[i], &patch1); - break; - default: - QCC_PR_ParseError(ERR_BADSWITCHTYPE, "Bad switch type"); - e2 = NULL; - break; - } - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IF_I], e2, 0, &patch3)); - } - else - { - if (e->type->type == ev_string) - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IFNOT_S], e, 0, &patch3)); - else if (e->type->type == ev_float) - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IFNOT_F], e, 0, &patch3)); - else - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IFNOT_I], e, 0, &patch3)); - } - patch3->b = &statements[pr_cases[i]] - patch3; - } - } - } - } - if (defaultcase>=0) - { - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_GOTO], 0, 0, &patch3)); - patch3->a = &statements[pr_cases[defaultcase]] - patch3; - } - - num_cases = cases; - - - patch3 = &statements[numstatements]; - if (patch2) - patch2->a = patch3 - patch2; //set P1 jump - - if (breaks != num_breaks) - { - for(i = breaks; i < num_breaks; i++) - { - patch2 = &statements[pr_breaks[i]]; - patch2->a = patch3 - patch2; - } - num_breaks = breaks; - } - - if (et) - { - e->temp = et; - QCC_FreeTemp(e); - } - return; - } - - if (QCC_PR_CheckKeyword(keyword_asm, "asm")) - { - if (QCC_PR_CheckToken("{")) - { - while (!QCC_PR_CheckToken("}")) - QCC_PR_ParseAsm (); - } - else - QCC_PR_ParseAsm (); - return; - } - - if (QCC_PR_CheckToken(":")) - { - if (pr_token_type != tt_name) - { - QCC_PR_ParseError(ERR_BADLABELNAME, "invalid label name \"%s\"", pr_token); - return; - } - - for (i = 0; i < num_labels; i++) - if (!STRNCMP(pr_labels[i].name, pr_token, sizeof(pr_labels[num_labels].name) -1)) - { - QCC_PR_ParseWarning(WARN_DUPLICATELABEL, "Duplicate label %s", pr_token); - QCC_PR_Lex(); - return; - } - - if (num_labels >= max_labels) - { - max_labels += 8; - pr_labels = realloc(pr_labels, sizeof(*pr_labels)*max_labels); - } - - strncpy(pr_labels[num_labels].name, pr_token, sizeof(pr_labels[num_labels].name) -1); - pr_labels[num_labels].lineno = pr_source_line; - pr_labels[num_labels].statementno = numstatements; - - num_labels++; - -// QCC_PR_ParseWarning("Gotos are evil"); - QCC_PR_Lex(); - return; - } - if (QCC_PR_CheckKeyword(keyword_goto, "goto")) - { - if (pr_token_type != tt_name) - { - QCC_PR_ParseError(ERR_NOLABEL, "invalid label name \"%s\"", pr_token); - return; - } - - QCC_PR_Statement (&pr_opcodes[OP_GOTO], 0, 0, &patch2); - - QCC_PR_GotoStatement (patch2, pr_token); - -// QCC_PR_ParseWarning("Gotos are evil"); - QCC_PR_Lex(); - QCC_PR_Expect(";"); - return; - } - - if (QCC_PR_CheckKeyword(keyword_break, "break")) - { - if (!STRCMP ("(", pr_token)) - { //make sure it wasn't a call to the break function. - QCC_PR_IncludeChunk("break(", true, NULL); - QCC_PR_Lex(); //so it sees the break. - } - else - { - if (num_breaks >= max_breaks) - { - max_breaks += 8; - pr_breaks = realloc(pr_breaks, sizeof(*pr_breaks)*max_breaks); - } - pr_breaks[num_breaks] = numstatements; - QCC_PR_Statement (&pr_opcodes[OP_GOTO], 0, 0, NULL); - num_breaks++; - QCC_PR_Expect(";"); - return; - } - } - if (QCC_PR_CheckKeyword(keyword_continue, "continue")) - { - if (num_continues >= max_continues) - { - max_continues += 8; - pr_continues = realloc(pr_continues, sizeof(*pr_continues)*max_continues); - } - pr_continues[num_continues] = numstatements; - QCC_PR_Statement (&pr_opcodes[OP_GOTO], 0, 0, NULL); - num_continues++; - QCC_PR_Expect(";"); - return; - } - if (QCC_PR_CheckKeyword(keyword_case, "case")) - { - if (num_cases >= max_cases) - { - max_cases += 8; - pr_cases = realloc(pr_cases, sizeof(*pr_cases)*max_cases); - pr_casesdef = realloc(pr_casesdef, sizeof(*pr_casesdef)*max_cases); - pr_casesdef2 = realloc(pr_casesdef2, sizeof(*pr_casesdef2)*max_cases); - } - pr_cases[num_cases] = numstatements; - pr_casesdef[num_cases] = QCC_PR_Expression (TOP_PRIORITY, EXPR_DISALLOW_COMMA); - if (QCC_PR_CheckToken("..")) - { - pr_casesdef2[num_cases] = QCC_PR_Expression (TOP_PRIORITY, EXPR_DISALLOW_COMMA); - if (pr_casesdef[num_cases]->constant && pr_casesdef2[num_cases]->constant && - !pr_casesdef[num_cases]->temp && !pr_casesdef2[num_cases]->temp) - if (G_FLOAT(pr_casesdef[num_cases]->ofs) >= G_FLOAT(pr_casesdef2[num_cases]->ofs)) - QCC_PR_ParseError(ERR_CASENOTIMMEDIATE, "Caserange statement uses backwards range\n"); - } - else - pr_casesdef2[num_cases] = NULL; - - if (numstatements != pr_cases[num_cases]) - QCC_PR_ParseError(ERR_CASENOTIMMEDIATE, "Case statements may not use formulas\n"); - num_cases++; - QCC_PR_Expect(":"); - return; - } - if (QCC_PR_CheckKeyword(keyword_default, "default")) - { - if (num_cases >= max_cases) - { - max_cases += 8; - pr_cases = realloc(pr_cases, sizeof(*pr_cases)*max_cases); - pr_casesdef = realloc(pr_casesdef, sizeof(*pr_casesdef)*max_cases); - pr_casesdef2 = realloc(pr_casesdef2, sizeof(*pr_casesdef2)*max_cases); - } - pr_cases[num_cases] = numstatements; - pr_casesdef[num_cases] = NULL; - pr_casesdef2[num_cases] = NULL; - num_cases++; - QCC_PR_Expect(":"); - return; - } - - if (QCC_PR_CheckKeyword(keyword_thinktime, "thinktime")) - { - QCC_def_t *nextthink; - QCC_def_t *time; - e = QCC_PR_Expression (TOP_PRIORITY, 0); - QCC_PR_Expect(":"); - e2 = QCC_PR_Expression (TOP_PRIORITY, 0); - if (e->type->type != ev_entity || e2->type->type != ev_float) - QCC_PR_ParseError(ERR_THINKTIMETYPEMISMATCH, "thinktime type mismatch"); - - if (QCC_OPCodeValid(&pr_opcodes[OP_THINKTIME])) - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_THINKTIME], e, e2, NULL)); - else - { - nextthink = QCC_PR_GetDef(NULL, "nextthink", NULL, false, 0, false); - if (!nextthink) - QCC_PR_ParseError (ERR_UNKNOWNVALUE, "Unknown value \"%s\"", "nextthink"); - time = QCC_PR_GetDef(type_float, "time", NULL, false, 0, false); - if (!time) - QCC_PR_ParseError (ERR_UNKNOWNVALUE, "Unknown value \"%s\"", "time"); - nextthink = QCC_PR_Statement(&pr_opcodes[OP_ADDRESS], e, nextthink, NULL); - time = QCC_PR_Statement(&pr_opcodes[OP_ADD_F], time, e2, NULL); - QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STOREP_F], time, nextthink, NULL)); - } - QCC_PR_Expect(";"); - return; - } - if (QCC_PR_CheckToken(";")) - { - int osl = pr_source_line; - pr_source_line = statementstart; - if (!expandedemptymacro) - QCC_PR_ParseWarning(WARN_POINTLESSSTATEMENT, "Hanging ';'"); - pr_source_line = osl; - return; - } - -// qcc_functioncalled=0; - - qcc_usefulstatement = false; - e = QCC_PR_Expression (TOP_PRIORITY, 0); - expandedemptymacro = false; - QCC_PR_Expect (";"); - - if (e->type->type != ev_void && !qcc_usefulstatement) - { - int osl = pr_source_line; - pr_source_line = statementstart; - QCC_PR_ParseWarning(WARN_POINTLESSSTATEMENT, "Effectless statement"); - pr_source_line = osl; - } - - QCC_FreeTemp(e); - -// qcc_functioncalled=false; -} - - -/* -============== -PR_ParseState - -States are special functions made for convenience. They automatically -set frame, nextthink (implicitly), and think (allowing forward definitions). - -// void() name = [framenum, nextthink] {code} -// expands to: -// function void name () -// { -// self.frame=framenum; -// self.nextthink = time + 0.1; -// self.think = nextthink -// -// }; -============== -*/ -void QCC_PR_ParseState (void) -{ - char *name; - QCC_def_t *s1, *def, *sc = pr_scope; - char f; - - f = *pr_token; - if (QCC_PR_CheckToken("++") || QCC_PR_CheckToken("--")) - { - s1 = QCC_PR_ParseImmediate (); - QCC_PR_Expect(".."); - def = QCC_PR_ParseImmediate (); - QCC_PR_Expect ("]"); - - if (s1->type->type != ev_float || def->type->type != ev_float) - QCC_PR_ParseError(ERR_STATETYPEMISMATCH, "state type mismatch"); - - - if (QCC_OPCodeValid(&pr_opcodes[OP_CSTATE])) - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_CSTATE], s1, def, NULL)); - else - { - QCC_def_t *t1, *t2; - QCC_def_t *framef, *frame; - QCC_def_t *self; - QCC_def_t *cycle_wrapped; - temp_t *ftemp; - - self = QCC_PR_GetDef(type_entity, "self", NULL, false, 0, false); - framef = QCC_PR_GetDef(NULL, "frame", NULL, false, 0, false); - cycle_wrapped = QCC_PR_GetDef(type_float, "cycle_wrapped", NULL, false, 0, false); - - frame = QCC_PR_Statement(&pr_opcodes[OP_LOAD_F], self, framef, NULL); - if (cycle_wrapped) - QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_F], QCC_MakeFloatConst(0), cycle_wrapped, NULL)); - QCC_UnFreeTemp(frame); - - //make sure the frame is within the bounds given. - ftemp = frame->temp; - frame->temp = NULL; - t1 = QCC_PR_Statement(&pr_opcodes[OP_LT_F], frame, s1, NULL); - t2 = QCC_PR_Statement(&pr_opcodes[OP_GT_F], frame, def, NULL); - t1 = QCC_PR_Statement(&pr_opcodes[OP_OR_F], t1, t2, NULL); - QCC_PR_SimpleStatement(OP_IFNOT_I, t1->ofs, 2, 0, false); - QCC_FreeTemp(t1); - QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_F], s1, frame, NULL)); - QCC_PR_SimpleStatement(OP_GOTO, t1->ofs, 13, 0, false); - - t1 = QCC_PR_Statement(&pr_opcodes[OP_GE_F], def, s1, NULL); - QCC_PR_SimpleStatement(OP_IFNOT_I, t1->ofs, 7, 0, false); - QCC_FreeTemp(t1); //this block is the 'it's in a forwards direction' - QCC_PR_SimpleStatement(OP_ADD_F, frame->ofs, QCC_MakeFloatConst(1)->ofs, frame->ofs, false); - t1 = QCC_PR_Statement(&pr_opcodes[OP_GT_F], frame, def, NULL); - QCC_PR_SimpleStatement(OP_IFNOT_I, t1->ofs,2, 0, false); - QCC_FreeTemp(t1); - QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_F], s1, frame, NULL)); - QCC_UnFreeTemp(frame); - if (cycle_wrapped) - QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_F], QCC_MakeFloatConst(1), cycle_wrapped, NULL)); - - QCC_PR_SimpleStatement(OP_GOTO, 6, 0, 0, false); - //reverse animation. - QCC_PR_SimpleStatement(OP_SUB_F, frame->ofs, QCC_MakeFloatConst(1)->ofs, frame->ofs, false); - t1 = QCC_PR_Statement(&pr_opcodes[OP_LT_F], frame, s1, NULL); - QCC_PR_SimpleStatement(OP_IFNOT_I, t1->ofs,2, 0, false); - QCC_FreeTemp(t1); - QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_F], def, frame, NULL)); - QCC_UnFreeTemp(frame); - if (cycle_wrapped) - QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_F], QCC_MakeFloatConst(1), cycle_wrapped, NULL)); - - //self.frame = frame happens with the normal state opcode. - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_STATE], frame, pr_scope, NULL)); - - frame->temp = ftemp; - QCC_FreeTemp(frame); - } - return; - } - - if (pr_token_type != tt_immediate || pr_immediate_type != type_float) - QCC_PR_ParseError (ERR_STATETYPEMISMATCH, "state frame must be a number"); - s1 = QCC_PR_ParseImmediate (); - - QCC_PR_CheckToken (","); - - name = QCC_PR_ParseName (); - pr_scope = NULL; - def = QCC_PR_GetDef (type_function, name, NULL, true, 0, false); - pr_scope = sc; - - QCC_PR_Expect ("]"); - - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_STATE], s1, def, NULL)); -} - -void QCC_PR_ParseAsm(void) -{ - QCC_dstatement_t *patch1; - int op, p; - QCC_def_t *a, *b, *c; - - if (QCC_PR_CheckKeyword(keyword_local, "local")) - { - QCC_PR_ParseDefs (NULL); - locals_end = numpr_globals; - return; - } - - for (op = 0; op < OP_NUMOPS; op++) - { - if (!STRCMP(pr_token, pr_opcodes[op].opname)) - { - QCC_PR_Lex(); - if (pr_opcodes[op].priority==-1 && pr_opcodes[op].associative!=ASSOC_LEFT) - { - if (pr_opcodes[op].type_a==NULL) - { - patch1 = &statements[numstatements]; - - QCC_PR_Statement3(&pr_opcodes[op], NULL, NULL, NULL, true); - - if (pr_token_type == tt_name) - { - QCC_PR_GotoStatement(patch1, QCC_PR_ParseName()); - } - else - { - p = (int)pr_immediate._float; - patch1->a = (int)p; - } - - QCC_PR_Lex(); - } - else if (pr_opcodes[op].type_b==NULL) - { - patch1 = &statements[numstatements]; - - a = QCC_PR_ParseValue(pr_classtype, false); - QCC_PR_Statement3(&pr_opcodes[op], a, NULL, NULL, true); - - if (pr_token_type == tt_name) - { - QCC_PR_GotoStatement(patch1, QCC_PR_ParseName()); - } - else - { - p = (int)pr_immediate._float; - patch1->b = (int)p; - } - - QCC_PR_Lex(); - } - else - { - patch1 = &statements[numstatements]; - - a = QCC_PR_ParseValue(pr_classtype, false); - b = QCC_PR_ParseValue(pr_classtype, false); - QCC_PR_Statement3(&pr_opcodes[op], a, b, NULL, true); - - if (pr_token_type == tt_name) - { - QCC_PR_GotoStatement(patch1, QCC_PR_ParseName()); - } - else - { - p = (int)pr_immediate._float; - patch1->c = (int)p; - } - - QCC_PR_Lex(); - } - } - else - { - if (pr_opcodes[op].type_a != &type_void) - a = QCC_PR_ParseValue(pr_classtype, false); - else - a=NULL; - if (pr_opcodes[op].type_b != &type_void) - b = QCC_PR_ParseValue(pr_classtype, false); - else - b=NULL; - if (pr_opcodes[op].associative==ASSOC_LEFT && pr_opcodes[op].type_c != &type_void) - c = QCC_PR_ParseValue(pr_classtype, false); - else - c=NULL; - - QCC_PR_Statement3(&pr_opcodes[op], a, b, c, true); - } - - QCC_PR_Expect(";"); - return; - } - } - QCC_PR_ParseError(ERR_BADOPCODE, "Bad op code name %s", pr_token); -} - -pbool QCC_FuncJumpsTo(int first, int last, int statement) -{ - int st; - for (st = first; st < last; st++) - { - if (pr_opcodes[statements[st].op].type_a == NULL) - { - if (st + (signed)statements[st].a == statement) - { - if (st != first) - { - if (statements[st-1].op == OP_RETURN) - continue; - if (statements[st-1].op == OP_DONE) - continue; - return true; - } - } - } - if (pr_opcodes[statements[st].op].type_b == NULL) - { - if (st + (signed)statements[st].b == statement) - { - if (st != first) - { - if (statements[st-1].op == OP_RETURN) - continue; - if (statements[st-1].op == OP_DONE) - continue; - return true; - } - } - } - if (pr_opcodes[statements[st].op].type_c == NULL) - { - if (st + (signed)statements[st].c == statement) - { - if (st != first) - { - if (statements[st-1].op == OP_RETURN) - continue; - if (statements[st-1].op == OP_DONE) - continue; - return true; - } - } - } - } - return false; -} - -pbool QCC_FuncJumpsToRange(int first, int last, int firstr, int lastr) -{ - int st; - for (st = first; st < last; st++) - { - if (pr_opcodes[statements[st].op].type_a == NULL) - { - if (st + (signed)statements[st].a >= firstr && st + (signed)statements[st].a <= lastr) - { - if (st != first) - { - if (statements[st-1].op == OP_RETURN) - continue; - if (statements[st-1].op == OP_DONE) - continue; - return true; - } - } - } - if (pr_opcodes[statements[st].op].type_b == NULL) - { - if (st + (signed)statements[st].b >= firstr && st + (signed)statements[st].b <= lastr) - { - if (st != first) - { - if (statements[st-1].op == OP_RETURN) - continue; - if (statements[st-1].op == OP_DONE) - continue; - return true; - } - } - } - if (pr_opcodes[statements[st].op].type_c == NULL) - { - if (st + (signed)statements[st].c >= firstr && st + (signed)statements[st].c <= lastr) - { - if (st != first) - { - if (statements[st-1].op == OP_RETURN) - continue; - if (statements[st-1].op == OP_DONE) - continue; - return true; - } - } - } - } - return false; -} - -#if 0 -void QCC_CompoundJumps(int first, int last) -{ - //jumps to jumps are reordered so they become jumps to the final target. - int statement; - int st; - for (st = first; st < last; st++) - { - if (pr_opcodes[statements[st].op].type_a == NULL) - { - statement = st + (signed)statements[st].a; - if (statements[statement].op == OP_RETURN || statements[statement].op == OP_DONE) - { //goto leads to return. Copy the command out to remove the goto. - statements[st].op = statements[statement].op; - statements[st].a = statements[statement].a; - statements[st].b = statements[statement].b; - statements[st].c = statements[statement].c; - optres_compound_jumps++; - } - while (statements[statement].op == OP_GOTO) - { - statements[st].a = statement+statements[statement].a - st; - statement = st + (signed)statements[st].a; - optres_compound_jumps++; - } - } - if (pr_opcodes[statements[st].op].type_b == NULL) - { - statement = st + (signed)statements[st].b; - while (statements[statement].op == OP_GOTO) - { - statements[st].b = statement+statements[statement].a - st; - statement = st + (signed)statements[st].b; - optres_compound_jumps++; - } - } - if (pr_opcodes[statements[st].op].type_c == NULL) - { - statement = st + (signed)statements[st].c; - while (statements[statement].op == OP_GOTO) - { - statements[st].c = statement+statements[statement].a - st; - statement = st + (signed)statements[st].c; - optres_compound_jumps++; - } - } - } -} -#else -void QCC_CompoundJumps(int first, int last) -{ - //jumps to jumps are reordered so they become jumps to the final target. - int statement; - int st; - int infloop; - for (st = first; st < last; st++) - { - if (pr_opcodes[statements[st].op].type_a == NULL) - { - statement = st + (signed)statements[st].a; - if (statements[statement].op == OP_RETURN || statements[statement].op == OP_DONE) - { //goto leads to return. Copy the command out to remove the goto. - statements[st].op = statements[statement].op; - statements[st].a = statements[statement].a; - statements[st].b = statements[statement].b; - statements[st].c = statements[statement].c; - optres_compound_jumps++; - } - infloop = 1000; - while (statements[statement].op == OP_GOTO) - { - if (!infloop--) - { - QCC_PR_ParseWarning(0, "Infinate loop detected"); - break; - } - statements[st].a = (statement+statements[statement].a - st); - statement = st + (signed)statements[st].a; - optres_compound_jumps++; - } - } - if (pr_opcodes[statements[st].op].type_b == NULL) - { - statement = st + (signed)statements[st].b; - infloop = 1000; - while (statements[statement].op == OP_GOTO) - { - if (!infloop--) - { - QCC_PR_ParseWarning(0, "Infinate loop detected"); - break; - } - statements[st].b = (statement+statements[statement].a - st); - statement = st + (signed)statements[st].b; - optres_compound_jumps++; - } - } - if (pr_opcodes[statements[st].op].type_c == NULL) - { - statement = st + (signed)statements[st].c; - infloop = 1000; - while (statements[statement].op == OP_GOTO) - { - if (!infloop--) - { - QCC_PR_ParseWarning(0, "Infinate loop detected"); - break; - } - statements[st].c = (statement+statements[statement].a - st); - statement = st + (signed)statements[st].c; - optres_compound_jumps++; - } - } - } -} -#endif - -void QCC_CheckForDeadAndMissingReturns(int first, int last, int rettype) -{ - int st, st2; - - if (statements[last-1].op == OP_DONE) - last--; //don't want the done - - if (rettype != ev_void) - if (statements[last-1].op != OP_RETURN) - { - if (statements[last-1].op != OP_GOTO || (signed)statements[last-1].a > 0) - { - QCC_PR_ParseWarning(WARN_MISSINGRETURN, "%s: not all control paths return a value", pr_scope->name ); - return; - } - } - - for (st = first; st < last; st++) - { - if (statements[st].op == OP_RETURN || statements[st].op == OP_GOTO) - { - st++; - if (st == last) - continue; //erm... end of function doesn't count as unreachable. - - if (!opt_compound_jumps) - { //we can ignore single statements like these without compound jumps (compound jumps correctly removes all). - if (statements[st].op == OP_GOTO) //inefficient compiler, we can ignore this. - continue; - if (statements[st].op == OP_DONE) //inefficient compiler, we can ignore this. - continue; - if (statements[st].op == OP_RETURN) //inefficient compiler, we can ignore this. - continue; - } - - //make sure something goes to just after this return. - for (st2 = first; st2 < last; st2++) - { - if (pr_opcodes[statements[st2].op].type_a == NULL) - { - if (st2 + (signed)statements[st2].a == st) - break; - } - if (pr_opcodes[statements[st2].op].type_b == NULL) - { - if (st2 + (signed)statements[st2].b == st) - break; - } - if (pr_opcodes[statements[st2].op].type_c == NULL) - { - if (st2 + (signed)statements[st2].c == st) - break; - } - } - if (st2 == last) - { - QCC_PR_ParseWarning(WARN_UNREACHABLECODE, "%s: contains unreachable code", pr_scope->name ); - } - continue; - } - if (rettype != ev_void) - { - if (pr_opcodes[statements[st].op].type_a == NULL) - { - if (st + (signed)statements[st].a == last) - { - QCC_PR_ParseWarning(WARN_MISSINGRETURN, "%s: not all control paths return a value", pr_scope->name ); - return; - } - } - if (pr_opcodes[statements[st].op].type_b == NULL) - { - if (st + (signed)statements[st].b == last) - { - QCC_PR_ParseWarning(WARN_MISSINGRETURN, "%s: not all control paths return a value", pr_scope->name ); - return; - } - } - if (pr_opcodes[statements[st].op].type_c == NULL) - { - if (st + (signed)statements[st].c == last) - { - QCC_PR_ParseWarning(WARN_MISSINGRETURN, "%s: not all control paths return a value", pr_scope->name ); - return; - } - } - } - } -} - -pbool QCC_StatementIsAJump(int stnum, int notifdest) //only the unconditionals. -{ - if (statements[stnum].op == OP_RETURN) - return true; - if (statements[stnum].op == OP_DONE) - return true; - if (statements[stnum].op == OP_GOTO) - if ((int)statements[stnum].a != notifdest) - return true; - return false; -} - -int QCC_AStatementJumpsTo(int targ, int first, int last) -{ - int st; - for (st = first; st < last; st++) - { - if (pr_opcodes[statements[st].op].type_a == NULL) - { - if (st + (signed)statements[st].a == targ && statements[st].a) - { - return true; - } - } - if (pr_opcodes[statements[st].op].type_b == NULL) - { - if (st + (signed)statements[st].b == targ) - { - return true; - } - } - if (pr_opcodes[statements[st].op].type_c == NULL) - { - if (st + (signed)statements[st].c == targ) - { - return true; - } - } - } - - for (st = 0; st < num_labels; st++) //assume it's used. - { - if (pr_labels[st].statementno == targ) - return true; - } - - - return false; -} -/* -//goes through statements, if it sees a matching statement earlier, it'll strim out the current. -void QCC_CommonSubExpressionRemoval(int first, int last) -{ - int cur; //the current - int prev; //the earlier statement - for (cur = last-1; cur >= first; cur--) - { - if (pr_opcodes[statements[cur].op].priority == -1) - continue; - for (prev = cur-1; prev >= first; prev--) - { - if (statements[prev].op >= OP_CALL0 && statements[prev].op <= OP_CALL8) - { - optres_test1++; - break; - } - if (statements[prev].op >= OP_CALL1H && statements[prev].op <= OP_CALL8H) - { - optres_test1++; - break; - } - if (pr_opcodes[statements[prev].op].right_associative) - { //make sure no changes to var_a occur. - if (statements[prev].b == statements[cur].a) - { - optres_test2++; - break; - } - if (statements[prev].b == statements[cur].b && !pr_opcodes[statements[cur].op].right_associative) - { - optres_test2++; - break; - } - } - else - { - if (statements[prev].c == statements[cur].a) - { - optres_test2++; - break; - } - if (statements[prev].c == statements[cur].b && !pr_opcodes[statements[cur].op].right_associative) - { - optres_test2++; - break; - } - } - - if (statements[prev].op == statements[cur].op) - if (statements[prev].a == statements[cur].a) - if (statements[prev].b == statements[cur].b) - if (statements[prev].c == statements[cur].c) - { - if (!QCC_FuncJumpsToRange(first, last, prev, cur)) - { - statements[cur].op = OP_STORE_F; - statements[cur].a = 28; - statements[cur].b = 28; - optres_comexprremoval++; - } - else - optres_test1++; - break; - } - } - } -} -*/ - -void QCC_RemapOffsets(unsigned int firststatement, unsigned int laststatement, unsigned int min, unsigned int max, unsigned int newmin) -{ - QCC_dstatement_t *st; - unsigned int i; - - for (i = firststatement, st = &statements[i]; i < laststatement; i++, st++) - { - if (pr_opcodes[st->op].type_a && st->a >= min && st->a < max) - st->a = st->a - min + newmin; - if (pr_opcodes[st->op].type_b && st->b >= min && st->b < max) - st->b = st->b - min + newmin; - if (pr_opcodes[st->op].type_c && st->c >= min && st->c < max) - st->c = st->c - min + newmin; - } -} - -void QCC_Marshal_Locals(int first, int laststatement) -{ - QCC_def_t *local; - unsigned int newofs; - int size; - -// if (!opt_overlaptemps) //clear these after each function. we arn't overlapping them so why do we need to keep track of them? -// { -// temp_t *t; -// for (t = functemps; t; t = t->next) -// QCC_FreeOffset(t->ofs, t->size); -// functemps = NULL; -// } - - if (!pr.localvars) //nothing to marshal - { - locals_start = numpr_globals; - locals_end = numpr_globals; - return; - } - - if (!opt_locals_marshalling) - { - pr.localvars = NULL; - return; - } - - //initial backwards bounds. - locals_start = MAX_REGS; - locals_end = 0; - - newofs = MAX_REGS; //this is a handy place to put it. :) - - //the params need to be in the order that they were allocated - //so we allocate in a backwards order. - for (local = pr.localvars; local; local = local->nextlocal) - { - if (local->constant) - continue; - - size = local->type->size*(local->arraysize?local->arraysize:1); - if (local->arraysize) - size++; - - newofs += size; - } - - locals_start = MAX_REGS; - locals_end = newofs; - - - optres_locals_marshalling+=newofs-MAX_REGS; - - for (local = pr.localvars; local; local = local->nextlocal) - { - if (local->constant) - continue; - - if (((int*)qcc_pr_globals)[local->ofs]) - QCC_PR_ParseError(ERR_INTERNAL, "Marshall of a set value"); - - size = local->type->size*(local->arraysize?local->arraysize:1); - if (local->arraysize) - size++; - - newofs -= size; - - QCC_RemapOffsets(first, laststatement, local->ofs, local->ofs+size, newofs); - QCC_FreeOffset(local->ofs, size); - - local->ofs = newofs; - } - - - pr.localvars = NULL; -} - -#ifdef WRITEASM -void QCC_WriteAsmFunction(QCC_def_t *sc, unsigned int firststatement, gofs_t firstparm) -{ - unsigned int i; - unsigned int p; - gofs_t o; - QCC_type_t *type; - QCC_def_t *param; - - if (!asmfile) - return; - - type = sc->type; - fprintf(asmfile, "%s(", TypeName(type->aux_type)); - p = type->num_parms; - for (o = firstparm, i = 0, type = type->param; i < p; i++, type = type->next) - { - if (i) - fprintf(asmfile, ", "); - - for (param = pr.localvars; param; param = param->nextlocal) - { - if (param->ofs == o) - break; - } - if (param) - fprintf(asmfile, "%s %s", TypeName(type), param->name); - else - fprintf(asmfile, "%s", TypeName(type)); - - o += type->size; - } - - fprintf(asmfile, ") %s = asm\n{\n", sc->name); - - QCC_fprintfLocals(asmfile, firstparm, o); - - for (i = firststatement; i < (unsigned int)numstatements; i++) - { - fprintf(asmfile, "\t%s", pr_opcodes[statements[i].op].opname); - if (pr_opcodes[statements[i].op].type_a != &type_void) - { - if (strlen(pr_opcodes[statements[i].op].opname)<6) - fprintf(asmfile, "\t"); - if (pr_opcodes[statements[i].op].type_a) - fprintf(asmfile, "\t%s", QCC_VarAtOffset(statements[i].a, (*pr_opcodes[statements[i].op].type_a)->size)); - else - fprintf(asmfile, "\t%i", statements[i].a); - if (pr_opcodes[statements[i].op].type_b != &type_void) - { - if (pr_opcodes[statements[i].op].type_b) - fprintf(asmfile, ",\t%s", QCC_VarAtOffset(statements[i].b, (*pr_opcodes[statements[i].op].type_b)->size)); - else - fprintf(asmfile, ",\t%i", statements[i].b); - if (pr_opcodes[statements[i].op].type_c != &type_void && pr_opcodes[statements[i].op].associative==ASSOC_LEFT) - { - if (pr_opcodes[statements[i].op].type_c) - fprintf(asmfile, ",\t%s", QCC_VarAtOffset(statements[i].c, (*pr_opcodes[statements[i].op].type_c)->size)); - else - fprintf(asmfile, ",\t%i", statements[i].c); - } - } - else - { - if (pr_opcodes[statements[i].op].type_c != &type_void) - { - if (pr_opcodes[statements[i].op].type_c) - fprintf(asmfile, ",\t%s", QCC_VarAtOffset(statements[i].c, (*pr_opcodes[statements[i].op].type_c)->size)); - else - fprintf(asmfile, ",\t%i", statements[i].c); - } - } - } - fprintf(asmfile, "; /*%i*/\n", statement_linenums[i]); - } - - fprintf(asmfile, "}\n\n"); -} -#endif - -/* -============ -PR_ParseImmediateStatements - -Parse a function body -============ -*/ -QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_type_t *type) -{ - int i; - QCC_function_t *f; - QCC_def_t *defs[MAX_PARMS+MAX_EXTRA_PARMS], *e2; - - QCC_type_t *parm; - pbool needsdone=false; - freeoffset_t *oldfofs; - - conditional = 0; - - expandedemptymacro = false; - - - f = (void *)qccHunkAlloc (sizeof(QCC_function_t)); - -// -// check for builtin function definition #1, #2, etc -// -// hexenC has void name() : 2; - if (QCC_PR_CheckToken ("#") || QCC_PR_CheckToken (":")) - { - int binum = 0; - if (pr_token_type == tt_immediate - && pr_immediate_type == type_float - && pr_immediate._float == (int)pr_immediate._float) - binum = (int)pr_immediate._float; - else if (pr_token_type == tt_immediate && pr_immediate_type == type_integer) - binum = pr_immediate._int; - else - QCC_PR_ParseError (ERR_BADBUILTINIMMEDIATE, "Bad builtin immediate"); - f->builtin = binum; - QCC_PR_Lex (); - - locals_start = locals_end = OFS_PARM0; //hmm... - return f; - } - if (QCC_PR_CheckKeyword(keyword_external, "external")) - { //reacc style builtin - if (pr_token_type != tt_immediate - || pr_immediate_type != type_float - || pr_immediate._float != (int)pr_immediate._float) - QCC_PR_ParseError (ERR_BADBUILTINIMMEDIATE, "Bad builtin immediate"); - f->builtin = (int)-pr_immediate._float; - QCC_PR_Lex (); - QCC_PR_Expect(";"); - - locals_start = locals_end = OFS_PARM0; //hmm... - return f; - } - - if (type->num_parms < 0) - QCC_PR_ParseError (ERR_FUNCTIONWITHVARGS, "QC function with variable arguments and function body"); - - f->builtin = 0; -// -// define the parms -// - - locals_start = locals_end = numpr_globals; - - oldfofs = freeofs; - freeofs = NULL; - - parm = type->param; - for (i=0 ; inum_parms ; i++) - { - if (!*pr_parm_names[i]) - QCC_PR_ParseError(ERR_PARAMWITHNONAME, "Parameter is not named"); - defs[i] = QCC_PR_GetDef (parm, pr_parm_names[i], pr_scope, true, 0, false); - - defs[i]->references++; - if (i < MAX_PARMS) - { - f->parm_ofs[i] = defs[i]->ofs; - if (i > 0 && f->parm_ofs[i] < f->parm_ofs[i-1]) - QCC_Error (ERR_BADPARAMORDER, "bad parm order"); - if (i > 0 && f->parm_ofs[i] != f->parm_ofs[i-1]+defs[i-1]->type->size) - QCC_Error (ERR_BADPARAMORDER, "parms not packed"); - } - parm = parm->next; - } - - if (type->num_parms) - locals_start = locals_end = defs[0]->ofs; - - freeofs = oldfofs; - - f->code = numstatements; - - if (type->num_parms > MAX_PARMS) - { - for (i = MAX_PARMS; i < type->num_parms; i++) - { - if (!extra_parms[i - MAX_PARMS]) - { - e2 = (QCC_def_t *) qccHunkAlloc (sizeof(QCC_def_t)); - e2->name = "extra parm"; - e2->ofs = QCC_GetFreeOffsetSpace(3); - extra_parms[i - MAX_PARMS] = e2; - } - extra_parms[i - MAX_PARMS]->type = defs[i]->type; - if (defs[i]->type->type != ev_vector) - QCC_PR_Statement (&pr_opcodes[OP_STORE_F], extra_parms[i - MAX_PARMS], defs[i], NULL); - else - QCC_PR_Statement (&pr_opcodes[OP_STORE_V], extra_parms[i - MAX_PARMS], defs[i], NULL); - } - } - - QCC_RemapLockedTemps(-1, -1); - - /*if (pr_classtype) - { - QCC_def_t *e, *e2; - e = QCC_PR_GetDef(pr_classtype, "__oself", pr_scope, true, 0); - e2 = QCC_PR_GetDef(type_entity, "self", NULL, true, 0); - QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_ENT], QCC_PR_DummyDef(pr_classtype, "self", pr_scope, 0, e2->ofs, false), e, NULL)); - }*/ - -// -// check for a state opcode -// - if (QCC_PR_CheckToken ("[")) - QCC_PR_ParseState (); - - if (QCC_PR_CheckKeyword (keyword_asm, "asm")) - { - QCC_PR_Expect ("{"); - while (!QCC_PR_CheckToken("}")) - QCC_PR_ParseAsm (); - } - else - { - if (QCC_PR_CheckKeyword (keyword_var, "var")) //reacc support - { //parse lots of locals - char *name; - do { - name = QCC_PR_ParseName(); - QCC_PR_Expect(":"); - e2 = QCC_PR_GetDef(QCC_PR_ParseType(false, false), name, pr_scope, true, 0, false); - QCC_PR_Expect(";"); - } while(!QCC_PR_CheckToken("{")); - } - else - QCC_PR_Expect ("{"); -// -// parse regular statements -// - while (!QCC_PR_CheckToken("}")) - { - QCC_PR_ParseStatement (); - QCC_FreeTemps(); - } - } - QCC_FreeTemps(); - - // this is cheap -// if (type->aux_type->type) -// if (statements[numstatements - 1].op != OP_RETURN) -// QCC_PR_ParseWarning(WARN_MISSINGRETURN, "%s: not all control paths return a value", pr_scope->name ); - - if (f->code == numstatements) - needsdone = true; - else if (statements[numstatements - 1].op != OP_RETURN && statements[numstatements - 1].op != OP_DONE) - needsdone = true; - - if (num_gotos) - { - int j; - for (i = 0; i < num_gotos; i++) - { - for (j = 0; j < num_labels; j++) - { - if (!strcmp(pr_gotos[i].name, pr_labels[j].name)) - { - if (!pr_opcodes[statements[pr_gotos[i].statementno].op].type_a) - statements[pr_gotos[i].statementno].a += pr_labels[j].statementno - pr_gotos[i].statementno; - else if (!pr_opcodes[statements[pr_gotos[i].statementno].op].type_b) - statements[pr_gotos[i].statementno].b += pr_labels[j].statementno - pr_gotos[i].statementno; - else - statements[pr_gotos[i].statementno].c += pr_labels[j].statementno - pr_gotos[i].statementno; - break; - } - } - if (j == num_labels) - { - num_gotos = 0; - QCC_PR_ParseError(ERR_NOLABEL, "Goto statement with no matching label \"%s\"", pr_gotos[i].name); - } - } - num_gotos = 0; - } - - if (opt_return_only && !needsdone) - needsdone = QCC_FuncJumpsTo(f->code, numstatements, numstatements); - - // emit an end of statements opcode - if (!opt_return_only || needsdone) - { - /*if (pr_classtype) - { - QCC_def_t *e, *e2; - e = QCC_PR_GetDef(NULL, "__oself", pr_scope, false, 0); - e2 = QCC_PR_GetDef(NULL, "self", NULL, false, 0); - QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_ENT], e, QCC_PR_DummyDef(pr_classtype, "self", pr_scope, 0, e2->ofs, false), NULL)); - }*/ - - QCC_PR_Statement (pr_opcodes, 0,0, NULL); - } - else - optres_return_only++; - - QCC_CheckForDeadAndMissingReturns(f->code, numstatements, type->aux_type->type); - - if (opt_compound_jumps) - QCC_CompoundJumps(f->code, numstatements); -// if (opt_comexprremoval) -// QCC_CommonSubExpressionRemoval(f->code, numstatements); - - - QCC_RemapLockedTemps(f->code, numstatements); - locals_end = numpr_globals; - - QCC_WriteAsmFunction(pr_scope, f->code, locals_start); - - QCC_Marshal_Locals(f->code, numstatements); - - if (num_labels) - num_labels = 0; - - - if (num_continues) - { - num_continues=0; - QCC_PR_ParseError(ERR_ILLEGALCONTINUES, "%s: function contains illegal continues", pr_scope->name); - } - if (num_breaks) - { - num_breaks=0; - QCC_PR_ParseError(ERR_ILLEGALBREAKS, "%s: function contains illegal breaks", pr_scope->name); - } - if (num_cases) - { - num_cases = 0; - QCC_PR_ParseError(ERR_ILLEGALCASES, "%s: function contains illegal cases", pr_scope->name); - } - - return f; -} - -void QCC_PR_ArrayRecurseDivideRegular(QCC_def_t *array, QCC_def_t *index, int min, int max) -{ - QCC_dstatement_t *st; - QCC_def_t *eq; - if (min == max || min+1 == max) - { - eq = QCC_PR_Statement(pr_opcodes+OP_LT_F, index, QCC_MakeFloatConst(min+0.5f), NULL); - QCC_UnFreeTemp(index); - QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_IFNOT_I, eq, 0, &st)); - st->b = 2; - QCC_PR_Statement(pr_opcodes+OP_RETURN, 0, 0, &st); - st->a = array->ofs + min*array->type->size; - } - else - { - int mid = min + (max-min)/2; - - if (max-min>4) - { - eq = QCC_PR_Statement(pr_opcodes+OP_LT_F, index, QCC_MakeFloatConst(mid+0.5f), NULL); - QCC_UnFreeTemp(index); - QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_IFNOT_I, eq, 0, &st)); - } - else - st = NULL; - QCC_PR_ArrayRecurseDivideRegular(array, index, min, mid); - if (st) - st->b = numstatements - (st-statements); - QCC_PR_ArrayRecurseDivideRegular(array, index, mid, max); - } -} - -//the idea here is that we return a vector, the caller then figures out the extra 3rd. -//This is useful when we have a load of indexes. -void QCC_PR_ArrayRecurseDivideUsingVectors(QCC_def_t *array, QCC_def_t *index, int min, int max) -{ - QCC_dstatement_t *st; - QCC_def_t *eq; - if (min == max || min+1 == max) - { - eq = QCC_PR_Statement(pr_opcodes+OP_LT_F, index, QCC_MakeFloatConst(min+0.5f), NULL); - QCC_UnFreeTemp(index); - QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_IFNOT_I, eq, 0, &st)); - st->b = 2; - QCC_PR_Statement(pr_opcodes+OP_RETURN, 0, 0, &st); - st->a = array->ofs + min*3; - } - else - { - int mid = min + (max-min)/2; - - if (max-min>4) - { - eq = QCC_PR_Statement(pr_opcodes+OP_LT_F, index, QCC_MakeFloatConst(mid+0.5f), NULL); - QCC_UnFreeTemp(index); - QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_IFNOT_I, eq, 0, &st)); - } - else - st = NULL; - QCC_PR_ArrayRecurseDivideUsingVectors(array, index, min, mid); - if (st) - st->b = numstatements - (st-statements); - QCC_PR_ArrayRecurseDivideUsingVectors(array, index, mid, max); - } -} - -//returns a vector overlapping the result needed. -QCC_def_t *QCC_PR_EmitArrayGetVector(QCC_def_t *array) -{ - QCC_dfunction_t *df; - QCC_def_t *temp, *index, *func; - - func = QCC_PR_GetDef(type_function, qcva("ArrayGetVec*%s", array->name), NULL, true, 0, false); - - pr_scope = func; - - if (numfunctions >= MAX_FUNCTIONS) - QCC_Error(ERR_INTERNAL, "Too many function defs"); - - df = &functions[numfunctions]; - numfunctions++; - - df->s_file = 0; - df->s_name = QCC_CopyString(func->name); - df->first_statement = numstatements; - df->parm_size[0] = 1; - df->numparms = 1; - df->parm_start = numpr_globals; - index = QCC_PR_GetDef(type_float, "index___", func, true, 0, false); - index->references++; - temp = QCC_PR_GetDef(type_float, "div3___", func, true, 0, false); - locals_end = numpr_globals; - df->locals = locals_end - df->parm_start; - QCC_PR_Statement3(pr_opcodes+OP_DIV_F, index, QCC_MakeFloatConst(3), temp, false); - QCC_PR_Statement3(pr_opcodes+OP_BITAND_F, temp, temp, temp, false);//round down to int - - QCC_PR_ArrayRecurseDivideUsingVectors(array, temp, 0, (array->arraysize+2)/3); //round up - - QCC_PR_Statement(pr_opcodes+OP_RETURN, QCC_MakeFloatConst(0), 0, NULL); //err... we didn't find it, give up. - QCC_PR_Statement(pr_opcodes+OP_DONE, 0, 0, NULL); //err... we didn't find it, give up. - - G_FUNCTION(func->ofs) = df - functions; - func->initialized = 1; - return func; -} - -void QCC_PR_EmitArrayGetFunction(QCC_def_t *scope, char *arrayname) -{ - QCC_def_t *vectortrick; - QCC_dfunction_t *df; - QCC_def_t *def, *index; - - QCC_dstatement_t *st; - QCC_def_t *eq; - - QCC_def_t *fasttrackpossible; - - if (flag_fasttrackarrays) - fasttrackpossible = QCC_PR_GetDef(type_float, "__ext__fasttrackarrays", NULL, true, 0, false); - else - fasttrackpossible = NULL; - - def = QCC_PR_GetDef(NULL, arrayname, NULL, false, 0, false); - - if (def->arraysize >= 15 && def->type->size == 1) - { - vectortrick = QCC_PR_EmitArrayGetVector(def); - } - else - vectortrick = NULL; - - pr_scope = scope; - - if (numfunctions >= MAX_FUNCTIONS) - QCC_Error(ERR_INTERNAL, "Too many function defs"); - - df = &functions[numfunctions]; - numfunctions++; - - df->s_file = 0; - df->s_name = QCC_CopyString(scope->name); - df->first_statement = numstatements; - df->parm_size[0] = 1; - df->numparms = 1; - df->parm_start = numpr_globals; - index = QCC_PR_GetDef(type_float, "indexg___", def, true, 0, false); - - G_FUNCTION(scope->ofs) = df - functions; - - if (fasttrackpossible) - { - QCC_PR_Statement(pr_opcodes+OP_IFNOT_I, fasttrackpossible, NULL, &st); - //fetch_gbl takes: (float size, variant array[]), float index, variant pos - //note that the array size is coded into the globals, one index before the array. - - if (def->type->size >= 3) - QCC_PR_Statement3(&pr_opcodes[OP_FETCH_GBL_V], def, index, &def_ret, true); - else - QCC_PR_Statement3(&pr_opcodes[OP_FETCH_GBL_F], def, index, &def_ret, true); - - QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_RETURN], &def_ret, NULL, NULL)); - - //finish the jump - st->b = &statements[numstatements] - st; - } - - if (vectortrick) - { - QCC_def_t *div3, *intdiv3, *ret; - - //okay, we've got a function to retrieve the var as part of a vector. - //we need to work out which part, x/y/z that it's stored in. - //0,1,2 = i - ((int)i/3 *) 3; - - div3 = QCC_PR_GetDef(type_float, "div3___", def, true, 0, false); - intdiv3 = QCC_PR_GetDef(type_float, "intdiv3___", def, true, 0, false); - - eq = QCC_PR_Statement(pr_opcodes+OP_GE_F, index, QCC_MakeFloatConst((float)def->arraysize), NULL); //escape clause - should call some sort of error function instead.. that'd rule! - QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_IFNOT_I, eq, 0, &st)); - st->b = 2; - QCC_PR_Statement(pr_opcodes+OP_RETURN, QCC_MakeFloatConst(0), 0, &st); - - div3->references++; - QCC_PR_Statement3(pr_opcodes+OP_BITAND_F, index, index, index, false); - QCC_PR_Statement3(pr_opcodes+OP_DIV_F, index, QCC_MakeFloatConst(3), div3, false); - QCC_PR_Statement3(pr_opcodes+OP_BITAND_F, div3, div3, intdiv3, false); - - QCC_PR_Statement3(pr_opcodes+OP_STORE_F, index, &def_parms[0], NULL, false); - QCC_PR_Statement3(pr_opcodes+OP_CALL1, vectortrick, NULL, NULL, false); - vectortrick->references++; - ret = QCC_PR_GetDef(type_vector, "vec__", pr_scope, true, 0, false); - ret->references+=4; - QCC_PR_Statement3(pr_opcodes+OP_STORE_V, &def_ret, ret, NULL, false); - QCC_FreeTemp(&def_ret); - - div3 = QCC_PR_Statement(pr_opcodes+OP_MUL_F, intdiv3, QCC_MakeFloatConst(3), NULL); - QCC_PR_Statement3(pr_opcodes+OP_SUB_F, index, div3, index, false); - QCC_FreeTemp(div3); - - eq = QCC_PR_Statement(pr_opcodes+OP_LT_F, index, QCC_MakeFloatConst(0+0.5f), NULL); - QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_IFNOT_I, eq, 0, &st)); - st->b = 2; - QCC_PR_Statement(pr_opcodes+OP_RETURN, 0, 0, &st); - st->a = ret->ofs + 0; - - eq = QCC_PR_Statement(pr_opcodes+OP_LT_F, index, QCC_MakeFloatConst(1+0.5f), NULL); - QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_IFNOT_I, eq, 0, &st)); - st->b = 2; - QCC_PR_Statement(pr_opcodes+OP_RETURN, 0, 0, &st); - st->a = ret->ofs + 1; - - eq = QCC_PR_Statement(pr_opcodes+OP_LT_F, index, QCC_MakeFloatConst(2+0.5), NULL); - QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_IFNOT_I, eq, 0, &st)); - st->b = 2; - QCC_PR_Statement(pr_opcodes+OP_RETURN, 0, 0, &st); - st->a = ret->ofs + 2; - QCC_FreeTemp(ret); - QCC_FreeTemp(index); - } - else - { - QCC_PR_Statement3(pr_opcodes+OP_BITAND_F, index, index, index, false); - QCC_PR_ArrayRecurseDivideRegular(def, index, 0, def->arraysize); - } - - QCC_PR_Statement(pr_opcodes+OP_RETURN, QCC_MakeFloatConst(0), 0, NULL); - - QCC_PR_Statement(pr_opcodes+OP_DONE, 0, 0, NULL); - - locals_end = numpr_globals; - df->locals = locals_end - df->parm_start; - - - QCC_WriteAsmFunction(pr_scope, df->first_statement, df->parm_start); - - QCC_FreeTemps(); -} - -void QCC_PR_ArraySetRecurseDivide(QCC_def_t *array, QCC_def_t *index, QCC_def_t *value, int min, int max) -{ - QCC_dstatement_t *st; - QCC_def_t *eq; - if (min == max || min+1 == max) - { - eq = QCC_PR_Statement(pr_opcodes+OP_EQ_F, index, QCC_MakeFloatConst((float)min), NULL); - QCC_UnFreeTemp(index); - QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_IFNOT_I, eq, 0, &st)); - st->b = 3; - if (array->type->size == 3) - QCC_PR_Statement(pr_opcodes+OP_STORE_V, value, array, &st); - else - QCC_PR_Statement(pr_opcodes+OP_STORE_F, value, array, &st); - st->b = array->ofs + min*array->type->size; - QCC_PR_Statement(pr_opcodes+OP_RETURN, 0, 0, &st); - } - else - { - int mid = min + (max-min)/2; - - if (max-min>4) - { - eq = QCC_PR_Statement(pr_opcodes+OP_LT_F, index, QCC_MakeFloatConst((float)mid), NULL); - QCC_UnFreeTemp(index); - QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_IFNOT_I, eq, 0, &st)); - } - else - st = NULL; - QCC_PR_ArraySetRecurseDivide(array, index, value, min, mid); - if (st) - st->b = numstatements - (st-statements); - QCC_PR_ArraySetRecurseDivide(array, index, value, mid, max); - } -} - -void QCC_PR_EmitArraySetFunction(QCC_def_t *scope, char *arrayname) -{ - QCC_dfunction_t *df; - QCC_def_t *def, *index, *value; - - QCC_def_t *fasttrackpossible; - - if (flag_fasttrackarrays) - fasttrackpossible = QCC_PR_GetDef(type_float, "__ext__fasttrackarrays", NULL, true, 0, false); - else - fasttrackpossible = NULL; - - def = QCC_PR_GetDef(NULL, arrayname, NULL, false, 0, false); - pr_scope = scope; - - if (numfunctions >= MAX_FUNCTIONS) - QCC_Error(ERR_INTERNAL, "Too many function defs"); - - df = &functions[numfunctions]; - numfunctions++; - - df->s_file = 0; - df->s_name = QCC_CopyString(scope->name); - df->first_statement = numstatements; - df->parm_size[0] = 1; - df->parm_size[1] = def->type->size; - df->numparms = 2; - df->parm_start = numpr_globals; - index = QCC_PR_GetDef(type_float, "indexs___", def, true, 0, false); - value = QCC_PR_GetDef(def->type, "value___", def, true, 0, false); - locals_end = numpr_globals; - df->locals = locals_end - df->parm_start; - - G_FUNCTION(scope->ofs) = df - functions; - - if (fasttrackpossible) - { - QCC_dstatement_t *st; - - QCC_PR_Statement(pr_opcodes+OP_IFNOT_I, fasttrackpossible, NULL, &st); - //note that the array size is coded into the globals, one index before the array. - - QCC_PR_Statement3(&pr_opcodes[OP_CONV_FTOI], index, NULL, index, true); //address stuff is integer based, but standard qc (which this accelerates in supported engines) only supports floats - QCC_PR_SimpleStatement (OP_BOUNDCHECK, index->ofs, ((int*)qcc_pr_globals)[def->ofs-1]+1, 0, true);//annoy the programmer. :p - if (def->type->size != 1)//shift it upwards for larger types - QCC_PR_Statement3(&pr_opcodes[OP_MUL_I], index, QCC_MakeIntConst(def->type->size), index, true); - QCC_PR_Statement3(&pr_opcodes[OP_GLOBALADDRESS], def, index, index, true); //comes with built in add - if (def->type->size >= 3) - QCC_PR_Statement3(&pr_opcodes[OP_STOREP_V], value, index, NULL, true); //*b = a - else - QCC_PR_Statement3(&pr_opcodes[OP_STOREP_F], value, index, NULL, true); - QCC_PR_Statement(&pr_opcodes[OP_RETURN], value, NULL, NULL); - - //finish the jump - st->b = &statements[numstatements] - st; - } - - QCC_PR_Statement3(pr_opcodes+OP_BITAND_F, index, index, index, false); - QCC_PR_ArraySetRecurseDivide(def, index, value, 0, def->arraysize); - - QCC_PR_Statement(pr_opcodes+OP_DONE, 0, 0, NULL); - - - - QCC_WriteAsmFunction(pr_scope, df->first_statement, df->parm_start); - - QCC_FreeTemps(); -} - -//register a def, and all of it's sub parts. -//only the main def is of use to the compiler. -//the subparts are emitted to the compiler and allow correct saving/loading -//be careful with fields, this doesn't allocated space, so will it allocate fields. It only creates defs at specified offsets. -QCC_def_t *QCC_PR_DummyDef(QCC_type_t *type, char *name, QCC_def_t *scope, int arraysize, unsigned int ofs, int referable, pbool saved) -{ - char array[64]; - char newname[256]; - int a; - QCC_def_t *def, *first=NULL; - -#define KEYWORD(x) if (!STRCMP(name, #x) && keyword_##x) {if (keyword_##x)QCC_PR_ParseWarning(WARN_KEYWORDDISABLED, "\""#x"\" keyword used as variable name%s", keywords_coexist?" - coexisting":" - disabling");keyword_##x=keywords_coexist;} - if (name) - { - KEYWORD(var); - KEYWORD(thinktime); - KEYWORD(for); - KEYWORD(switch); - KEYWORD(case); - KEYWORD(default); - KEYWORD(goto); - if (type->type != ev_function) - KEYWORD(break); - KEYWORD(continue); - KEYWORD(state); - KEYWORD(string); - if (qcc_targetformat != QCF_HEXEN2) - KEYWORD(float); //hmm... hexen2 requires this... - KEYWORD(entity); - KEYWORD(vector); - KEYWORD(const); - KEYWORD(asm); - } - - for (a = 0; a < (arraysize?arraysize:1); a++) - { - if (a == 0) - *array = '\0'; - else - sprintf(array, "[%i]", a); - - if (name) - sprintf(newname, "%s%s", name, array); - else - *newname = *""; - - // allocate a new def - def = (void *)qccHunkAlloc (sizeof(QCC_def_t)); - memset (def, 0, sizeof(*def)); - def->next = NULL; - def->arraysize = a?0:arraysize; - if (name) - { - pr.def_tail->next = def; - pr.def_tail = def; - } - - if (a > 0) - def->references++; - - def->s_line = pr_source_line; - def->s_file = s_file; - if (a) - def->initialized = 1; - - def->name = (void *)qccHunkAlloc (strlen(newname)+1); - strcpy (def->name, newname); - def->type = type; - - def->scope = scope; - def->saved = saved; - - //if (type->type = ev_field) - def->constant = true; - - if (ofs + type->size*a >= MAX_REGS) - QCC_Error(ERR_TOOMANYGLOBALS, "MAX_REGS is too small"); - def->ofs = ofs + type->size*a; - if (!first) - first = def; - -// printf("Emited %s\n", newname); - - if (type->type == ev_struct) - { - int partnum; - QCC_type_t *parttype; - parttype = type->param; - for (partnum = 0; partnum < type->num_parms; partnum++) - { - switch (parttype->type) - { - case ev_vector: - sprintf(newname, "%s%s.%s", name, array, parttype->name); - QCC_PR_DummyDef(parttype, newname, scope, 0, ofs + type->size*a + parttype->ofs, false, saved); - - sprintf(newname, "%s%s.%s_x", name, array, parttype->name); - QCC_PR_DummyDef(type_float, newname, scope, 0, ofs + type->size*a + parttype->ofs, false, false); - sprintf(newname, "%s%s.%s_y", name, array, parttype->name); - QCC_PR_DummyDef(type_float, newname, scope, 0, ofs + type->size*a + parttype->ofs+1, false, false); - sprintf(newname, "%s%s.%s_z", name, array, parttype->name); - QCC_PR_DummyDef(type_float, newname, scope, 0, ofs + type->size*a + parttype->ofs+2, false, false); - break; - - case ev_float: - case ev_string: - case ev_entity: - case ev_field: - case ev_pointer: - case ev_integer: - case ev_struct: - case ev_union: - case ev_variant: //for lack of any better alternative - sprintf(newname, "%s%s.%s", name, array, parttype->name); - QCC_PR_DummyDef(parttype, newname, scope, 0, ofs + type->size*a + parttype->ofs, false, saved); - break; - - case ev_function: - sprintf(newname, "%s%s.%s", name, array, parttype->name); - QCC_PR_DummyDef(parttype, newname, scope, 0, ofs + type->size*a +parttype->ofs, false, saved)->initialized = true; - break; - case ev_void: - break; - } - parttype=parttype->next; - } - } - else if (type->type == ev_vector) - { //do the vector thing. - sprintf(newname, "%s%s_x", name, array); - QCC_PR_DummyDef(type_float, newname, scope, 0, ofs + type->size*a+0, referable, false); - sprintf(newname, "%s%s_y", name, array); - QCC_PR_DummyDef(type_float, newname, scope, 0, ofs + type->size*a+1, referable, false); - sprintf(newname, "%s%s_z", name, array); - QCC_PR_DummyDef(type_float, newname, scope, 0, ofs + type->size*a+2, referable, false); - } - else if (type->type == ev_field) - { - if (type->aux_type->type == ev_vector) - { - //do the vector thing. - sprintf(newname, "%s%s_x", name, array); - QCC_PR_DummyDef(type_floatfield, newname, scope, 0, ofs + type->size*a+0, referable, false); - sprintf(newname, "%s%s_y", name, array); - QCC_PR_DummyDef(type_floatfield, newname, scope, 0, ofs + type->size*a+1, referable, false); - sprintf(newname, "%s%s_z", name, array); - QCC_PR_DummyDef(type_floatfield, newname, scope, 0, ofs + type->size*a+2, referable, false); - } - } - first->deftail = pr.def_tail; - } - - if (referable) - { - if (!pHash_Get(&globalstable, "end_sys_fields")) - first->references++; //anything above needs to be left in, and so warning about not using it is just going to pee people off. - if (!arraysize && first->type->type != ev_field) - first->constant = false; - if (scope) - pHash_Add(&localstable, first->name, first, qccHunkAlloc(sizeof(bucket_t))); - else - pHash_Add(&globalstable, first->name, first, qccHunkAlloc(sizeof(bucket_t))); - - if (!scope && asmfile) - fprintf(asmfile, "%s %s;\n", TypeName(first->type), first->name); - } - - return first; -} - -/* -============ -PR_GetDef - -If type is NULL, it will match any type -If allocate is true, a new def will be allocated if it can't be found -If arraysize=0, its not an array and has 1 element. -If arraysize>0, its an array and requires array notation -If arraysize<0, its an array with undefined size - GetDef will fail if its not already allocated. -============ -*/ - -QCC_def_t *QCC_PR_GetDef (QCC_type_t *type, char *name, QCC_def_t *scope, pbool allocate, int arraysize, pbool saved) -{ - int ofs; - QCC_def_t *def; -// char element[MAX_NAME]; - unsigned int i; - QCC_def_t *foundstatic = NULL; - - if (!allocate) - arraysize = -1; - - if (scope) - { - def = Hash_Get(&localstable, name); - - while(def) - { - if ( def->scope && def->scope != scope) - { - def = Hash_GetNext(&localstable, name, def); - continue; // in a different function - } - - if (type && typecmp(def->type, type)) - QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHREDEC, def, "Type mismatch on redeclaration of %s. %s, should be %s",name, TypeName(type), TypeName(def->type)); - if (def->arraysize != arraysize && arraysize>=0) - QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHARRAYSIZE, def, "Array sizes for redecleration of %s do not match",name); - if (allocate && scope) - { - QCC_PR_ParseWarning (WARN_DUPLICATEDEFINITION, "%s duplicate definition ignored", name); - QCC_PR_ParsePrintDef(WARN_DUPLICATEDEFINITION, def); -// if (!scope) -// QCC_PR_ParsePrintDef(def); - } - return def; - } - } - - - def = Hash_Get(&globalstable, name); - - while(def) - { - if ( def->scope && def->scope != scope) - { - def = Hash_GetNext(&globalstable, name, def); - continue; // in a different function - } - - if (def->isstatic && def->s_file != s_file) - { //warn? or would that be pointless? - foundstatic = def; - def = Hash_GetNext(&globalstable, name, def); - continue; // in a different function - } - - if (type && typecmp(def->type, type)) - { - if (!pr_scope) - QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHREDEC, def, "Type mismatch on redeclaration of %s. %s, should be %s",name, TypeName(type), TypeName(def->type)); - } - if (def->arraysize != arraysize && arraysize>=0) - QCC_PR_ParseErrorPrintDef(ERR_TYPEMISMATCHARRAYSIZE, def, "Array sizes for redecleration of %s do not match",name); - if (allocate && scope) - { - if (pr_scope) - { //warn? or would that be pointless? - def = Hash_GetNext(&globalstable, name, def); - continue; // in a different function - } - - QCC_PR_ParseWarning (WARN_DUPLICATEDEFINITION, "%s duplicate definition ignored", name); - QCC_PR_ParsePrintDef(WARN_DUPLICATEDEFINITION, def); -// if (!scope) -// QCC_PR_ParsePrintDef(def); - } - return def; - } - - if (pHash_Get != &Hash_Get && !allocate) //do we want to try case insensative too? - { - if (scope) - { - def = pHash_Get(&localstable, name); - - while(def) - { - if ( def->scope && def->scope != scope) - { - def = pHash_GetNext(&localstable, name, def); - continue; // in a different function - } - - if (type && typecmp(def->type, type)) - QCC_PR_ParseError (ERR_TYPEMISMATCHREDEC, "Type mismatch on redeclaration of %s. %s, should be %s",name, TypeName(type), TypeName(def->type)); - if (def->arraysize != arraysize && arraysize>=0) - QCC_PR_ParseError (ERR_TYPEMISMATCHARRAYSIZE, "Array sizes for redecleration of %s do not match",name); - if (allocate && scope) - { - QCC_PR_ParseWarning (WARN_DUPLICATEDEFINITION, "%s duplicate definition ignored", name); - QCC_PR_ParsePrintDef(WARN_DUPLICATEDEFINITION, def); - // if (!scope) - // QCC_PR_ParsePrintDef(def); - } - return def; - } - } - - - def = pHash_Get(&globalstable, name); - - while(def) - { - if ( def->scope && def->scope != scope) - { - def = pHash_GetNext(&globalstable, name, def); - continue; // in a different function - } - - if (def->isstatic && def->s_file != s_file) - { //warn? or would that be pointless? - foundstatic = def; - def = Hash_GetNext(&globalstable, name, def); - continue; // in a different function - } - - if (type && typecmp(def->type, type)) - { - if (!pr_scope) - QCC_PR_ParseError (ERR_TYPEMISMATCHREDEC, "Type mismatch on redeclaration of %s. %s, should be %s",name, TypeName(type), TypeName(def->type)); - } - if (def->arraysize != arraysize && arraysize>=0) - QCC_PR_ParseError (ERR_TYPEMISMATCHARRAYSIZE, "Array sizes for redecleration of %s do not match",name); - if (allocate && scope) - { - if (pr_scope) - { //warn? or would that be pointless? - def = pHash_GetNext(&globalstable, name, def); - continue; // in a different function - } - - QCC_PR_ParseWarning (WARN_DUPLICATEDEFINITION, "%s duplicate definition ignored", name); - QCC_PR_ParsePrintDef(WARN_DUPLICATEDEFINITION, def); - // if (!scope) - // QCC_PR_ParsePrintDef(def); - } - return def; - } - } - - if (foundstatic && !allocate) - { - QCC_PR_ParseWarning (WARN_DUPLICATEDEFINITION, "%s defined static", name); - QCC_PR_ParsePrintDef(WARN_DUPLICATEDEFINITION, foundstatic); - } - - if (!allocate) - return NULL; - - if (arraysize < 0) - { - QCC_PR_ParseError (ERR_ARRAYNEEDSSIZE, "First declaration of array %s with no size",name); - } - - if (scope) - { - if (QCC_PR_GetDef(type, name, NULL, false, arraysize, false)) - QCC_PR_ParseWarning(WARN_SAMENAMEASGLOBAL, "Local \"%s\" defined with name of a global", name); - } - - ofs = numpr_globals; - if (arraysize) - { //write the array size - ofs = QCC_GetFreeOffsetSpace(1 + (type->size * arraysize)); - - ((int *)qcc_pr_globals)[ofs] = arraysize-1; //An array needs the size written first. This is a hexen2 opcode thing. - ofs++; - } - else - ofs = QCC_GetFreeOffsetSpace(type->size); - - def = QCC_PR_DummyDef(type, name, scope, arraysize, ofs, true, saved); - - //fix up fields. - if (type->type == ev_field && allocate != 2) - { - for (i = 0; i < type->size*(arraysize?arraysize:1); i++) //make arrays of fields work. - *(int *)&qcc_pr_globals[def->ofs+i] = pr.size_fields+i; - - pr.size_fields += i; - } - - if (scope) - { - def->nextlocal = pr.localvars; - pr.localvars = def; - } - - return def; -} - -QCC_def_t *QCC_PR_DummyFieldDef(QCC_type_t *type, char *name, QCC_def_t *scope, int arraysize, unsigned int *fieldofs, pbool saved) -{ - char array[64]; - char newname[256]; - int a, parms; - QCC_def_t *def, *first=NULL; - unsigned int maxfield, startfield; - QCC_type_t *ftype; - pbool isunion; - startfield = *fieldofs; - maxfield = startfield; - - for (a = 0; a < (arraysize?arraysize:1); a++) - { - if (a == 0) - *array = '\0'; - else - sprintf(array, "[%i]", a); - - if (*name) - { - sprintf(newname, "%s%s", name, array); - - // allocate a new def - def = (void *)qccHunkAlloc (sizeof(QCC_def_t)); - memset (def, 0, sizeof(*def)); - def->next = NULL; - def->arraysize = arraysize; - - pr.def_tail->next = def; - pr.def_tail = def; - - def->s_line = pr_source_line; - def->s_file = s_file; - - def->name = (void *)qccHunkAlloc (strlen(newname)+1); - strcpy (def->name, newname); - def->type = type; - - def->scope = scope; - - def->ofs = QCC_GetFreeOffsetSpace(1); - ((int *)qcc_pr_globals)[def->ofs] = *fieldofs; - fieldofs++; - if (!first) - first = def; - } - else - { - def=NULL; - } - -// printf("Emited %s\n", newname); - - if ((type)->type == ev_struct||(type)->type == ev_union) - { - int partnum; - QCC_type_t *parttype; - if (def) - def->references++; - parttype = (type)->param; - isunion = ((type)->type == ev_union); - for (partnum = 0, parms = (type)->num_parms; partnum < parms; partnum++) - { - switch (parttype->type) - { - case ev_union: - case ev_struct: - if (*name) - sprintf(newname, "%s%s.%s", name, array, parttype->name); - else - sprintf(newname, "%s%s", parttype->name, array); - def = QCC_PR_DummyFieldDef(parttype, newname, scope, 1, fieldofs, saved); - break; - case ev_float: - case ev_string: - case ev_vector: - case ev_entity: - case ev_field: - case ev_pointer: - case ev_integer: - case ev_variant: - if (*name) - sprintf(newname, "%s%s.%s", name, array, parttype->name); - else - sprintf(newname, "%s%s", parttype->name, array); - ftype = QCC_PR_NewType("FIELD TYPE", ev_field, false); - ftype->aux_type = parttype; - if (parttype->type == ev_vector) - ftype->size = parttype->size; //vector fields create a _y and _z too, so we need this still. - def = QCC_PR_GetDef(NULL, newname, scope, false, 0, saved); - if (!def) - { - def = QCC_PR_GetDef(ftype, newname, scope, true, 0, saved); - } - else - { - QCC_PR_ParseWarning(WARN_CONFLICTINGUNIONMEMBER, "conflicting offsets for union/struct expansion of %s. Ignoring new def.", newname); - QCC_PR_ParsePrintDef(WARN_CONFLICTINGUNIONMEMBER, def); - } - break; - - case ev_function: - if (*name) - sprintf(newname, "%s%s.%s", name, array, parttype->name); - else - sprintf(newname, "%s%s", parttype->name, array); - ftype = QCC_PR_NewType("FIELD TYPE", ev_field, false); - ftype->aux_type = parttype; - def = QCC_PR_GetDef(ftype, newname, scope, true, 0, saved); - def->initialized = true; - ((int *)qcc_pr_globals)[def->ofs] = *fieldofs; - *fieldofs += parttype->size; - break; - case ev_void: - break; - } - if (*fieldofs > maxfield) - maxfield = *fieldofs; - if (isunion) - *fieldofs = startfield; - - type = parttype; - parttype=parttype->next; - } - } - } - - *fieldofs = maxfield; //final size of the union. - return first; -} - - - -void QCC_PR_ExpandUnionToFields(QCC_type_t *type, int *fields) -{ - QCC_type_t *pass = type->aux_type; - QCC_PR_DummyFieldDef(pass, "", pr_scope, 1, fields, true); -} - - -void QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *def, QCC_type_t *type, int offset) -{ - QCC_def_t *tmp; - int i; - - if (arraysize) - { - //arrays go recursive - QCC_PR_Expect("{"); - for (i = 0; i < arraysize; i++) - { - QCC_PR_ParseInitializerType(0, def, type, offset + i*type->size); - if (!QCC_PR_CheckToken(",")) - break; - } - QCC_PR_Expect("}"); - } - else - { - if (type->type == ev_function && pr_token_type == tt_punct) - { - /*begin function special case*/ - QCC_def_t *parentfunc = pr_scope; - QCC_function_t *f; - QCC_dfunction_t *df; - QCC_type_t *parm; - - tmp = NULL; - - def->references++; - pr_scope = def; - if (QCC_PR_CheckToken ("#") || QCC_PR_CheckToken (":")) - { - int binum = 0; - if (pr_token_type == tt_immediate - && pr_immediate_type == type_float - && pr_immediate._float == (int)pr_immediate._float) - binum = (int)pr_immediate._float; - else if (pr_token_type == tt_immediate && pr_immediate_type == type_integer) - binum = pr_immediate._int; - else - QCC_PR_ParseError (ERR_BADBUILTINIMMEDIATE, "Bad builtin immediate"); - QCC_PR_Lex(); - - if (def->initialized) - for (i = 0; i < numfunctions; i++) - { - if (functions[i].first_statement == -binum) - { - tmp = QCC_MakeIntConst(i); - break; - } - } - - if (!tmp) - { - f = (void *)qccHunkAlloc (sizeof(QCC_function_t)); - f->builtin = binum; - - locals_start = locals_end = OFS_PARM0; //hmm... - } - else - f = NULL; - } - else - f = QCC_PR_ParseImmediateStatements (type); - if (!tmp) - { - pr_scope = parentfunc; - tmp = QCC_MakeIntConst(numfunctions); - f->def = def; - - if (numfunctions >= MAX_FUNCTIONS) - QCC_Error(ERR_INTERNAL, "Too many function defs"); - - // fill in the dfunction - df = &functions[numfunctions]; - numfunctions++; - if (f->builtin) - df->first_statement = -f->builtin; - else - df->first_statement = f->code; - - if (f->builtin && opt_function_names) - optres_function_names += strlen(f->def->name); - else - df->s_name = QCC_CopyString (f->def->name); - df->s_file = s_file2; - df->numparms = f->def->type->num_parms; - df->locals = locals_end - locals_start; - df->parm_start = locals_start; - for (i=0,parm = type->param ; inumparms ; i++, parm = parm->next) - { - df->parm_size[i] = parm->size; - } - /*end function special case*/ - } - } - else if (type->type == ev_string && QCC_PR_CheckName("_")) - { - char trname[128]; - QCC_PR_Expect("("); - if (pr_token_type != tt_immediate || pr_immediate_type->type != ev_string) - QCC_PR_ParseError(0, "_() intrinsic accepts only a string immediate"); - tmp = QCC_MakeStringConst(pr_immediate_string); - QCC_PR_Lex(); - QCC_PR_Expect(")"); - - if (!pr_scope || def->constant) - { - QCC_def_t *dt; - sprintf(trname, "dotranslate_%i", ++dotranslate_count); - dt = QCC_PR_DummyDef(type_string, trname, pr_scope, 0, offset, true, true); - dt->references = 1; - dt->constant = 1; - dt->initialized = 1; - } - } - else if (type->type == ev_struct || type->type == ev_union) - { - //structs go recursive - QCC_type_t *parttype; - int partnum; - int parms; - pbool isunion; - QCC_PR_Expect("{"); - - isunion = ((type)->type == ev_union); - for (partnum = 0, parttype = (type)->param, parms = (type)->num_parms; partnum < parms; partnum++, parttype = parttype->next) - { - QCC_PR_ParseInitializerType(parttype->arraysize, def, parttype, offset + parttype->ofs); - if (isunion || !QCC_PR_CheckToken(",")) - break; - } - QCC_PR_Expect("}"); - return; - } - else - { - tmp = QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA); - if (typecmp(type, tmp->type)) - { - /*you can cast from const 0 to anything*/ - if (tmp->type->size == type->size && (tmp->type->type == ev_integer || tmp->type->type == ev_float) && tmp->constant && !G_INT(tmp->ofs)) - { - } - /*cast from int->float will convert*/ - else if (type->type == ev_float && tmp->type->type == ev_integer) - tmp = QCC_PR_Statement (&pr_opcodes[OP_CONV_ITOF], tmp, 0, NULL); - /*cast from float->int will convert*/ - else if (type->type == ev_integer && tmp->type->type == ev_float) - tmp = QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], tmp, 0, NULL); - else - QCC_PR_ParseErrorPrintDef (ERR_BADIMMEDIATETYPE, def, "wrong initializer type"); - } - } - - if (!pr_scope || def->constant) - { - if (!tmp->constant) - QCC_PR_ParseErrorPrintDef (ERR_BADIMMEDIATETYPE, def, "initializer is not constant"); - - if (def->initialized) - { - for (i = 0; (unsigned)i < type->size; i++) - if (G_INT(offset+i) != G_INT(tmp->ofs+i)) - QCC_PR_ParseErrorPrintDef (ERR_REDECLARATION, def, "incompatible redeclaration"); - } - else - { - for (i = 0; (unsigned)i < type->size; i++) - G_INT(offset+i) = G_INT(tmp->ofs+i); - } - } - else - { - QCC_def_t lhs, rhs; - if (def->initialized) - QCC_PR_ParseErrorPrintDef (ERR_REDECLARATION, def, "%s initialised twice", def->name); - - memset(&lhs, 0, sizeof(lhs)); - memset(&rhs, 0, sizeof(rhs)); - def->references++; - for (i = 0; (unsigned)i < type->size; ) - { - rhs.type = lhs.type = type_float; - lhs.ofs = offset+i; - tmp->references++; - rhs.ofs = tmp->ofs+i; - - if (type->size - i >= 3) - { - QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_V], &rhs, &lhs, NULL)); - i+=3; - } - else - { - QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_F], &rhs, &lhs, NULL)); - i++; - } - } - } - QCC_FreeTemp(tmp); - } -} - -void QCC_PR_ParseInitializerDef(QCC_def_t *def, pbool isvar, pbool isconst) -{ - def->constant = (isconst || (!isvar && !pr_scope)); - QCC_PR_ParseInitializerType(def->arraysize, def, def->type, def->ofs); - if (!def->initialized) - def->initialized = 1; -} - -int accglobalsblock; //0 = error, 1 = var, 2 = function, 3 = objdata -/* -================ -PR_ParseDefs - -Called at the outer layer and when a local statement is hit -================ -*/ -void QCC_PR_ParseDefs (char *classname) -{ - char *name; - QCC_type_t *type, *parm; - QCC_def_t *def, *d; - QCC_function_t *f; - QCC_dfunction_t *df; - int i = 0; // warning: ‘i’ may be used uninitialized in this function - pbool shared=false; - pbool isstatic=defaultstatic; - pbool externfnc=false; - pbool isconstant = false; - pbool isvar = false; - pbool noref = defaultnoref; - pbool nosave = false; - pbool allocatenew = true; - pbool inlinefunction = false; - gofs_t oldglobals; - int arraysize; - - while (QCC_PR_CheckToken(";")) - ; - - if (QCC_PR_CheckKeyword(keyword_enum, "enum")) - { - if (QCC_PR_CheckKeyword(keyword_integer, "integer") || QCC_PR_CheckKeyword(keyword_int, "int")) - { - int iv = 0; - QCC_PR_Expect("{"); - i = 0; - d = NULL; - while(1) - { - name = QCC_PR_ParseName(); - if (QCC_PR_CheckToken("=")) - { - if (pr_token_type != tt_immediate && pr_immediate_type->type != ev_integer) - { - def = QCC_PR_GetDef(NULL, QCC_PR_ParseName(), NULL, false, 0, false); - if (def) - { - if (!def->constant) - QCC_PR_ParseError(ERR_NOTANUMBER, "enum - %s is not a constant", def->name); - else - iv = G_INT(def->ofs); - } - else - QCC_PR_ParseError(ERR_NOTANUMBER, "enum - not a number"); - } - else - { - iv = pr_immediate._int; - QCC_PR_Lex(); - } - } - def = QCC_MakeIntConst(iv); - pHash_Add(&globalstable, name, def, qccHunkAlloc(sizeof(bucket_t))); - iv++; - - if (QCC_PR_CheckToken("}")) - break; - QCC_PR_Expect(","); - if (QCC_PR_CheckToken("}")) - break; // accept trailing comma - } - } - else - { - float fv = 0; - QCC_PR_CheckKeyword(keyword_float, "float"); - QCC_PR_Expect("{"); - i = 0; - d = NULL; - while(1) - { - name = QCC_PR_ParseName(); - if (QCC_PR_CheckToken("=")) - { - if (pr_token_type != tt_immediate && pr_immediate_type->type != ev_float) - { - def = QCC_PR_GetDef(NULL, QCC_PR_ParseName(), NULL, false, 0, false); - if (def) - { - if (!def->constant) - QCC_PR_ParseError(ERR_NOTANUMBER, "enum - %s is not a constant", def->name); - else - fv = G_FLOAT(def->ofs); - } - else - QCC_PR_ParseError(ERR_NOTANUMBER, "enum - not a number"); - } - else - { - fv = pr_immediate._float; - QCC_PR_Lex(); - } - } - def = QCC_MakeFloatConst(fv); - pHash_Add(&globalstable, name, def, qccHunkAlloc(sizeof(bucket_t))); - fv++; - - if (QCC_PR_CheckToken("}")) - break; - QCC_PR_Expect(","); - if (QCC_PR_CheckToken("}")) - break; // accept trailing comma - } - } - QCC_PR_Expect(";"); - return; - } - - if (QCC_PR_CheckKeyword(keyword_enumflags, "enumflags")) - { - int bits; - - if (QCC_PR_CheckKeyword(keyword_integer, "integer") || QCC_PR_CheckKeyword(keyword_int, "int")) - { - int iv = 1; - QCC_PR_Expect("{"); - i = 0; - d = NULL; - while(1) - { - name = QCC_PR_ParseName(); - if (QCC_PR_CheckToken("=")) - { - if (pr_token_type != tt_immediate && pr_immediate_type->type != ev_integer) - { - def = QCC_PR_GetDef(NULL, QCC_PR_ParseName(), NULL, false, 0, false); - if (def) - { - if (!def->constant) - QCC_PR_ParseError(ERR_NOTANUMBER, "enumflags - %s is not a constant", def->name); - else - iv = G_INT(def->ofs); - } - else - QCC_PR_ParseError(ERR_NOTANUMBER, "enumflags - not a number"); - } - else - { - iv = pr_immediate._int; - QCC_PR_Lex(); - } - } - - bits = 0; - i = (int)iv; - if (i != iv) - QCC_PR_ParseWarning(WARN_ENUMFLAGS_NOTINTEGER, "enumflags - %f not an integer", iv); - else - { - while(i) - { - if (((i>>1)<<1) != i) - bits++; - i>>=1; - } - if (bits > 1) - QCC_PR_ParseWarning(WARN_ENUMFLAGS_NOTBINARY, "enumflags - value %i not a single bit", (int)iv); - } - - def = QCC_MakeIntConst(iv); - pHash_Add(&globalstable, name, def, qccHunkAlloc(sizeof(bucket_t))); - - iv*=2; - - if (QCC_PR_CheckToken("}")) - break; - QCC_PR_Expect(","); - } - } - else - { - float fv = 1; - QCC_PR_CheckKeyword(keyword_float, "float"); - - QCC_PR_Expect("{"); - i = 0; - d = NULL; - while(1) - { - name = QCC_PR_ParseName(); - if (QCC_PR_CheckToken("=")) - { - if (pr_token_type != tt_immediate && pr_immediate_type->type != ev_float) - { - def = QCC_PR_GetDef(NULL, QCC_PR_ParseName(), NULL, false, 0, false); - if (def) - { - if (!def->constant) - QCC_PR_ParseError(ERR_NOTANUMBER, "enumflags - %s is not a constant", def->name); - else - fv = G_FLOAT(def->ofs); - } - else - QCC_PR_ParseError(ERR_NOTANUMBER, "enumflags - not a number"); - } - else - { - fv = pr_immediate._float; - QCC_PR_Lex(); - } - } - - bits = 0; - i = (int)fv; - if (i != fv) - QCC_PR_ParseWarning(WARN_ENUMFLAGS_NOTINTEGER, "enumflags - %f not an integer", fv); - else - { - while(i) - { - if (((i>>1)<<1) != i) - bits++; - i>>=1; - } - if (bits > 1) - QCC_PR_ParseWarning(WARN_ENUMFLAGS_NOTBINARY, "enumflags - value %i not a single bit", (int)fv); - } - - def = QCC_MakeFloatConst(fv); - pHash_Add(&globalstable, name, def, qccHunkAlloc(sizeof(bucket_t))); - - fv*=2; - - if (QCC_PR_CheckToken("}")) - break; - QCC_PR_Expect(","); - } - } - QCC_PR_Expect(";"); - return; - } - - if (QCC_PR_CheckKeyword (keyword_typedef, "typedef")) - { - type = QCC_PR_ParseType(true, false); - if (!type) - { - QCC_PR_ParseError(ERR_NOTANAME, "typedef found unexpected tokens"); - } - type->name = QCC_CopyString(pr_token)+strings; - type->typedefed = true; - QCC_PR_Lex(); - QCC_PR_Expect(";"); - return; - } - - if (flag_acc) - { - char *oldp; - if (QCC_PR_CheckKeyword (keyword_codesys, "CodeSys")) //reacc support. - { - if (ForcedCRC) - QCC_PR_ParseError(ERR_BADEXTENSION, "progs crc was already specified - only one is allowed"); - ForcedCRC = (int)pr_immediate._float; - QCC_PR_Lex(); - QCC_PR_Expect(";"); - return; - } - - oldp = pr_file_p; - if (QCC_PR_CheckKeyword (keyword_var, "var")) //reacc support. - { - if (accglobalsblock == 3) - { - if (!QCC_PR_GetDef(type_void, "end_sys_fields", NULL, false, 0, false)) - QCC_PR_GetDef(type_void, "end_sys_fields", NULL, true, 0, false); - } - - QCC_PR_ParseName(); - if (QCC_PR_CheckToken(":")) - accglobalsblock = 1; - pr_file_p = oldp; - QCC_PR_Lex(); - } - - if (QCC_PR_CheckKeyword (keyword_function, "function")) //reacc support. - { - accglobalsblock = 2; - } - if (QCC_PR_CheckKeyword (keyword_objdata, "objdata")) //reacc support. - { - if (accglobalsblock == 3) - { - if (!QCC_PR_GetDef(type_void, "end_sys_fields", NULL, false, 0, false)) - QCC_PR_GetDef(type_void, "end_sys_fields", NULL, true, 0, false); - } - else - if (!QCC_PR_GetDef(type_void, "end_sys_globals", NULL, false, 0, false)) - QCC_PR_GetDef(type_void, "end_sys_globals", NULL, true, 0, false); - accglobalsblock = 3; - } - } - - if (!pr_scope) - switch(accglobalsblock)//reacc support. - { - case 1: - { - char *oldp = pr_file_p; - name = QCC_PR_ParseName(); - if (!QCC_PR_CheckToken(":")) //nope, it wasn't! - { - QCC_PR_IncludeChunk(name, true, NULL); - QCC_PR_Lex(); - QCC_PR_UnInclude(); - pr_file_p = oldp; - break; - } - if (QCC_PR_CheckKeyword(keyword_object, "object")) - QCC_PR_GetDef(type_entity, name, NULL, true, 0, true); - else if (QCC_PR_CheckKeyword(keyword_string, "string")) - QCC_PR_GetDef(type_string, name, NULL, true, 0, true); - else if (QCC_PR_CheckKeyword(keyword_real, "real")) - { - def = QCC_PR_GetDef(type_float, name, NULL, true, 0, true); - if (QCC_PR_CheckToken("=")) - { - G_FLOAT(def->ofs) = pr_immediate._float; - QCC_PR_Lex(); - } - } - else if (QCC_PR_CheckKeyword(keyword_vector, "vector")) - { - def = QCC_PR_GetDef(type_vector, name, NULL, true, 0, true); - if (QCC_PR_CheckToken("=")) - { - QCC_PR_Expect("["); - G_FLOAT(def->ofs+0) = pr_immediate._float; - QCC_PR_Lex(); - G_FLOAT(def->ofs+1) = pr_immediate._float; - QCC_PR_Lex(); - G_FLOAT(def->ofs+2) = pr_immediate._float; - QCC_PR_Lex(); - QCC_PR_Expect("]"); - } - } - else if (QCC_PR_CheckKeyword(keyword_pfunc, "pfunc")) - QCC_PR_GetDef(type_function, name, NULL, true, 0, true); - else - QCC_PR_ParseError(ERR_BADNOTTYPE, "Bad type\n"); - QCC_PR_Expect (";"); - - if (QCC_PR_CheckKeyword (keyword_system, "system")) - QCC_PR_Expect (";"); - return; - } - case 2: - name = QCC_PR_ParseName(); - QCC_PR_GetDef(type_function, name, NULL, true, 1, true); - QCC_PR_CheckToken (";"); - return; - case 3: - { - char *oldp = pr_file_p; - name = QCC_PR_ParseName(); - if (!QCC_PR_CheckToken(":")) //nope, it wasn't! - { - QCC_PR_IncludeChunk(name, true, NULL); - QCC_PR_Lex(); - QCC_PR_UnInclude(); - pr_file_p = oldp; - break; - } - if (QCC_PR_CheckKeyword(keyword_object, "object")) - QCC_PR_GetDef(QCC_PR_FieldType(type_entity), name, NULL, true, 0, true); - else if (QCC_PR_CheckKeyword(keyword_string, "string")) - QCC_PR_GetDef(QCC_PR_FieldType(type_string), name, NULL, true, 0, true); - else if (QCC_PR_CheckKeyword(keyword_real, "real")) - QCC_PR_GetDef(QCC_PR_FieldType(type_float), name, NULL, true, 0, true); - else if (QCC_PR_CheckKeyword(keyword_vector, "vector")) - QCC_PR_GetDef(QCC_PR_FieldType(type_vector), name, NULL, true, 0, true); - else if (QCC_PR_CheckKeyword(keyword_pfunc, "pfunc")) - QCC_PR_GetDef(QCC_PR_FieldType(type_function), name, NULL, true, 0, true); - else - QCC_PR_ParseError(ERR_BADNOTTYPE, "Bad type\n"); - QCC_PR_Expect (";"); - return; - } - } - - while(1) - { - if (QCC_PR_CheckKeyword(keyword_extern, "extern")) - externfnc=true; - else if (QCC_PR_CheckKeyword(keyword_shared, "shared")) - { - shared=true; - if (pr_scope) - QCC_PR_ParseError (ERR_NOSHAREDLOCALS, "Cannot have shared locals"); - } - else if (QCC_PR_CheckKeyword(keyword_const, "const")) - isconstant = true; - else if (QCC_PR_CheckKeyword(keyword_var, "var")) - isvar = true; - else if (!pr_scope && QCC_PR_CheckKeyword(keyword_var, "static")) - isstatic = true; - else if (!pr_scope && QCC_PR_CheckKeyword(keyword_var, "nonstatic")) - isstatic = false; - else if (QCC_PR_CheckKeyword(keyword_noref, "noref")) - noref=true; - else if (QCC_PR_CheckKeyword(keyword_nosave, "nosave")) - nosave = true; - else - break; - } - - type = QCC_PR_ParseType (false, false); - if (type == NULL) //ignore - return; - - inlinefunction = type_inlinefunction; - - if (externfnc && type->type != ev_function) - { - printf ("Only functions may be defined as external (yet)\n"); - externfnc=false; - } - - if (!pr_scope && QCC_PR_CheckKeyword(keyword_function, "function")) //reacc support. - { - name = QCC_PR_ParseName (); - QCC_PR_Expect("("); - type = QCC_PR_ParseFunctionTypeReacc(false, type); - QCC_PR_Expect(";"); - def = QCC_PR_GetDef (type, name, NULL, true, 0, false); - - if (autoprototype) - { //ignore the code and stuff - - if (QCC_PR_CheckKeyword(keyword_external, "external")) - { //builtin - QCC_PR_Lex(); - QCC_PR_Expect(";"); - } - else - { - int blev = 1; - - while (!QCC_PR_CheckToken("{")) //skip over the locals. - { - if (pr_token_type == tt_eof) - { - QCC_PR_ParseError(0, "Unexpected EOF"); - break; - } - QCC_PR_Lex(); - } - - //balance out the { and } - while(blev) - { - if (pr_token_type == tt_eof) - break; - if (QCC_PR_CheckToken("{")) - blev++; - else if (QCC_PR_CheckToken("}")) - blev--; - else - QCC_PR_Lex(); //ignore it. - } - } - return; - } - - def->references++; - - pr_scope = def; - f = QCC_PR_ParseImmediateStatements (type); - pr_scope = NULL; - def->initialized = 1; - def->isstatic = isstatic; - G_FUNCTION(def->ofs) = numfunctions; - f->def = def; -// if (pr_dumpasm) -// PR_PrintFunction (def); - - if (numfunctions >= MAX_FUNCTIONS) - QCC_Error(ERR_INTERNAL, "Too many function defs"); - -// fill in the dfunction - df = &functions[numfunctions]; - numfunctions++; - if (f->builtin) - df->first_statement = -f->builtin; - else - df->first_statement = f->code; - - if (f->builtin && opt_function_names) - optres_function_names += strlen(f->def->name); - else - df->s_name = QCC_CopyString (f->def->name); - df->s_file = s_file2; - df->numparms = f->def->type->num_parms; - df->locals = locals_end - locals_start; - df->parm_start = locals_start; - for (i=0,parm = type->param ; inumparms ; i++, parm = parm->next) - { - df->parm_size[i] = parm->size; - } - - return; - } - -// if (pr_scope && (type->type == ev_field) ) -// QCC_PR_ParseError ("Fields must be global"); - - do - { - if (QCC_PR_CheckToken (";")) - { - if (type->type == ev_field && (type->aux_type->type == ev_union || type->aux_type->type == ev_struct)) - { - QCC_PR_ExpandUnionToFields(type, &pr.size_fields); - return; - } -// if (type->type == ev_union) -// { -// return; -// } - QCC_PR_ParseError (ERR_TYPEWITHNONAME, "type with no name"); - name = NULL; - } - else - { - name = QCC_PR_ParseName (); - } - - if (QCC_PR_CheckToken("::") && !classname) - { - classname = name; - name = QCC_PR_ParseName(); - } - -//check for an array - - if ( QCC_PR_CheckToken ("[") ) - { - char *oldprfile = pr_file_p; - arraysize = 0; - if (QCC_PR_CheckToken("]")) - { - QCC_PR_Expect("="); - QCC_PR_Expect("{"); - QCC_PR_Lex(); - arraysize++; - while(1) - { - if(pr_token_type == tt_eof) - break; - if (QCC_PR_CheckToken(",")) - arraysize++; - if (QCC_PR_CheckToken("}")) - break; - QCC_PR_Lex(); - } - pr_file_p = oldprfile; - QCC_PR_Lex(); - } - else - { - arraysize = QCC_PR_IntConstExpr(); - QCC_PR_Expect("]"); - } - - if (arraysize < 1) - { - QCC_PR_ParseError (ERR_BADARRAYSIZE, "Definition of array (%s) size is not of a numerical value", name); - arraysize=1; //grrr... - } - } - else - arraysize = 0; - - if (QCC_PR_CheckToken("(")) - { - if (inlinefunction) - QCC_PR_ParseWarning(WARN_UNSAFEFUNCTIONRETURNTYPE, "Function returning function. Is this what you meant? (suggestion: use typedefs)"); - inlinefunction = false; - type = QCC_PR_ParseFunctionType(false, type); - } - - if (classname) - { - char *membername = name; - name = qccHunkAlloc(strlen(classname) + strlen(name) + 3); - sprintf(name, "%s::"MEMBERFIELDNAME, classname, membername); - if (!QCC_PR_GetDef(NULL, name, NULL, false, 0, false)) - QCC_PR_ParseError(ERR_NOTANAME, "%s %s is not a member of class %s\n", TypeName(type), membername, classname); - sprintf(name, "%s::%s", classname, membername); - - pr_classtype = QCC_TypeForName(classname); - if (!pr_classtype || !pr_classtype->parentclass) - QCC_PR_ParseError(ERR_NOTANAME, "%s is not a class\n", classname); - } - else - pr_classtype = NULL; - - oldglobals = numpr_globals; - - def = QCC_PR_GetDef (type, name, pr_scope, allocatenew, arraysize, !nosave); - - if (!def) - QCC_PR_ParseError(ERR_NOTANAME, "%s is not part of class %s", name, classname); - - if (noref) - def->references++; - - if (!def->initialized && shared) //shared count as initiialised - { - def->shared = shared; - def->initialized = true; - } - if (externfnc) - def->initialized = 2; - - if (isstatic) - { - if (def->s_file == s_file) - def->isstatic = isstatic; - else //if (type->type != ev_function && defaultstatic) //functions don't quite consitiute a definition - QCC_PR_ParseErrorPrintDef (ERR_REDECLARATION, def, "can't redefine non-static as static"); - } - -// check for an initialization - if (type->type == ev_function && (pr_scope)) - { - if ( QCC_PR_CheckToken ("=") ) - { - QCC_PR_ParseError (ERR_INITIALISEDLOCALFUNCTION, "local functions may not be initialised"); - } - - d = def; - while (d != def->deftail) - { - d = d->next; - d->initialized = 1; //fake function - G_FUNCTION(d->ofs) = 0; - } - - continue; - } - - if (type->type == ev_field && QCC_PR_CheckName ("alias")) - { - QCC_PR_ParseError(ERR_INTERNAL, "FTEQCC does not support this variant of decompiled hexenc\nPlease obtain the original version released by Raven Software instead."); - name = QCC_PR_ParseName(); - } - else if ( QCC_PR_CheckToken ("=") || ((type->type == ev_function) && (pr_token[0] == '{' || pr_token[0] == '[' || pr_token[0] == ':'))) //this is an initialisation (or a function) - { - if (def->shared) - QCC_PR_ParseError (ERR_SHAREDINITIALISED, "shared values may not be assigned an initial value", name); - if (def->initialized == 1) - { -// if (def->type->type == ev_function) -// { -// i = G_FUNCTION(def->ofs); -// df = &functions[i]; -// QCC_PR_ParseErrorPrintDef (ERR_REDECLARATION, def, "%s redeclared, prev instance is in %s", name, strings+df->s_file); -// } -// else -// QCC_PR_ParseErrorPrintDef(ERR_REDECLARATION, def, "%s redeclared", name); - } - - if (autoprototype) - { //ignore the code and stuff - if (QCC_PR_CheckToken("[")) - { - while (!QCC_PR_CheckToken("]")) - { - if (pr_token_type == tt_eof) - break; - QCC_PR_Lex(); - } - } - if (QCC_PR_CheckToken("{")) - { - int blev = 1; - //balance out the { and } - while(blev) - { - if (pr_token_type == tt_eof) - break; - if (QCC_PR_CheckToken("{")) - blev++; - else if (QCC_PR_CheckToken("}")) - blev--; - else - QCC_PR_Lex(); //ignore it. - } - } - else - { - QCC_PR_CheckToken("#"); - QCC_PR_Lex(); - } - continue; - } - - QCC_PR_ParseInitializerDef(def, isvar, isconstant); - } - else - { - if (type->type == ev_function && isvar) - { - isconstant = !isvar; - def->initialized = 1; - } - - if (type->type == ev_field) - { - if (isconstant) - def->constant = 2; //special flag on fields, 2, makes the pointer obtained from them also constant. - else if (isvar) - def->constant = 0; - else - def->constant = 1; - } - else - def->constant = isconstant; - } - - d = def; - while (d != def->deftail) - { - d = d->next; - d->constant = def->constant; - d->initialized = def->initialized; - } - } while (QCC_PR_CheckToken (",")); - - if (type->type == ev_function) - QCC_PR_CheckToken (";"); - else - { - if (!QCC_PR_CheckToken (";")) - QCC_PR_ParseWarning(WARN_UNDESIRABLECONVENTION, "Missing semicolon at end of definition"); - } -} - -/* -============ -PR_CompileFile - -compiles the 0 terminated text, adding defintions to the pr structure -============ -*/ -pbool QCC_PR_CompileFile (char *string, char *filename) -{ - jmp_buf oldjb; - if (!pr.memory) - QCC_Error (ERR_INTERNAL, "PR_CompileFile: Didn't clear"); - - QCC_PR_ClearGrabMacros (); // clear the frame macros - - compilingfile = filename; - - if (opt_filenames) - { - optres_filenames += strlen(filename); - pr_file_p = qccHunkAlloc(strlen(filename)+1); - strcpy(pr_file_p, filename); - s_file = pr_file_p - strings; - s_file2 = 0; - } - else - { - s_file = s_file2 = QCC_CopyString (filename); - } - pr_file_p = string; - - pr_source_line = 0; - - memcpy(&oldjb, &pr_parse_abort, sizeof(oldjb)); - - if( setjmp( pr_parse_abort ) ) { - // dont count it as error - } else { - //clock up the first line - QCC_PR_NewLine (false); - - QCC_PR_Lex (); // read first token - } - - while (pr_token_type != tt_eof) - { - if (setjmp(pr_parse_abort)) - { - if (++pr_error_count > MAX_ERRORS) - { - memcpy(&pr_parse_abort, &oldjb, sizeof(oldjb)); - return false; - } - num_continues = 0; - num_breaks = 0; - num_cases = 0; - QCC_PR_SkipToSemicolon (); - if (pr_token_type == tt_eof) - { - memcpy(&pr_parse_abort, &oldjb, sizeof(oldjb)); - return false; - } - } - - pr_scope = NULL; // outside all functions - - QCC_PR_ParseDefs (NULL); - } - memcpy(&pr_parse_abort, &oldjb, sizeof(oldjb)); - - return (pr_error_count == 0); -} - -pbool QCC_Include(char *filename) -{ - char *newfile; - char fname[512]; - char *opr_file_p; - QCC_string_t os_file, os_file2; - int opr_source_line; - char *ocompilingfile; - struct qcc_includechunk_s *oldcurrentchunk; - - ocompilingfile = compilingfile; - os_file = s_file; - os_file2 = s_file2; - opr_source_line = pr_source_line; - opr_file_p = pr_file_p; - oldcurrentchunk = currentchunk; - - strcpy(fname, filename); - QCC_LoadFile(fname, (void*)&newfile); - currentchunk = NULL; - pr_file_p = newfile; - QCC_PR_CompileFile(newfile, fname); - currentchunk = oldcurrentchunk; - - compilingfile = ocompilingfile; - s_file = os_file; - s_file2 = os_file2; - pr_source_line = opr_source_line; - pr_file_p = opr_file_p; - -// QCC_PR_IncludeChunk(newfile, false, fname); - - return true; -} - -#endif diff --git a/misc/source/fteqcc-src/qcc_pr_lex.c b/misc/source/fteqcc-src/qcc_pr_lex.c deleted file mode 100644 index 7a4319ba..00000000 --- a/misc/source/fteqcc-src/qcc_pr_lex.c +++ /dev/null @@ -1,3661 +0,0 @@ -#ifndef MINIMAL - -#include "qcc.h" -#ifdef QCC -#define print printf -#endif -#include "time.h" - -#ifdef _WIN64 - #ifdef _SDL - #define snprintf linuxlike_snprintf - int VARGS linuxlike_snprintf(char *buffer, int size, const char *format, ...) LIKEPRINTF(3); - #define vsnprintf linuxlike_vsnprintf - int VARGS linuxlike_vsnprintf(char *buffer, int size, const char *format, va_list argptr); - //void *__imp__vsnprintf = vsnprintf; - #endif -#endif - -#define MEMBERFIELDNAME "__m%s" - -#define STRCMP(s1,s2) (((*s1)!=(*s2)) || strcmp(s1+1,s2+1)) //saves about 2-6 out of 120 - expansion of idea from fastqcc - -void QCC_PR_ConditionCompilation(void); -pbool QCC_PR_UndefineName(char *name); -char *QCC_PR_CheakCompConstString(char *def); -CompilerConstant_t *QCC_PR_CheckCompConstDefined(char *def); -pbool QCC_Include(char *filename); - -char *compilingfile; - -int pr_source_line; - -char *pr_file_p; -char *pr_line_start; // start of current source line - -int pr_bracelevel; - -char pr_token[8192]; -token_type_t pr_token_type; -QCC_type_t *pr_immediate_type; -QCC_eval_t pr_immediate; - -char pr_immediate_string[8192]; - -int pr_error_count; -int pr_warning_count; - - -CompilerConstant_t *CompilerConstant; -int numCompilerConstants; -extern pbool expandedemptymacro; - - - -char *pr_punctuation[] = -// longer symbols must be before a shorter partial match -{"&&", "||", "<=", ">=","==", "!=", "/=", "*=", "+=", "-=", "(+)", "(-)", "|=", "&~=", "++", "--", "->", "::", ";", ",", "!", "*", "/", "(", ")", "-", "+", "=", "[", "]", "{", "}", "...", "..", ".", "<<", "<", ">>", ">" , "?", "#" , "@", "&" , "|", "^", ":", NULL}; - -char *pr_punctuationremap[] = //a nice bit of evilness. -//(+) -> |= -//-> -> . -//(-) -> &~= -{"&&", "||", "<=", ">=","==", "!=", "/=", "*=", "+=", "-=", "|=", "&~=", "|=", "&~=", "++", "--", ".", "::", ";", ",", "!", "*", "/", "(", ")", "-", "+", "=", "[", "]", "{", "}", "...", "..", ".", "<<", "<", ">>", ">" , "?", "#" , "@", "&" , "|", "^", ":", NULL}; - -// simple types. function types are dynamically allocated -QCC_type_t *type_void;// = {ev_void/*, &def_void*/}; -QCC_type_t *type_string;// = {ev_string/*, &def_string*/}; -QCC_type_t *type_float;// = {ev_float/*, &def_float*/}; -QCC_type_t *type_vector;// = {ev_vector/*, &def_vector*/}; -QCC_type_t *type_entity;// = {ev_entity/*, &def_entity*/}; -QCC_type_t *type_field;// = {ev_field/*, &def_field*/}; -QCC_type_t *type_function;// = {ev_function/*, &def_function*/,NULL,&type_void}; -// type_function is a void() function used for state defs -QCC_type_t *type_pointer;// = {ev_pointer/*, &def_pointer*/}; -QCC_type_t *type_integer;// = {ev_integer/*, &def_integer*/}; -QCC_type_t *type_variant;// = {ev_integer/*, &def_integer*/}; -QCC_type_t *type_floatpointer; -QCC_type_t *type_intpointer; - -QCC_type_t *type_floatfield;// = {ev_field/*, &def_field*/, NULL, &type_float}; - -/*QCC_def_t def_void = {type_void, "temp"}; -QCC_def_t def_string = {type_string, "temp"}; -QCC_def_t def_float = {type_float, "temp"}; -QCC_def_t def_vector = {type_vector, "temp"}; -QCC_def_t def_entity = {type_entity, "temp"}; -QCC_def_t def_field = {type_field, "temp"}; -QCC_def_t def_function = {type_function, "temp"}; -QCC_def_t def_pointer = {type_pointer, "temp"}; -QCC_def_t def_integer = {type_integer, "temp"}; -*/ -QCC_def_t def_ret, def_parms[MAX_PARMS]; - -//QCC_def_t *def_for_type[9] = {&def_void, &def_string, &def_float, &def_vector, &def_entity, &def_field, &def_function, &def_pointer, &def_integer}; - -void QCC_PR_LexWhitespace (void); - - - - -//for compiler constants and file includes. - -qcc_includechunk_t *currentchunk; -void QCC_PR_IncludeChunkEx (char *data, pbool duplicate, char *filename, CompilerConstant_t *cnst) -{ - qcc_includechunk_t *chunk = qccHunkAlloc(sizeof(qcc_includechunk_t)); - chunk->prev = currentchunk; - currentchunk = chunk; - - chunk->currentdatapoint = pr_file_p; - chunk->currentlinenumber = pr_source_line; - chunk->cnst = cnst; - if( cnst ) - { - cnst->inside++; - } - - if (duplicate) - { - pr_file_p = qccHunkAlloc(strlen(data)+1); - strcpy(pr_file_p, data); - } - else - pr_file_p = data; -} -void QCC_PR_IncludeChunk (char *data, pbool duplicate, char *filename) -{ - QCC_PR_IncludeChunkEx(data, duplicate, filename, NULL); -} - -pbool QCC_PR_UnInclude(void) -{ - if (!currentchunk) - return false; - - if( currentchunk->cnst ) - currentchunk->cnst->inside--; - - pr_file_p = currentchunk->currentdatapoint; - pr_source_line = currentchunk->currentlinenumber; - - currentchunk = currentchunk->prev; - - return true; -} - - -/* -============== -PR_PrintNextLine -============== -*/ -void QCC_PR_PrintNextLine (void) -{ - char *t; - - printf ("%3i:",pr_source_line); - for (t=pr_line_start ; *t && *t != '\n' ; t++) - printf ("%c",*t); - printf ("\n"); -} - -extern char qccmsourcedir[]; -//also meant to include it. -void QCC_FindBestInclude(char *newfile, char *currentfile, char *rootpath) -{ - char fullname[1024]; - int doubledots; - - char *end = fullname; - - if (!*newfile) - return; - - doubledots = 0; - /*count how far up we need to go*/ - while(!strncmp(newfile, "../", 3) || !strncmp(newfile, "..\\", 3)) - { - newfile+=3; - doubledots++; - } - - currentfile += strlen(rootpath); //could this be bad? - - strcpy(fullname, rootpath); - end = fullname+strlen(end); - if (*fullname && end[-1] != '/') - { - strcpy(end, "/"); - end = end+strlen(end); - } - strcpy(end, currentfile); - end = end+strlen(end); - - while (end > fullname) - { - end--; - /*stop at the slash, unless we're meant to go further*/ - if (*end == '/' || *end == '\\') - { - if (!doubledots) - { - end++; - break; - } - doubledots--; - } - } - - strcpy(end, newfile); - - QCC_Include(fullname); -} - -pbool defaultnoref; -pbool defaultstatic; -int ForcedCRC; -int QCC_PR_LexInteger (void); -void QCC_AddFile (char *filename); -void QCC_PR_LexString (void); -pbool QCC_PR_SimpleGetToken (void); - -int ParsePrecompilerIf(void) -{ - CompilerConstant_t *c; - int eval = 0; - pbool notted = false; - - /*skip whitespace*/ - while (*pr_file_p && *pr_file_p <= ' ' && *pr_file_p != '\n') - { - pr_file_p++; - } - if (*pr_file_p == '!') - { - pr_file_p++; - notted = true; - while (*pr_file_p && *pr_file_p <= ' ' && *pr_file_p != '\n') - { - pr_file_p++; - } - } - - if (!QCC_PR_SimpleGetToken()) - { - if (*pr_file_p == '(') - { - pr_file_p++; - eval = ParsePrecompilerIf(); - while (*pr_file_p == ' ' || *pr_file_p == '\t') - pr_file_p++; - if (*pr_file_p != ')') - QCC_PR_ParseError(ERR_EXPECTED, "unclosed bracket condition\n"); - pr_file_p++; - } - else - QCC_PR_ParseError(ERR_EXPECTED, "expected bracket or constant\n"); - } - else if (!strcmp(pr_token, "defined")) - { - while (*pr_file_p == ' ' || *pr_file_p == '\t') - pr_file_p++; - if (*pr_file_p != '(') - QCC_PR_ParseError(ERR_EXPECTED, "no opening bracket after defined\n"); - else - { - pr_file_p++; - - QCC_PR_SimpleGetToken(); - eval = !!QCC_PR_CheckCompConstDefined(pr_token); - - while (*pr_file_p == ' ' || *pr_file_p == '\t') - pr_file_p++; - if (*pr_file_p != ')') - QCC_PR_ParseError(ERR_EXPECTED, "unclosed defined condition\n"); - pr_file_p++; - } - } - else - { - c = QCC_PR_CheckCompConstDefined(pr_token); - if (!c) - eval = atoi(pr_token); - else - eval = atoi(c->value); - } - - if (notted) - eval = !eval; - - QCC_PR_SimpleGetToken(); - if (!strcmp(pr_token, "||")) - eval = ParsePrecompilerIf()||eval; - else if (!strcmp(pr_token, "&&")) - eval = ParsePrecompilerIf()&&eval; - else if (!strcmp(pr_token, "<=")) - eval = eval <= ParsePrecompilerIf(); - else if (!strcmp(pr_token, ">=")) - eval = eval >= ParsePrecompilerIf(); - else if (!strcmp(pr_token, "<")) - eval = eval < ParsePrecompilerIf(); - else if (!strcmp(pr_token, ">")) - eval = eval > ParsePrecompilerIf(); - else if (!strcmp(pr_token, "!=")) - eval = eval != ParsePrecompilerIf(); - - return eval; -} -/* -============== -QCC_PR_Precompiler -============== - -Runs precompiler stage -*/ -pbool QCC_PR_Precompiler(void) -{ - char msg[1024]; - int ifmode; - int a; - static int ifs = 0; - int level; //#if level - pbool eval = false; - - if (*pr_file_p == '#') - { - char *directive; - for (directive = pr_file_p+1; *directive; directive++) //so # define works - { - if (*directive == '\r' || *directive == '\n') - QCC_PR_ParseError(ERR_UNKNOWNPUCTUATION, "Hanging # with no directive\n"); - if (*directive > ' ') - break; - } - if (!strncmp(directive, "define", 6)) - { - pr_file_p = directive; - QCC_PR_ConditionCompilation(); - while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line - { - pr_file_p++; - } - } - else if (!strncmp(directive, "undef", 5)) - { - pr_file_p = directive+5; - while(*pr_file_p <= ' ') - pr_file_p++; - - QCC_PR_SimpleGetToken (); - QCC_PR_UndefineName(pr_token); - - // QCC_PR_ConditionCompilation(); - while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line - { - pr_file_p++; - } - } - else if (!strncmp(directive, "if", 2)) - { - int originalline = pr_source_line; - pr_file_p = directive+2; - if (!strncmp(pr_file_p, "def ", 4)) - { - ifmode = 0; - pr_file_p+=4; - } - else if (!strncmp(pr_file_p, "ndef ", 5)) - { - ifmode = 1; - pr_file_p+=5; - } - else - { - ifmode = 2; - pr_file_p+=0; - //QCC_PR_ParseError("bad \"#if\" type"); - } - - if (ifmode == 2) - { - eval = ParsePrecompilerIf(); - - if(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line - { - QCC_PR_ParseError (ERR_NOENDIF, "junk on the end of #if line"); - } - } - else - { - QCC_PR_SimpleGetToken (); - - // if (!STRCMP(pr_token, "COOP_MODE")) - // eval = false; - if (QCC_PR_CheckCompConstDefined(pr_token)) - eval = true; - - if (ifmode == 1) - eval = eval?false:true; - } - - while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line - { - pr_file_p++; - } - level = 1; - - if (eval) - ifs+=1; - else - { - while (1) - { - while(*pr_file_p && (*pr_file_p==' ' || *pr_file_p == '\t')) - pr_file_p++; - - if (!*pr_file_p) - { - pr_source_line = originalline; - QCC_PR_ParseError (ERR_NOENDIF, "#if with no endif"); - } - - if (*pr_file_p == '#') - { - pr_file_p++; - while(*pr_file_p==' ' || *pr_file_p == '\t') - pr_file_p++; - if (!strncmp(pr_file_p, "endif", 5)) - level--; - if (!strncmp(pr_file_p, "if", 2)) - level++; - if (!strncmp(pr_file_p, "else", 4) && level == 1) - { - ifs+=1; - while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line - { - pr_file_p++; - } - break; - } - } - - while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line - { - pr_file_p++; - } - if (level <= 0) - break; - pr_file_p++; //next line - pr_source_line++; - } - } - } - else if (!strncmp(directive, "else", 4)) - { - int originalline = pr_source_line; - - ifs -= 1; - level = 1; - - while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line - { - pr_file_p++; - } - while (1) - { - while(*pr_file_p && (*pr_file_p==' ' || *pr_file_p == '\t')) - pr_file_p++; - - if (!*pr_file_p) - { - pr_source_line = originalline; - QCC_PR_ParseError(ERR_NOENDIF, "#if with no endif"); - } - - if (*pr_file_p == '#') - { - pr_file_p++; - while(*pr_file_p==' ' || *pr_file_p == '\t') - pr_file_p++; - - if (!strncmp(pr_file_p, "endif", 5)) - level--; - if (!strncmp(pr_file_p, "if", 2)) - level++; - if (!strncmp(pr_file_p, "else", 4) && level == 1) - { - ifs+=1; - break; - } - } - - while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line - { - pr_file_p++; - } - if (level <= 0) - break; - pr_file_p++; //go off the end - pr_source_line++; - } - } - else if (!strncmp(directive, "endif", 5)) - { - while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line - { - pr_file_p++; - } - if (ifs <= 0) - QCC_PR_ParseError(ERR_NOPRECOMPILERIF, "unmatched #endif"); - else - ifs-=1; - } - else if (!strncmp(directive, "eof", 3)) - { - pr_file_p = NULL; - return true; - } - else if (!strncmp(directive, "error", 5)) - { - pr_file_p = directive+5; - for (a = 0; a < sizeof(msg)-1 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++) - msg[a] = pr_file_p[a]; - - msg[a] = '\0'; - - while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line, yes, I KNOW we are going to register an error, and not properly leave this function tree, but... - { - pr_file_p++; - } - - QCC_PR_ParseError(ERR_HASHERROR, "#Error: %s", msg); - } - else if (!strncmp(directive, "warning", 7)) - { - pr_file_p = directive+7; - for (a = 0; a < 1023 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++) - msg[a] = pr_file_p[a]; - - msg[a-1] = '\0'; - - while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line - { - pr_file_p++; - } - - QCC_PR_ParseWarning(WARN_PRECOMPILERMESSAGE, "#warning: %s", msg); - } - else if (!strncmp(directive, "message", 7)) - { - pr_file_p = directive+7; - for (a = 0; a < 1023 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++) - msg[a] = pr_file_p[a]; - - msg[a-1] = '\0'; - - while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line - { - pr_file_p++; - } - - printf("#message: %s\n", msg); - } - else if (!strncmp(directive, "copyright", 9)) - { - pr_file_p = directive+9; - for (a = 0; a < sizeof(msg)-1 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++) - msg[a] = pr_file_p[a]; - - msg[a-1] = '\0'; - - while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line - { - pr_file_p++; - } - - if (strlen(msg) >= sizeof(QCC_copyright)) - QCC_PR_ParseWarning(WARN_STRINGTOOLONG, "Copyright message is too long\n"); - strncpy(QCC_copyright, msg, sizeof(QCC_copyright)-1); - } - else if (!strncmp(directive, "pack", 4)) - { - ifmode = 0; - pr_file_p=directive+4; - if (!strncmp(pr_file_p, "id", 2)) - pr_file_p+=3; - else - { - ifmode = QCC_PR_LexInteger(); - if (ifmode == 0) - ifmode = 1; - pr_file_p++; - } - for (a = 0; a < sizeof(msg)-1 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++) - msg[a] = pr_file_p[a]; - - msg[a-1] = '\0'; - - while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line - { - pr_file_p++; - } - - if (ifmode == 0) - QCC_packid = atoi(msg); - else if (ifmode <= 5) - strcpy(QCC_Packname[ifmode-1], msg); - else - QCC_PR_ParseError(ERR_TOOMANYPACKFILES, "No more than 5 packs are allowed"); - } - else if (!strncmp(directive, "forcecrc", 8)) - { - pr_file_p=directive+8; - - ForcedCRC = QCC_PR_LexInteger(); - - pr_file_p++; - - for (a = 0; a < sizeof(msg)-1 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++) - msg[a] = pr_file_p[a]; - - msg[a-1] = '\0'; - - while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line - { - pr_file_p++; - } - } - else if (!strncmp(directive, "includelist", 11)) - { - pr_file_p=directive+11; - - while(*pr_file_p <= ' ') - pr_file_p++; - - while(1) - { - QCC_PR_LexWhitespace(); - if (!QCC_PR_SimpleGetToken()) - { - if (!*pr_file_p) - QCC_Error(ERR_EOF, "eof in includelist"); - else - { - pr_file_p++; - pr_source_line++; - } - continue; - } - if (!strcmp(pr_token, "#endlist")) - break; - - QCC_FindBestInclude(pr_token, compilingfile, qccmsourcedir); - - if (*pr_file_p == '\r') - pr_file_p++; - - while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line - { - pr_file_p++; - } - } - - while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line - { - pr_file_p++; - } - } - else if (!strncmp(directive, "include", 7)) - { - char sm; - - pr_file_p=directive+7; - - while(*pr_file_p <= ' ') - pr_file_p++; - - msg[0] = '\0'; - if (*pr_file_p == '\"') - sm = '\"'; - else if (*pr_file_p == '<') - sm = '>'; - else - { - QCC_PR_ParseError(0, "Not a string literal (on a #include)"); - sm = 0; - } - pr_file_p++; - a=0; - while(*pr_file_p != sm) - { - if (*pr_file_p == '\n') - { - QCC_PR_ParseError(0, "#include continued over line boundry\n"); - break; - } - msg[a++] = *pr_file_p; - pr_file_p++; - } - msg[a] = 0; - - QCC_FindBestInclude(msg, compilingfile, qccmsourcedir); - - pr_file_p++; - - while(*pr_file_p != '\n' && *pr_file_p != '\0' && *pr_file_p <= ' ') - pr_file_p++; - - - while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line - { - pr_file_p++; - } - } - else if (!strncmp(directive, "datafile", 8)) - { - pr_file_p=directive+8; - - while(*pr_file_p <= ' ') - pr_file_p++; - - QCC_PR_LexString(); - printf("Including datafile: %s\n", pr_token); - QCC_AddFile(pr_token); - - pr_file_p++; - - for (a = 0; a < sizeof(msg)-1 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++) - msg[a] = pr_file_p[a]; - - msg[a-1] = '\0'; - - while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line - { - pr_file_p++; - } - } - else if (!strncmp(directive, "output", 6)) - { - extern char destfile[1024]; - pr_file_p=directive+6; - - while(*pr_file_p <= ' ') - pr_file_p++; - - QCC_PR_LexString(); - strcpy(destfile, pr_token); - printf("Outputfile: %s\n", destfile); - - pr_file_p++; - - for (a = 0; a < sizeof(msg)-1 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++) - msg[a] = pr_file_p[a]; - - msg[a-1] = '\0'; - - while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line - { - pr_file_p++; - } - } - else if (!strncmp(directive, "pragma", 6)) - { - pr_file_p=directive+6; - while(*pr_file_p <= ' ') - pr_file_p++; - - qcc_token[0] = '\0'; - for(a = 0; *pr_file_p != '\n' && *pr_file_p != '\0'; pr_file_p++) //read on until the end of the line - { - if ((*pr_file_p == ' ' || *pr_file_p == '\t'|| *pr_file_p == '(') && !*qcc_token) - { - msg[a] = '\0'; - strcpy(qcc_token, msg); - a=0; - continue; - } - msg[a++] = *pr_file_p; - } - - msg[a] = '\0'; - { - char *end; - for (end = msg + a-1; end>=msg && *end <= ' '; end--) - *end = '\0'; - } - - if (!*qcc_token) - { - strcpy(qcc_token, msg); - msg[0] = '\0'; - } - - { - char *end; - for (end = msg + a-1; end>=msg && *end <= ' '; end--) - *end = '\0'; - } - - if (!QC_strcasecmp(qcc_token, "DONT_COMPILE_THIS_FILE")) - { - while (*pr_file_p) - { - while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line - pr_file_p++; - - if (*pr_file_p == '\n') - { - pr_file_p++; - QCC_PR_NewLine(false); - } - } - } - else if (!QC_strcasecmp(qcc_token, "COPYRIGHT")) - { - if (strlen(msg) >= sizeof(QCC_copyright)) - QCC_PR_ParseWarning(WARN_STRINGTOOLONG, "Copyright message is too long\n"); - strncpy(QCC_copyright, msg, sizeof(QCC_copyright)-1); - } - else if (!strncmp(qcc_token, "compress", 8)) - { - extern pbool compressoutput; - compressoutput = atoi(msg); - } - else if (!strncmp(qcc_token, "forcecrc", 8)) - { - ForcedCRC = atoi(msg); - } - else if (!strncmp(qcc_token, "noref", 8)) - { - defaultnoref = atoi(msg); - } - else if (!strncmp(qcc_token, "defaultstatic", 13)) - { - defaultstatic = atoi(msg); - } - else if (!strncmp(qcc_token, "wrasm", 5)) - { - pbool on = atoi(msg); - - if (asmfile && !on) - { - fclose(asmfile); - asmfile = NULL; - } - if (!asmfile && on) - asmfile = fopen("qc.asm", "wb"); - } - else if (!strncmp(qcc_token, "sourcefile", 10)) - { - #define MAXSOURCEFILESLIST 8 - extern char sourcefileslist[MAXSOURCEFILESLIST][1024]; - //extern int currentsourcefile; // warning: unused variable âcurrentsourcefileâ - extern int numsourcefiles; - - int i; - - QCC_COM_Parse(msg); - - for (i = 0; i < numsourcefiles; i++) - { - if (!strcmp(sourcefileslist[i], qcc_token)) - break; - } - if (i == numsourcefiles && numsourcefiles < MAXSOURCEFILESLIST) - strcpy(sourcefileslist[numsourcefiles++], qcc_token); - } - else if (!QC_strcasecmp(qcc_token, "TARGET")) - { - if (qcc_targetformat == QCF_HEXEN2 && numstatements) - QCC_PR_ParseWarning(WARN_BADTARGET, "Cannot switch from hexen2 target \'%s\'. Ignored.", msg); - else if (!QC_strcasecmp(msg, "H2") || !QC_strcasecmp(msg, "HEXEN2")) - { - if (numstatements) - QCC_PR_ParseWarning(WARN_BADTARGET, "Cannot switch to hexen2 target \'%s\'. Ignored.", msg); - else - qcc_targetformat = QCF_HEXEN2; - } - else if (!QC_strcasecmp(msg, "KK7")) - qcc_targetformat = QCF_KK7; - else if (!QC_strcasecmp(msg, "DP") || !QC_strcasecmp(msg, "DARKPLACES")) - qcc_targetformat = QCF_DARKPLACES; - else if (!QC_strcasecmp(msg, "FTEDEBUG")) - qcc_targetformat = QCF_FTEDEBUG; - else if (!QC_strcasecmp(msg, "FTE")) - qcc_targetformat = QCF_FTE; - else if (!QC_strcasecmp(msg, "STANDARD") || !QC_strcasecmp(msg, "ID")) - qcc_targetformat = QCF_STANDARD; - else if (!QC_strcasecmp(msg, "DEBUG")) - qcc_targetformat = QCF_FTEDEBUG; - else if (!QC_strcasecmp(msg, "QTEST")) - qcc_targetformat = QCF_QTEST; - else - QCC_PR_ParseWarning(WARN_BADTARGET, "Unknown target \'%s\'. Ignored.", msg); - } - else if (!QC_strcasecmp(qcc_token, "PROGS_SRC")) - { //doesn't make sence, but silenced if you are switching between using a certain precompiler app used with CuTF. - } - else if (!QC_strcasecmp(qcc_token, "PROGS_DAT")) - { //doesn't make sence, but silenced if you are switching between using a certain precompiler app used with CuTF. - extern char destfile[1024]; -#ifndef QCCONLY - extern char qccmfilename[1024]; - int p; - char *s, *s2; -#endif - QCC_COM_Parse(msg); - -#ifndef QCCONLY - p=0; - s2 = qcc_token; - if (!strncmp(s2, "./", 2)) - s2+=2; - else - { - while(!strncmp(s2, "../", 3)) - { - s2+=3; - p++; - } - } - strcpy(qccmfilename, qccmsourcedir); - for (s=qccmfilename+strlen(qccmfilename);p && s>=qccmfilename; s--) - { - if (*s == '/' || *s == '\\') - { - *(s+1) = '\0'; - p--; - } - } - sprintf(destfile, "%s", s2); - - while (p>0) - { - memmove(destfile+3, destfile, strlen(destfile)+1); - destfile[0] = '.'; - destfile[1] = '.'; - destfile[2] = '/'; - p--; - } -#else - - strcpy(destfile, qcc_token); -#endif - printf("Outputfile: %s\n", destfile); - } - else if (!QC_strcasecmp(qcc_token, "keyword") || !QC_strcasecmp(qcc_token, "flag")) - { - char *s; - int st; - s = QCC_COM_Parse(msg); - if (!QC_strcasecmp(qcc_token, "enable") || !QC_strcasecmp(qcc_token, "on")) - st = 1; - else if (!QC_strcasecmp(qcc_token, "disable") || !QC_strcasecmp(qcc_token, "off")) - st = 0; - else - { - QCC_PR_ParseWarning(WARN_BADPRAGMA, "compiler flag state not recognised"); - st = -1; - } - if (st < 0) - QCC_PR_ParseWarning(WARN_BADPRAGMA, "warning id not recognised"); - else - { - int f; - s = QCC_COM_Parse(s); - - for (f = 0; compiler_flag[f].enabled; f++) - { - if (!QC_strcasecmp(compiler_flag[f].abbrev, qcc_token)) - { - if (compiler_flag[f].flags & FLAG_MIDCOMPILE) - *compiler_flag[f].enabled = st; - else - QCC_PR_ParseWarning(WARN_BADPRAGMA, "Cannot enable/disable keyword/flag via a pragma"); - break; - } - } - if (!compiler_flag[f].enabled) - QCC_PR_ParseWarning(WARN_BADPRAGMA, "keyword/flag not recognised"); - - } - } - else if (!QC_strcasecmp(qcc_token, "warning")) - { - int st; - char *s; - s = QCC_COM_Parse(msg); - if (!stricmp(qcc_token, "enable") || !stricmp(qcc_token, "on")) - st = 0; - else if (!stricmp(qcc_token, "disable") || !stricmp(qcc_token, "off")) - st = 1; - else if (!stricmp(qcc_token, "toggle")) - st = 2; - else - { - QCC_PR_ParseWarning(WARN_BADPRAGMA, "warning state not recognised"); - st = -1; - } - if (st>=0) - { - int wn; - s = QCC_COM_Parse(s); - wn = QCC_WarningForName(qcc_token); - if (wn < 0) - QCC_PR_ParseWarning(WARN_BADPRAGMA, "warning id not recognised"); - else - { - if (st == 2) //toggle - qccwarningdisabled[wn] = true - qccwarningdisabled[wn]; - else - qccwarningdisabled[wn] = st; - } - } - } - else - QCC_PR_ParseWarning(WARN_BADPRAGMA, "Unknown pragma \'%s\'", qcc_token); - } - return true; - } - - return false; -} - -/* -============== -PR_NewLine - -Call at start of file and when *pr_file_p == '\n' -============== -*/ -void QCC_PR_NewLine (pbool incomment) -{ - pr_source_line++; - pr_line_start = pr_file_p; - while(*pr_file_p==' ' || *pr_file_p == '\t') - pr_file_p++; - if (incomment) //no constants if in a comment. - { - } - else if (QCC_PR_Precompiler()) - { - } - -// if (pr_dumpasm) -// PR_PrintNextLine (); -} - -/* -============== -PR_LexString - -Parses a quoted string -============== -*/ -#if 0 -void QCC_PR_LexString (void) -{ - int c; - int len; - char tmpbuf[2048]; - - char *text; - char *oldf; - int oldline; - - bool fromfile = true; - - len = 0; - - text = pr_file_p; - do - { - QCC_COM_Parse(text); -// print("Next token is \"%s\"\n", com_token); - if (*text == '\"') - { - text++; - if (fromfile) pr_file_p++; - } - do - { - c = *text++; - if (fromfile) pr_file_p++; - if (!c) - QCC_PR_ParseError ("EOF inside quote"); - if (c=='\n') - QCC_PR_ParseError ("newline inside quote"); - if (c=='\\') - { // escape char - c = *text++; - if (fromfile) pr_file_p++; - if (!c) - QCC_PR_ParseError ("EOF inside quote"); - if (c == 'n') - c = '\n'; - else if (c == '"') - c = '"'; - else if (c == '\\') - c = '\\'; - else - QCC_PR_ParseError ("Unknown escape char"); - } - else if (c=='\"') - { - if (fromfile) pr_file_p++; - break; - } - tmpbuf[len] = c; - len++; - } while (1); - tmpbuf[len] = 0; -// if (fromfile) pr_file_p++; - - pr_immediate_type=NULL; - oldline=pr_source_line; - oldf=pr_file_p; - QCC_PR_Lex(); - if (pr_immediate_type == &type_string) - { -// print("Appending \"%s\" to \"%s\"\n", pr_immediate_string, tmpbuf); - strcat(tmpbuf, pr_immediate_string); - len+=strlen(pr_immediate_string); - } - else - { - pr_source_line = oldline; - pr_file_p = oldf-1; - QCC_PR_LexWhitespace(); - if (*pr_file_p != '\"') //annother string - break; - } - - QCC_PR_LexWhitespace(); - text = pr_file_p; - - } while (1); - - strcpy(pr_token, tmpbuf); - pr_token_type = tt_immediate; - pr_immediate_type = &type_string; - strcpy (pr_immediate_string, pr_token); - -// print("Found \"%s\"\n", pr_immediate_string); -} -#else -void QCC_PR_LexString (void) -{ - int c; - int len; - char *end, *cnst; - - int texttype=0; - - len = 0; - pr_file_p++; - do - { - c = *pr_file_p++; - if (!c) - QCC_PR_ParseError (ERR_EOF, "EOF inside quote"); - if (c=='\n') - QCC_PR_ParseError (ERR_INVALIDSTRINGIMMEDIATE, "newline inside quote"); - if (c=='\\') - { // escape char - c = *pr_file_p++; - if (!c) - QCC_PR_ParseError (ERR_EOF, "EOF inside quote"); - if (c == 'n') - c = '\n'; - else if (c == 'r') - c = '\r'; - else if (c == '"') - c = '"'; - else if (c == 't') - c = '\t'; - else if (c == 'a') - c = '\a'; - else if (c == 'v') - c = '\v'; - else if (c == 'f') - c = '\f'; - else if (c == 's' || c == 'b') - { - texttype ^= 128; - continue; - } - else if (c == '[') - c = 16; - else if (c == ']') - c = 17; - else if (c == '{') - { - int d; - c = 0; - while ((d = *pr_file_p++) != '}') - { - c = c * 10 + d - '0'; - if (d < '0' || d > '9' || c > 255) - QCC_PR_ParseError(ERR_BADCHARACTERCODE, "Bad character code"); - } - } - else if (c == '<') - c = 29; - else if (c == '-') - c = 30; - else if (c == '>') - c = 31; - else if (c == 'x' || c == 'X') - { - int d; - c = 0; - - d = (unsigned char)*pr_file_p++; - if (d >= '0' && d <= '9') - c += d - '0'; - else if (d >= 'A' && d <= 'F') - c += d - 'A' + 10; - else if (d >= 'a' && d <= 'f') - c += d - 'a' + 10; - else - QCC_PR_ParseError(ERR_BADCHARACTERCODE, "Bad character code"); - - c *= 16; - - d = (unsigned char)*pr_file_p++; - if (d >= '0' && d <= '9') - c += d - '0'; - else if (d >= 'A' && d <= 'F') - c += d - 'A' + 10; - else if (d >= 'a' && d <= 'f') - c += d - 'a' + 10; - else - QCC_PR_ParseError(ERR_BADCHARACTERCODE, "Bad character code"); - } - else if (c == '\\') - c = '\\'; - else if (c == '\'') - c = '\''; - else if (c >= '0' && c <= '9') - c = 18 + c - '0'; - else if (c == '\r') - { //sigh - c = *pr_file_p++; - if (c != '\n') - QCC_PR_ParseWarning(WARN_HANGINGSLASHR, "Hanging \\\\\r"); - pr_source_line++; - } - else if (c == '\n') - { //sigh - pr_source_line++; - } - else - QCC_PR_ParseError (ERR_INVALIDSTRINGIMMEDIATE, "Unknown escape char %c", c); - } - else if (c=='\"') - { - if (len >= sizeof(pr_immediate_string)-1) - QCC_Error(ERR_INVALIDSTRINGIMMEDIATE, "String length exceeds %i", sizeof(pr_immediate_string)-1); - - while(*pr_file_p && *pr_file_p <= ' ') - { - if (*pr_file_p == '\n') - { - pr_file_p++; - QCC_PR_NewLine(false); - } - else - pr_file_p++; - } - if (*pr_file_p == '\"') //have annother go - { - pr_file_p++; - continue; - } - pr_token[len] = 0; - pr_token_type = tt_immediate; - pr_immediate_type = type_string; - strcpy (pr_immediate_string, pr_token); - return; - } - else if (c == '#') - { - for (end = pr_file_p; ; end++) - { - if (*end <= ' ') - break; - - if (*end == ')' - || *end == '(' - || *end == '+' - || *end == '-' - || *end == '*' - || *end == '/' - || *end == '\\' - || *end == '|' - || *end == '&' - || *end == '=' - || *end == '^' - || *end == '~' - || *end == '[' - || *end == ']' - || *end == '\"' - || *end == '{' - || *end == '}' - || *end == ';' - || *end == ':' - || *end == ',' - || *end == '.' - || *end == '#') - break; - } - - c = *end; - *end = '\0'; - cnst = QCC_PR_CheakCompConstString(pr_file_p); - if (cnst==pr_file_p) - cnst=NULL; - *end = c; - c = '#'; //undo - if (cnst) - { - QCC_PR_ParseWarning(WARN_MACROINSTRING, "Macro expansion in string"); - - if (len+strlen(cnst) >= sizeof(pr_token)-1) - QCC_Error(ERR_INVALIDSTRINGIMMEDIATE, "String length exceeds %i", sizeof(pr_token)-1); - - strcpy(pr_token+len, cnst); - len+=strlen(cnst); - pr_file_p = end; - continue; - } - } - else if (c == 0x7C && flag_acc) //reacc support... reacc is strange. - c = '\n'; - else - c |= texttype; - - pr_token[len] = c; - len++; - if (len >= sizeof(pr_token)-1) - QCC_Error(ERR_INVALIDSTRINGIMMEDIATE, "String length exceeds %i", sizeof(pr_token)-1); - } while (1); -} -#endif - -/* -============== -PR_LexNumber -============== -*/ -int QCC_PR_LexInteger (void) -{ - int c; - int len; - - len = 0; - c = *pr_file_p; - if (pr_file_p[0] == '0' && pr_file_p[1] == 'x') - { - pr_token[0] = '0'; - pr_token[1] = 'x'; - len = 2; - c = *(pr_file_p+=2); - } - do - { - pr_token[len] = c; - len++; - pr_file_p++; - c = *pr_file_p; - } while ((c >= '0' && c<= '9') || (c == '.'&&pr_file_p[1]!='.') || (c>='a' && c <= 'f')); - pr_token[len] = 0; - return atoi (pr_token); -} - -void QCC_PR_LexNumber (void) -{ - int tokenlen = 0; - int num=0; - int base=0; - int c; - int sign=1; - if (*pr_file_p == '-') - { - sign=-1; - pr_file_p++; - - pr_token[tokenlen++] = '-'; - } - if (pr_file_p[0] == '0' && pr_file_p[1] == 'x') - { - pr_file_p+=2; - base = 16; - - pr_token[tokenlen++] = '0'; - pr_token[tokenlen++] = 'x'; - } - - pr_immediate_type = NULL; - //assume base 10 if not stated - if (!base) - base = 10; - - while((c = *pr_file_p)) - { - if (c >= '0' && c <= '9') - { - pr_token[tokenlen++] = c; - num*=base; - num += c-'0'; - } - else if (c >= 'a' && c <= 'f' && base > 10) - { - pr_token[tokenlen++] = c; - num*=base; - num += c -'a'+10; - } - else if (c >= 'A' && c <= 'F' && base > 10) - { - pr_token[tokenlen++] = c; - num*=base; - num += c -'A'+10; - } - else if (c == '.' && pr_file_p[1]!='.') - { - pr_token[tokenlen++] = c; - pr_file_p++; - pr_immediate_type = type_float; - while(1) - { - c = *pr_file_p; - if (c >= '0' && c <= '9') - { - pr_token[tokenlen++] = c; - } - else if (c == 'f') - { - pr_file_p++; - break; - } - else - { - break; - } - pr_file_p++; - } - pr_token[tokenlen++] = 0; - pr_immediate._float = (float)atof(pr_token); - return; - } - else if (c == 'f') - { - pr_token[tokenlen++] = c; - pr_token[tokenlen++] = 0; - pr_file_p++; - pr_immediate_type = type_float; - pr_immediate._float = num*sign; - return; - } - else if (c == 'i') - { - pr_token[tokenlen++] = c; - pr_token[tokenlen++] = 0; - pr_file_p++; - pr_immediate_type = type_integer; - pr_immediate._int = num*sign; - return; - } - else - break; - pr_file_p++; - } - pr_token[tokenlen++] = 0; - - if (!pr_immediate_type) - { - if (flag_assume_integer) - pr_immediate_type = type_integer; - else - pr_immediate_type = type_float; - } - - if (pr_immediate_type == type_integer) - { - pr_immediate_type = type_integer; - pr_immediate._int = num*sign; - } - else - { - pr_immediate_type = type_float; - // at this point, we know there's no . in it, so the NaN bug shouldn't happen - // and we cannot use atof on tokens like 0xabc, so use num*sign, it SHOULD be safe - //pr_immediate._float = atof(pr_token); - pr_immediate._float = (float)(num*sign); - } -} - - -float QCC_PR_LexFloat (void) -{ - int c; - int len; - - len = 0; - c = *pr_file_p; - do - { - pr_token[len] = c; - len++; - pr_file_p++; - c = *pr_file_p; - } while ((c >= '0' && c<= '9') || (c == '.'&&pr_file_p[1]!='.')); //only allow a . if the next isn't too... - if (*pr_file_p == 'f') - pr_file_p++; - pr_token[len] = 0; - return (float)atof (pr_token); -} - -/* -============== -PR_LexVector - -Parses a single quoted vector -============== -*/ -void QCC_PR_LexVector (void) -{ - int i; - - pr_file_p++; - - if (*pr_file_p == '\\') - {//extended character constant - pr_token_type = tt_immediate; - pr_immediate_type = type_float; - pr_file_p++; - switch(*pr_file_p) - { - case 'n': - pr_immediate._float = '\n'; - break; - case 'r': - pr_immediate._float = '\r'; - break; - case 't': - pr_immediate._float = '\t'; - break; - case '\'': - pr_immediate._float = '\''; - break; - case '\"': - pr_immediate._float = '\"'; - break; - case '\\': - pr_immediate._float = '\\'; - break; - default: - QCC_PR_ParseError (ERR_INVALIDVECTORIMMEDIATE, "Bad character constant"); - } - pr_file_p++; - if (*pr_file_p != '\'') - QCC_PR_ParseError (ERR_INVALIDVECTORIMMEDIATE, "Bad character constant"); - pr_file_p++; - return; - } - if (pr_file_p[1] == '\'') - {//character constant - pr_token_type = tt_immediate; - pr_immediate_type = type_float; - pr_immediate._float = pr_file_p[0]; - pr_file_p+=2; - return; - } - pr_token_type = tt_immediate; - pr_immediate_type = type_vector; - QCC_PR_LexWhitespace (); - for (i=0 ; i<3 ; i++) - { - pr_immediate.vector[i] = QCC_PR_LexFloat (); - QCC_PR_LexWhitespace (); - - if (*pr_file_p == '\'' && i == 1) - { - if (i < 2) - QCC_PR_ParseWarning (WARN_FTE_SPECIFIC, "Bad vector"); - - for (i++ ; i<3 ; i++) - pr_immediate.vector[i] = 0; - break; - } - } - if (*pr_file_p != '\'') - QCC_PR_ParseError (ERR_INVALIDVECTORIMMEDIATE, "Bad vector"); - pr_file_p++; -} - -/* -============== -PR_LexName - -Parses an identifier -============== -*/ -void QCC_PR_LexName (void) -{ - int c; - int len; - - len = 0; - c = *pr_file_p; - do - { - pr_token[len] = c; - len++; - pr_file_p++; - c = *pr_file_p; - } while ( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' - || (c >= '0' && c <= '9')); - - pr_token[len] = 0; - pr_token_type = tt_name; -} - -/* -============== -PR_LexPunctuation -============== -*/ -void QCC_PR_LexPunctuation (void) -{ - int i; - int len; - char *p; - - pr_token_type = tt_punct; - - for (i=0 ; (p = pr_punctuation[i]) != NULL ; i++) - { - len = strlen(p); - if (!strncmp(p, pr_file_p, len) ) - { - strcpy (pr_token, pr_punctuationremap[i]); - if (p[0] == '{') - pr_bracelevel++; - else if (p[0] == '}') - pr_bracelevel--; - pr_file_p += len; - return; - } - } - - QCC_PR_ParseError (ERR_UNKNOWNPUCTUATION, "Unknown punctuation"); -} - - -/* -============== -PR_LexWhitespace -============== -*/ -void QCC_PR_LexWhitespace (void) -{ - int c; - - while (1) - { - // skip whitespace - while ( (c = *pr_file_p) <= ' ') - { - if (c=='\n') - { - pr_file_p++; - QCC_PR_NewLine (false); - if (!pr_file_p) - return; - } - else - { - if (c == 0) - return; // end of file - pr_file_p++; - } - } - - // skip // comments - if (c=='/' && pr_file_p[1] == '/') - { - while (*pr_file_p && *pr_file_p != '\n') - pr_file_p++; - - if (*pr_file_p == '\n') - pr_file_p++; //don't break on eof. - QCC_PR_NewLine(false); - continue; - } - - // skip /* */ comments - if (c=='/' && pr_file_p[1] == '*') - { - pr_file_p+=2; - do - { - if (pr_file_p[0]=='\n') - { - QCC_PR_NewLine(true); - } - if (pr_file_p[1] == 0) - { - QCC_PR_ParseError(0, "EOF inside comment\n"); - pr_file_p++; - return; - } - pr_file_p++; - } while (pr_file_p[0] != '*' || pr_file_p[1] != '/'); - pr_file_p+=2; - continue; - } - - break; // a real character has been found - } -} - -//============================================================================ - -#define MAX_FRAMES 8192 -char pr_framemodelname[64]; -char pr_framemacros[MAX_FRAMES][64]; -int pr_framemacrovalue[MAX_FRAMES]; -int pr_nummacros, pr_oldmacros; -int pr_macrovalue; -int pr_savedmacro; - -void QCC_PR_ClearGrabMacros (void) -{ - pr_oldmacros = pr_nummacros; -// pr_nummacros = 0; - pr_macrovalue = 0; - pr_savedmacro = -1; -} - -int QCC_PR_FindMacro (char *name) -{ - int i; - - for (i=pr_nummacros-1 ; i>=0 ; i--) - { - if (!STRCMP (name, pr_framemacros[i])) - { - return pr_framemacrovalue[i]; - } - } - for (i=pr_nummacros-1 ; i>=0 ; i--) - { - if (!stricmp (name, pr_framemacros[i])) - { - QCC_PR_ParseWarning(WARN_CASEINSENSATIVEFRAMEMACRO, "Case insensative frame macro"); - return pr_framemacrovalue[i]; - } - } - return -1; -} - -void QCC_PR_ExpandMacro(void) -{ - int i = QCC_PR_FindMacro(pr_token); - - if (i < 0) - QCC_PR_ParseError (ERR_BADFRAMEMACRO, "Unknown frame macro $%s", pr_token); - - sprintf (pr_token,"%d", i); - pr_token_type = tt_immediate; - pr_immediate_type = type_float; - pr_immediate._float = (float)i; -} - -// just parses text, returning false if an eol is reached -pbool QCC_PR_SimpleGetToken (void) -{ - int c; - int i; - - pr_token[0] = 0; - -// skip whitespace - while ( (c = *pr_file_p) <= ' ') - { - if (c=='\n' || c == 0) - return false; - pr_file_p++; - } - if (pr_file_p[0] == '/') - { - if (pr_file_p[1] == '/') - { //comment alert - while(*pr_file_p && *pr_file_p != '\n') - pr_file_p++; - return false; - } - if (pr_file_p[1] == '*') - return false; - } - - i = 0; - while ( (c = *pr_file_p) > ' ' && c != ',' && c != ';' && c != ')' && c != '(' && c != ']') - { - pr_token[i] = c; - i++; - pr_file_p++; - } - pr_token[i] = 0; - return i!=0; -} - -pbool QCC_PR_LexMacroName(void) -{ - int c; - int i; - - pr_token[0] = 0; - -// skip whitespace - while ( (c = *pr_file_p) <= ' ') - { - if (c=='\n' || c == 0) - return false; - pr_file_p++; - } - if (pr_file_p[0] == '/') - { - if (pr_file_p[1] == '/') - { //comment alert - while(*pr_file_p && *pr_file_p != '\n') - pr_file_p++; - return false; - } - if (pr_file_p[1] == '*') - return false; - } - - i = 0; - while ( (c = *pr_file_p) > ' ' && c != '\n' && c != ',' && c != ';' && c != ')' && c != '(' && c != ']' && !(pr_file_p[0] == '.' && pr_file_p[1] == '.')) - { - pr_token[i] = c; - i++; - pr_file_p++; - } - pr_token[i] = 0; - return i!=0; -} - -void QCC_PR_MacroFrame(char *name, int value) -{ - int i; - for (i=pr_nummacros-1 ; i>=0 ; i--) - { - if (!STRCMP (name, pr_framemacros[i])) - { - pr_framemacrovalue[i] = value; - if (i>=pr_oldmacros) - QCC_PR_ParseWarning(WARN_DUPLICATEMACRO, "Duplicate macro defined (%s)", pr_token); - //else it's from an old file, and shouldn't be mentioned. - return; - } - } - - if (strlen(name)+1 > sizeof(pr_framemacros[0])) - QCC_PR_ParseWarning(ERR_TOOMANYFRAMEMACROS, "Name for frame macro %s is too long", name); - else - { - strcpy (pr_framemacros[pr_nummacros], name); - pr_framemacrovalue[pr_nummacros] = value; - pr_nummacros++; - if (pr_nummacros >= MAX_FRAMES) - QCC_PR_ParseError(ERR_TOOMANYFRAMEMACROS, "Too many frame macros defined"); - } -} - -void QCC_PR_ParseFrame (void) -{ - while (QCC_PR_LexMacroName ()) - { - QCC_PR_MacroFrame(pr_token, pr_macrovalue++); - } -} - -/* -============== -PR_LexGrab - -Deals with counting sequence numbers and replacing frame macros -============== -*/ -void QCC_PR_LexGrab (void) -{ - pr_file_p++; // skip the $ -// if (!QCC_PR_SimpleGetToken ()) -// QCC_PR_ParseError ("hanging $"); - if (*pr_file_p <= ' ') - QCC_PR_ParseError (ERR_BADFRAMEMACRO, "hanging $"); - QCC_PR_LexMacroName(); - if (!*pr_token) - QCC_PR_ParseError (ERR_BADFRAMEMACRO, "hanging $"); - -// check for $frame - if (!STRCMP (pr_token, "frame") || !STRCMP (pr_token, "framesave")) - { - QCC_PR_ParseFrame (); - QCC_PR_Lex (); - } -// ignore other known $commands - just for model/spritegen - else if (!STRCMP (pr_token, "cd") - || !STRCMP (pr_token, "origin") - || !STRCMP (pr_token, "base") - || !STRCMP (pr_token, "flags") - || !STRCMP (pr_token, "scale") - || !STRCMP (pr_token, "skin") ) - { // skip to end of line - while (QCC_PR_LexMacroName ()) - ; - QCC_PR_Lex (); - } - else if (!STRCMP (pr_token, "flush")) - { - QCC_PR_ClearGrabMacros(); - while (QCC_PR_LexMacroName ()) - ; - QCC_PR_Lex (); - } - else if (!STRCMP (pr_token, "framevalue")) - { - QCC_PR_LexMacroName (); - pr_macrovalue = atoi(pr_token); - - QCC_PR_Lex (); - } - else if (!STRCMP (pr_token, "framerestore")) - { - QCC_PR_LexMacroName (); - QCC_PR_ExpandMacro(); - pr_macrovalue = (int)pr_immediate._float; - - QCC_PR_Lex (); - } - else if (!STRCMP (pr_token, "modelname")) - { - int i; - QCC_PR_LexMacroName (); - - if (*pr_framemodelname) - QCC_PR_MacroFrame(pr_framemodelname, pr_macrovalue); - - strncpy(pr_framemodelname, pr_token, sizeof(pr_framemodelname)-1); - pr_framemodelname[sizeof(pr_framemodelname)-1] = '\0'; - - i = QCC_PR_FindMacro(pr_framemodelname); - if (i) - pr_macrovalue = i; - else - i = 0; - - QCC_PR_Lex (); - } -// look for a frame name macro - else - QCC_PR_ExpandMacro (); -} - -//=========================== -//compiler constants - dmw - -pbool QCC_PR_UndefineName(char *name) -{ -// int a; - CompilerConstant_t *c; - c = pHash_Get(&compconstantstable, name); - if (!c) - { - QCC_PR_ParseWarning(WARN_UNDEFNOTDEFINED, "Precompiler constant %s was not defined", name); - return false; - } - - Hash_Remove(&compconstantstable, name); - return true; -} - -CompilerConstant_t *QCC_PR_DefineName(char *name) -{ - int i; - CompilerConstant_t *cnst; - -// if (numCompilerConstants >= MAX_CONSTANTS) -// QCC_PR_ParseError("Too many compiler constants - %i >= %i", numCompilerConstants, MAX_CONSTANTS); - - if (strlen(name) >= MAXCONSTANTNAMELENGTH || !*name) - QCC_PR_ParseError(ERR_NAMETOOLONG, "Compiler constant name length is too long or short"); - - cnst = pHash_Get(&compconstantstable, name); - if (cnst) - { - QCC_PR_ParseWarning(WARN_DUPLICATEDEFINITION, "Duplicate definition for Precompiler constant %s", name); - Hash_Remove(&compconstantstable, name); - } - - cnst = qccHunkAlloc(sizeof(CompilerConstant_t)); - - cnst->used = false; - cnst->numparams = 0; - strcpy(cnst->name, name); - cnst->namelen = strlen(name); - cnst->value = cnst->name + strlen(cnst->name); - for (i = 0; i < MAXCONSTANTPARAMS; i++) - cnst->params[i][0] = '\0'; - - pHash_Add(&compconstantstable, cnst->name, cnst, qccHunkAlloc(sizeof(bucket_t))); - - return cnst; -} - -void QCC_PR_Undefine(void) -{ - QCC_PR_SimpleGetToken (); - - QCC_PR_UndefineName(pr_token); -// QCC_PR_ParseError("%s was not defined.", pr_token); -} - -void QCC_PR_ConditionCompilation(void) -{ - char *oldval; - char *d; - char *dbuf; - int dbuflen; - char *s; - int quote=false; - CompilerConstant_t *cnst; - - QCC_PR_SimpleGetToken (); - - if (!QCC_PR_SimpleGetToken ()) - QCC_PR_ParseError(ERR_NONAME, "No name defined for compiler constant"); - - cnst = pHash_Get(&compconstantstable, pr_token); - if (cnst) - { - oldval = cnst->value; - Hash_Remove(&compconstantstable, pr_token); - } - else - oldval = NULL; - - cnst = QCC_PR_DefineName(pr_token); - - if (*pr_file_p == '(') - { - s = pr_file_p+1; - while(*pr_file_p++) - { - if (*pr_file_p == ',') - { - if (cnst->numparams >= MAXCONSTANTPARAMS) - QCC_PR_ParseError(ERR_MACROTOOMANYPARMS, "May not have more than %i parameters to a macro", MAXCONSTANTPARAMS); - strncpy(cnst->params[cnst->numparams], s, pr_file_p-s); - cnst->params[cnst->numparams][pr_file_p-s] = '\0'; - cnst->numparams++; - pr_file_p++; - s = pr_file_p; - } - if (*pr_file_p == ')') - { - if (cnst->numparams >= MAXCONSTANTPARAMS) - QCC_PR_ParseError(ERR_MACROTOOMANYPARMS, "May not have more than %i parameters to a macro", MAXCONSTANTPARAMS); - strncpy(cnst->params[cnst->numparams], s, pr_file_p-s); - cnst->params[cnst->numparams][pr_file_p-s] = '\0'; - cnst->numparams++; - pr_file_p++; - break; - } - } - } - else cnst->numparams = -1; - - s = pr_file_p; - d = dbuf = NULL; - dbuflen = 0; - while(*s == ' ' || *s == '\t') - s++; - while(1) - { - if ((d - dbuf) + 2 >= dbuflen) - { - int len = d - dbuf; - dbuflen = (len+128) * 2; - dbuf = qccHunkAlloc(dbuflen); - memcpy(dbuf, d - len, len); - d = dbuf + len; - } - - if( *s == '\\' ) - { - // read over a newline if necessary - if( s[1] == '\n' || s[1] == '\r' ) - { - s++; - QCC_PR_NewLine(false); - *d++ = *s++; - if( s[-1] == '\r' && s[0] == '\n' ) - { - *d++ = *s++; - } - } - } - else if(*s == '\r' || *s == '\n' || *s == '\0') - { - break; - } - - if (!quote && s[0]=='/'&&(s[1]=='/'||s[1]=='*')) - break; - if (*s == '\"') - quote=!quote; - - *d = *s; - d++; - s++; - } - *d = '\0'; - - cnst->value = dbuf; - - if (oldval) - { //we always warn if it was already defined - //we use different warning codes so that -Wno-mundane can be used to ignore identical redefinitions. - if (strcmp(oldval, cnst->value)) - QCC_PR_ParseWarning(WARN_DUPLICATEPRECOMPILER, "Alternate precompiler definition of %s", pr_token); - else - QCC_PR_ParseWarning(WARN_IDENTICALPRECOMPILER, "Identical precompiler definition of %s", pr_token); - } - - pr_file_p = s; -} - -/* *buffer, *bufferlen and *buffermax should be NULL/0 at the start */ -static void QCC_PR_ExpandStrCat(char **buffer, int *bufferlen, int *buffermax, char *newdata, int newlen) -{ - int newmax = *bufferlen + newlen; - if (newmax < *bufferlen) - { - QCC_PR_ParseWarning(ERR_INTERNAL, "out of memory"); - return; - } - if (newmax > *buffermax) - { - char *newbuf; - if (newmax < 64) - newmax = 64; - if (newmax < *bufferlen * 2) - { - newmax = *bufferlen * 2; - if (newmax < *bufferlen) /*overflowed?*/ - { - QCC_PR_ParseWarning(ERR_INTERNAL, "out of memory"); - return; - } - } - newbuf = realloc(*buffer, newmax); - if (!newbuf) - { - QCC_PR_ParseWarning(ERR_INTERNAL, "out of memory"); - return; /*OOM*/ - } - *buffer = newbuf; - *buffermax = newmax; - } - memcpy(*buffer + *bufferlen, newdata, newlen); - *bufferlen += newlen; - /*no null terminator, remember to cat one if required*/ -} - -int QCC_PR_CheakCompConst(void) -{ - char *oldpr_file_p = pr_file_p; - int whitestart; - - CompilerConstant_t *c; - - char *end; - for (end = pr_file_p; ; end++) - { - if (*end <= ' ') - break; - - if (*end == ')' - || *end == '(' - || *end == '+' - || *end == '-' - || *end == '*' - || *end == '/' - || *end == '|' - || *end == '&' - || *end == '=' - || *end == '^' - || *end == '~' - || *end == '[' - || *end == ']' - || *end == '\"' - || *end == '{' - || *end == '}' - || *end == ';' - || *end == ':' - || *end == ',' - || *end == '.' - || *end == '#') - break; - } - strncpy(pr_token, pr_file_p, end-pr_file_p); - pr_token[end-pr_file_p]='\0'; - -// printf("%s\n", pr_token); - c = pHash_Get(&compconstantstable, pr_token); - - if (c && !c->inside) - { - pr_file_p = oldpr_file_p+strlen(c->name); - while(*pr_file_p == ' ' || *pr_file_p == '\t') - pr_file_p++; - if (c->numparams>=0) - { - if (*pr_file_p == '(') - { - int p; - char *start; - char *starttok; - char *buffer; - int buffermax; - int bufferlen; - char *paramoffset[MAXCONSTANTPARAMS+1]; - int param=0; - int plevel=0; - - pr_file_p++; - while(*pr_file_p == ' ' || *pr_file_p == '\t') - pr_file_p++; - start = pr_file_p; - while(1) - { - // handle strings correctly by ignoring them - if (*pr_file_p == '\"') - { - do { - pr_file_p++; - } while( (pr_file_p[-1] == '\\' || pr_file_p[0] != '\"') && *pr_file_p && *pr_file_p != '\n' ); - } - if (*pr_file_p == '(') - plevel++; - else if (!plevel && (*pr_file_p == ',' || *pr_file_p == ')')) - { - paramoffset[param++] = start; - start = pr_file_p+1; - if (*pr_file_p == ')') - { - *pr_file_p = '\0'; - pr_file_p++; - break; - } - *pr_file_p = '\0'; - pr_file_p++; - while(*pr_file_p == ' ' || *pr_file_p == '\t') - { - pr_file_p++; - start++; - } - // move back by one char because we move forward by one at the end of the loop - pr_file_p--; - if (param == MAXCONSTANTPARAMS) - QCC_PR_ParseError(ERR_TOOMANYPARAMS, "Too many parameters in macro call"); - } else if (*pr_file_p == ')' ) - plevel--; - else if(*pr_file_p == '\n') - QCC_PR_NewLine(false); - - // see that *pr_file_p = '\0' up there? Must ++ BEFORE checking for !*pr_file_p - pr_file_p++; - if (!*pr_file_p) - QCC_PR_ParseError(ERR_EOF, "EOF on macro call"); - } - if (param < c->numparams) - QCC_PR_ParseError(ERR_TOOFEWPARAMS, "Not enough macro parameters"); - paramoffset[param] = start; - - buffer = NULL; - bufferlen = 0; - buffermax = 0; - - oldpr_file_p = pr_file_p; - pr_file_p = c->value; - for(;;) - { - whitestart = bufferlen; - starttok = pr_file_p; - while(*pr_file_p <= ' ') //copy across whitespace - { - if (!*pr_file_p) - break; - pr_file_p++; - } - if (starttok != pr_file_p) - { - QCC_PR_ExpandStrCat(&buffer, &bufferlen, &buffermax, starttok, pr_file_p - starttok); - } - - if(*pr_file_p == '\"') - { - starttok = pr_file_p; - do - { - pr_file_p++; - } while( (pr_file_p[-1] == '\\' || pr_file_p[0] != '\"') && *pr_file_p && *pr_file_p != '\n' ); - if(*pr_file_p == '\"') - pr_file_p++; - - QCC_PR_ExpandStrCat(&buffer, &bufferlen, &buffermax, starttok, pr_file_p - starttok); - continue; - } - else if (*pr_file_p == '#') //if you ask for #a##b you will be shot. use #a #b instead, or chain macros. - { - if (pr_file_p[1] == '#') - { //concatinate (strip out whitespace before the token) - bufferlen = whitestart; - pr_file_p+=2; - } - else - { //stringify - pr_file_p++; - pr_file_p = QCC_COM_Parse2(pr_file_p); - if (!pr_file_p) - break; - - for (p = 0; p < param; p++) - { - if (!STRCMP(qcc_token, c->params[p])) - { - QCC_PR_ExpandStrCat(&buffer, &bufferlen, &buffermax, "\"", 1); - QCC_PR_ExpandStrCat(&buffer, &bufferlen, &buffermax, paramoffset[p], strlen(paramoffset[p])); - QCC_PR_ExpandStrCat(&buffer, &bufferlen, &buffermax, "\"", 1); - break; - } - } - if (p == param) - { - QCC_PR_ExpandStrCat(&buffer, &bufferlen, &buffermax, "#", 1); - QCC_PR_ExpandStrCat(&buffer, &bufferlen, &buffermax, qcc_token, strlen(qcc_token)); - //QCC_PR_ParseWarning(0, "Stringification ignored"); - } - continue; //already did this one - } - } - - pr_file_p = QCC_COM_Parse2(pr_file_p); - if (!pr_file_p) - break; - - for (p = 0; p < param; p++) - { - if (!STRCMP(qcc_token, c->params[p])) - { - QCC_PR_ExpandStrCat(&buffer, &bufferlen, &buffermax, paramoffset[p], strlen(paramoffset[p])); - break; - } - } - if (p == param) - QCC_PR_ExpandStrCat(&buffer, &bufferlen, &buffermax, qcc_token, strlen(qcc_token)); - } - - for (p = 0; p < param-1; p++) - paramoffset[p][strlen(paramoffset[p])] = ','; - paramoffset[p][strlen(paramoffset[p])] = ')'; - - pr_file_p = oldpr_file_p; - if (!bufferlen) - expandedemptymacro = true; - else - { - QCC_PR_ExpandStrCat(&buffer, &bufferlen, &buffermax, "\0", 1); - QCC_PR_IncludeChunkEx(buffer, true, NULL, c); - } - free(buffer); - } - else - QCC_PR_ParseError(ERR_TOOFEWPARAMS, "Macro without argument list"); - } - else - { - if (!*c->value) - expandedemptymacro = true; - QCC_PR_IncludeChunkEx(c->value, false, NULL, c); - } - - QCC_PR_Lex(); - return true; - } - - if (!strncmp(pr_file_p, "__TIME__", 8)) - { - static char retbuf[128]; - - time_t long_time; - time( &long_time ); - strftime( retbuf, sizeof(retbuf), - "\"%H:%M\"", localtime( &long_time )); - - pr_file_p = retbuf; - QCC_PR_Lex(); //translate the macro's value - pr_file_p = oldpr_file_p+8; - - return true; - } - if (!strncmp(pr_file_p, "__DATE__", 8)) - { - static char retbuf[128]; - - time_t long_time; - time( &long_time ); - strftime( retbuf, sizeof(retbuf), - "\"%a %d %b %Y\"", localtime( &long_time )); - - pr_file_p = retbuf; - QCC_PR_Lex(); //translate the macro's value - pr_file_p = oldpr_file_p+8; - - return true; - } - if (!strncmp(pr_file_p, "__FILE__", 8)) - { - static char retbuf[256]; - sprintf(retbuf, "\"%s\"", strings + s_file); - pr_file_p = retbuf; - QCC_PR_Lex(); //translate the macro's value - pr_file_p = oldpr_file_p+8; - - return true; - } - if (!strncmp(pr_file_p, "__LINE__", 8)) - { - static char retbuf[256]; - sprintf(retbuf, "\"%i\"", pr_source_line); - pr_file_p = retbuf; - QCC_PR_Lex(); //translate the macro's value - pr_file_p = oldpr_file_p+8; - return true; - } - if (!strncmp(pr_file_p, "__FUNC__", 8)) - { - static char retbuf[256]; - sprintf(retbuf, "\"%s\"",pr_scope->name); - pr_file_p = retbuf; - QCC_PR_Lex(); //translate the macro's value - pr_file_p = oldpr_file_p+8; - return true; - } - if (!strncmp(pr_file_p, "__NULL__", 8)) - { - static char retbuf[256]; - sprintf(retbuf, "0i"); - pr_file_p = retbuf; - QCC_PR_Lex(); //translate the macro's value - pr_file_p = oldpr_file_p+8; - return true; - } - return false; -} - -char *QCC_PR_CheakCompConstString(char *def) -{ - char *s; - - CompilerConstant_t *c; - - c = pHash_Get(&compconstantstable, def); - - if (c) - { - s = QCC_PR_CheakCompConstString(c->value); - return s; - } - return def; -} - -CompilerConstant_t *QCC_PR_CheckCompConstDefined(char *def) -{ - CompilerConstant_t *c = pHash_Get(&compconstantstable, def); - return c; -} - -//============================================================================ - -/* -============== -PR_Lex - -Sets pr_token, pr_token_type, and possibly pr_immediate and pr_immediate_type -============== -*/ -void QCC_PR_Lex (void) -{ - int c; - - pr_token[0] = 0; - - if (!pr_file_p) - { - if (QCC_PR_UnInclude()) - { - QCC_PR_Lex(); - return; - } - pr_token_type = tt_eof; - return; - } - - QCC_PR_LexWhitespace (); - - if (!pr_file_p) - { - if (QCC_PR_UnInclude()) - { - QCC_PR_Lex(); - return; - } - pr_token_type = tt_eof; - return; - } - - c = *pr_file_p; - - if (!c) - { - if (QCC_PR_UnInclude()) - { - QCC_PR_Lex(); - return; - } - pr_token_type = tt_eof; - return; - } - -// handle quoted strings as a unit - if (c == '\"') - { - QCC_PR_LexString (); - return; - } - -// handle quoted vectors as a unit - if (c == '\'') - { - QCC_PR_LexVector (); - return; - } - -// if the first character is a valid identifier, parse until a non-id -// character is reached - if ( c == '~' || c == '%') //let's see which one we make into an operator first... possibly both... - { - QCC_PR_ParseWarning(0, "~ or %% prefixes to denote integers are deprecated. Please use a postfix of 'i'"); - pr_file_p++; - pr_token_type = tt_immediate; - pr_immediate_type = type_integer; - pr_immediate._int = QCC_PR_LexInteger (); - return; - } - if ( c == '0' && pr_file_p[1] == 'x') - { - pr_token_type = tt_immediate; - QCC_PR_LexNumber(); - return; - } - if ( (c == '.'&&pr_file_p[1]!='.'&&pr_file_p[1] >='0' && pr_file_p[1] <= '9') || (c >= '0' && c <= '9') || ( c=='-' && pr_file_p[1]>='0' && pr_file_p[1] <='9') ) - { - pr_token_type = tt_immediate; - QCC_PR_LexNumber (); - return; - } - - if (c == '#' && !(pr_file_p[1]=='-' || (pr_file_p[1]>='0' && pr_file_p[1] <='9'))) //hash and not number - { - pr_file_p++; - if (!QCC_PR_CheakCompConst()) - { - if (!QCC_PR_SimpleGetToken()) - strcpy(pr_token, "unknown"); - QCC_PR_ParseError(ERR_CONSTANTNOTDEFINED, "Explicit precompiler usage when not defined %s", pr_token); - } - else - if (pr_token_type == tt_eof) - QCC_PR_Lex(); - - return; - } - - if ( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' ) - { - if (flag_hashonly || !QCC_PR_CheakCompConst()) //look for a macro. - QCC_PR_LexName (); - else - if (pr_token_type == tt_eof) - { - if (QCC_PR_UnInclude()) - { - QCC_PR_Lex(); - return; - } - pr_token_type = tt_eof; - } - return; - } - - if (c == '$') - { - QCC_PR_LexGrab (); - return; - } - -// parse symbol strings until a non-symbol is found - QCC_PR_LexPunctuation (); -} - -//============================================================================= - - -void QCC_PR_ParsePrintDef (int type, QCC_def_t *def) -{ - if (qccwarningdisabled[type]) - return; - if (def->s_file) - { - if (flag_msvcstyle) - printf ("%s(%i) : %s is defined here\n", strings + def->s_file, def->s_line, def->name); - else - printf ("%s:%i: %s is defined here\n", strings + def->s_file, def->s_line, def->name); - } -} -void *errorscope; -void QCC_PR_PrintScope (void) -{ - if (pr_scope) - { - if (errorscope != pr_scope) - printf ("in function %s (line %i),\n", pr_scope->name, pr_scope->s_line); - errorscope = pr_scope; - } - else - { - if (errorscope) - printf ("at global scope,\n"); - errorscope = NULL; - } -} -void QCC_PR_ResetErrorScope(void) -{ - errorscope = NULL; -} -/* -============ -PR_ParseError - -Aborts the current file load -============ -*/ -#ifndef QCC -void editbadfile(char *file, int line); -#endif -void VARGS QCC_PR_ParseError (int errortype, char *error, ...) -{ - va_list argptr; - char string[1024]; - - va_start (argptr,error); - QC_vsnprintf (string,sizeof(string)-1, error,argptr); - va_end (argptr); - -#ifndef QCC - editbadfile(strings+s_file, pr_source_line); -#endif - - QCC_PR_PrintScope(); - if (flag_msvcstyle) - printf ("%s(%i) : error: %s\n", strings + s_file, pr_source_line, string); - else - printf ("%s:%i: error: %s\n", strings + s_file, pr_source_line, string); - - longjmp (pr_parse_abort, 1); -} -void VARGS QCC_PR_ParseErrorPrintDef (int errortype, QCC_def_t *def, char *error, ...) -{ - va_list argptr; - char string[1024]; - - va_start (argptr,error); - QC_vsnprintf (string,sizeof(string)-1, error,argptr); - va_end (argptr); - -#ifndef QCC - editbadfile(strings+s_file, pr_source_line); -#endif - QCC_PR_PrintScope(); - if (flag_msvcstyle) - printf ("%s(%i) : error: %s\n", strings + s_file, pr_source_line, string); - else - printf ("%s:%i: error: %s\n", strings + s_file, pr_source_line, string); - - QCC_PR_ParsePrintDef(WARN_ERROR, def); - - longjmp (pr_parse_abort, 1); -} -void VARGS QCC_PR_ParseWarning (int type, char *error, ...) -{ - va_list argptr; - char string[1024]; - - if (type < ERR_PARSEERRORS && qccwarningdisabled[type]) - return; - - va_start (argptr,error); - QC_vsnprintf (string,sizeof(string)-1, error,argptr); - va_end (argptr); - - QCC_PR_PrintScope(); - if (type >= ERR_PARSEERRORS) - { - if (flag_msvcstyle) - printf ("%s(%i) : error: %s\n", strings + s_file, pr_source_line, string); - else - printf ("%s:%i: error: %s\n", strings + s_file, pr_source_line, string); - pr_error_count++; - } - else - { - if (flag_msvcstyle) - printf ("%s(%i) : warning: %s\n", strings + s_file, pr_source_line, string); - else - printf ("%s:%i: warning: %s\n", strings + s_file, pr_source_line, string); - pr_warning_count++; - } -} - -void VARGS QCC_PR_Note (int type, char *file, int line, char *error, ...) -{ - va_list argptr; - char string[1024]; - - if (qccwarningdisabled[type]) - return; - - va_start (argptr,error); - QC_vsnprintf (string,sizeof(string)-1, error,argptr); - va_end (argptr); - - QCC_PR_PrintScope(); - if (file) - { - if (flag_msvcstyle) - printf ("%s(%i) : note: %s\n", file, line, string); - else - printf ("%s:%i: note: %s\n", file, line, string); - } - else - printf ("note: %s\n", string); -} - -pbool VARGS QCC_PR_Warning (int type, char *file, int line, char *error, ...) -{ - va_list argptr; - char string[1024]; - - if (qccwarningdisabled[type]) - return false; - - va_start (argptr,error); - QC_vsnprintf (string,sizeof(string)-1, error,argptr); - va_end (argptr); - - QCC_PR_PrintScope(); - if (file) - { - if (flag_msvcstyle) - printf ("%s(%i) : warning: %s\n", file, line, string); - else - printf ("%s:%i: warning: %s\n", file, line, string); - } - else - printf ("warning: %s\n", string); - pr_warning_count++; - - return true; -} - - -/* -============= -PR_Expect - -Issues an error if the current token isn't equal to string -Gets the next token -============= -*/ -#ifndef COMMONINLINES -void QCC_PR_Expect (char *string) -{ - if (STRCMP (string, pr_token)) - QCC_PR_ParseError (ERR_EXPECTED, "expected %s, found %s",string, pr_token); - QCC_PR_Lex (); -} -#endif - - -/* -============= -PR_Check - -Returns true and gets the next token if the current token equals string -Returns false and does nothing otherwise -============= -*/ -#ifndef COMMONINLINES -pbool QCC_PR_CheckToken (char *string) -{ - if (pr_token_type != tt_punct) - return false; - - if (STRCMP (string, pr_token)) - return false; - - QCC_PR_Lex (); - return true; -} - -pbool QCC_PR_CheckImmediate (char *string) -{ - if (pr_token_type != tt_immediate) - return false; - - if (STRCMP (string, pr_token)) - return false; - - QCC_PR_Lex (); - return true; -} - -pbool QCC_PR_CheckName(char *string) -{ - if (pr_token_type != tt_name) - return false; - if (flag_caseinsensative) - { - if (stricmp (string, pr_token)) - return false; - } - else - { - if (STRCMP(string, pr_token)) - return false; - } - QCC_PR_Lex (); - return true; -} - -pbool QCC_PR_CheckKeyword(int keywordenabled, char *string) -{ - if (!keywordenabled) - return false; - if (flag_caseinsensative) - { - if (stricmp (string, pr_token)) - return false; - } - else - { - if (STRCMP(string, pr_token)) - return false; - } - QCC_PR_Lex (); - return true; -} -#endif - - -/* -============ -PR_ParseName - -Checks to see if the current token is a valid name -============ -*/ -char *QCC_PR_ParseName (void) -{ - static char ident[MAX_NAME]; - char *ret; - - if (pr_token_type != tt_name) - { - if (pr_token_type == tt_eof) - QCC_PR_ParseError (ERR_EOF, "unexpected EOF", pr_token); - else - QCC_PR_ParseError (ERR_NOTANAME, "\"%s\" - not a name", pr_token); - } - if (strlen(pr_token) >= MAX_NAME-1) - QCC_PR_ParseError (ERR_NAMETOOLONG, "name too long"); - strcpy (ident, pr_token); - QCC_PR_Lex (); - - ret = qccHunkAlloc(strlen(ident)+1); - strcpy(ret, ident); - return ret; -// return ident; -} - -/* -============ -PR_FindType - -Returns a preexisting complex type that matches the parm, or allocates -a new one and copies it out. -============ -*/ - -//0 if same -int typecmp(QCC_type_t *a, QCC_type_t *b) -{ - if (a == b) - return 0; - if (!a || !b) - return 1; //different (^ and not both null) - - if (a->type != b->type) - return 1; - if (a->num_parms != b->num_parms) - { - return 1; - } - - if (a->size != b->size) - return 1; -// if (STRCMP(a->name, b->name)) //This isn't 100% clean. -// return 1; - - if (typecmp(a->aux_type, b->aux_type)) - return 1; - - if (a->param || b->param) - { - a = a->param; - b = b->param; - - while(a || b) - { - if (typecmp(a, b)) - return 1; - - a=a->next; - b=b->next; - } - } - - return 0; -} - -QCC_type_t *QCC_PR_DuplicateType(QCC_type_t *in) -{ - QCC_type_t *out, *op, *ip; - if (!in) - return NULL; - - out = QCC_PR_NewType(in->name, in->type, false); - out->aux_type = QCC_PR_DuplicateType(in->aux_type); - out->param = QCC_PR_DuplicateType(in->param); - ip = in->param; - op = NULL; - while(ip) - { - if (!op) - out->param = op = QCC_PR_DuplicateType(ip); - else - op = (op->next = QCC_PR_DuplicateType(ip)); - ip = ip->next; - } - out->arraysize = in->arraysize; - out->size = in->size; - out->num_parms = in->num_parms; - out->ofs = in->ofs; - out->name = in->name; - out->parentclass = in->parentclass; - - return out; -} - -char *TypeName(QCC_type_t *type) -{ - static char buffer[2][512]; - static int op; - char *ret; - - - op++; - ret = buffer[op&1]; - if (type->type == ev_field) - { - type = type->aux_type; - *ret++ = '.'; - } - *ret = 0; - - if (type->type == ev_function) - { - pbool varg = type->num_parms < 0; - int args = type->num_parms; - if (args < 0) - args = -(args+1); - strcat(ret, type->aux_type->name); - strcat(ret, " ("); - type = type->param; - while(type) - { - if (args<=0) - strcat(ret, "optional "); - args--; - - strcat(ret, type->name); - type = type->next; - - if (type || varg) - strcat(ret, ", "); - } - if (varg) - { - strcat(ret, "..."); - } - strcat(ret, ")"); - } - else if (type->type == ev_entity && type->parentclass) - { - ret = buffer[op&1]; - *ret = 0; - strcat(ret, "class "); - strcat(ret, type->name); -/* strcat(ret, " {"); - type = type->param; - while(type) - { - strcat(ret, type->name); - type = type->next; - - if (type) - strcat(ret, ", "); - } - strcat(ret, "}"); -*/ - } - else - strcpy(ret, type->name); - - return buffer[op&1]; -} -//#define typecmp(a, b) (a && ((a)->type==(b)->type) && !STRCMP((a)->name, (b)->name)) - -QCC_type_t *QCC_PR_FindType (QCC_type_t *type) -{ - int t; - for (t = 0; t < numtypeinfos; t++) - { -// check = &qcc_typeinfo[t]; - if (typecmp(&qcc_typeinfo[t], type)) - continue; - - -// c2 = check->next; -// n2 = type->next; -// for (i=0 ; n2&&c2 ; i++) -// { -// if (!typecmp((c2), (n2))) -// break; -// c2=c2->next; -// n2=n2->next; -// } - -// if (n2==NULL&&c2==NULL) - { - return &qcc_typeinfo[t]; - } - } -QCC_Error(ERR_INTERNAL, "Error with type"); - - return type; -} -/* -QCC_type_t *QCC_PR_NextSubType(QCC_type_t *type, QCC_type_t *prev) -{ - int p; - if (!prev) - return type->next; - - for (p = prev->num_parms; p; p--) - prev = QCC_PR_NextSubType(prev, NULL); - if (prev->num_parms) - - switch(prev->type) - { - case ev_function: - - } - - return prev->next; -} -*/ - -QCC_type_t *QCC_TypeForName(char *name) -{ - int i; - - for (i = 0; i < numtypeinfos; i++) - { - if (!STRCMP(qcc_typeinfo[i].name, name)) - { - return &qcc_typeinfo[i]; - } - } - - return NULL; -} - -/* -============ -PR_SkipToSemicolon - -For error recovery, also pops out of nested braces -============ -*/ -void QCC_PR_SkipToSemicolon (void) -{ - do - { - if (!pr_bracelevel && QCC_PR_CheckToken (";")) - return; - QCC_PR_Lex (); - } while (pr_token_type != tt_eof); -} - - -/* -============ -PR_ParseType - -Parses a variable type, including field and functions types -============ -*/ -#ifdef MAX_EXTRA_PARMS -char pr_parm_names[MAX_PARMS+MAX_EXTRA_PARMS][MAX_NAME]; -#else -char pr_parm_names[MAX_PARMS][MAX_NAME]; -#endif - -pbool recursivefunctiontype; - -//expects a ( to have already been parsed. -QCC_type_t *QCC_PR_ParseFunctionType (int newtype, QCC_type_t *returntype) -{ - QCC_type_t *ftype, *ptype, *nptype; - char *name; - int definenames = !recursivefunctiontype; - int optional = 0; - int numparms = 0; - - recursivefunctiontype++; - - ftype = QCC_PR_NewType(type_function->name, ev_function, false); - - ftype->aux_type = returntype; // return type - ftype->num_parms = 0; - ptype = NULL; - - - if (!QCC_PR_CheckToken (")")) - { - if (QCC_PR_CheckToken ("...")) - ftype->num_parms = -1; // variable args - else - do - { - if (ftype->num_parms>=MAX_PARMS+MAX_EXTRA_PARMS) - QCC_PR_ParseError(ERR_TOOMANYTOTALPARAMETERS, "Too many parameters. Sorry. (limit is %i)\n", MAX_PARMS+MAX_EXTRA_PARMS); - - if (QCC_PR_CheckToken ("...")) - { - if (optional) - numparms = optional-1; - ftype->num_parms = (numparms * -1) - 1; - break; - } - - if (QCC_PR_CheckKeyword(keyword_optional, "optional")) - { - if (!optional) - optional = numparms+1; - } - else if (optional) - QCC_PR_ParseWarning(WARN_MISSINGOPTIONAL, "optional not specified on all optional args\n"); - - nptype = QCC_PR_ParseType(true, false); - - if (nptype->type == ev_void) - break; - if (!ptype) - { - ptype = nptype; - ftype->param = ptype; - } - else - { - ptype->next = nptype; - ptype = ptype->next; - } -// type->name = "FUNC PARAMETER"; - - - if (STRCMP(pr_token, ",") && STRCMP(pr_token, ")")) - { - name = QCC_PR_ParseName (); - if (definenames) - strcpy (pr_parm_names[numparms], name); - } - else if (definenames) - strcpy (pr_parm_names[numparms], ""); - numparms++; - if (optional) - ftype->num_parms = optional-1; - else - ftype->num_parms = numparms; - } while (QCC_PR_CheckToken (",")); - - QCC_PR_Expect (")"); - } - recursivefunctiontype--; - if (newtype) - return ftype; - return QCC_PR_FindType (ftype); -} -QCC_type_t *QCC_PR_ParseFunctionTypeReacc (int newtype, QCC_type_t *returntype) -{ - QCC_type_t *ftype, *ptype, *nptype; - char *name; - char argname[64]; - int definenames = !recursivefunctiontype; - - recursivefunctiontype++; - - ftype = QCC_PR_NewType(type_function->name, ev_function, false); - - ftype->aux_type = returntype; // return type - ftype->num_parms = 0; - ptype = NULL; - - - if (!QCC_PR_CheckToken (")")) - { - if (QCC_PR_CheckToken ("...")) - ftype->num_parms = -1; // variable args - else - do - { - if (ftype->num_parms>=MAX_PARMS+MAX_EXTRA_PARMS) - QCC_PR_ParseError(ERR_TOOMANYTOTALPARAMETERS, "Too many parameters. Sorry. (limit is %i)\n", MAX_PARMS+MAX_EXTRA_PARMS); - - if (QCC_PR_CheckToken ("...")) - { - ftype->num_parms = (ftype->num_parms * -1) - 1; - break; - } - - if (QCC_PR_CheckName("arg")) - { - sprintf(argname, "arg%i", ftype->num_parms); - name = argname; - nptype = QCC_PR_NewType("Variant", ev_variant, false); - } - else if (QCC_PR_CheckName("vect")) //this can only be of vector sizes, so... - { - sprintf(argname, "arg%i", ftype->num_parms); - name = argname; - nptype = QCC_PR_NewType("Vector", ev_vector, false); - } - else - { - name = QCC_PR_ParseName(); - QCC_PR_Expect(":"); - nptype = QCC_PR_ParseType(true, false); - } - - if (nptype->type == ev_void) - break; - if (!ptype) - { - ptype = nptype; - ftype->param = ptype; - } - else - { - ptype->next = nptype; - ptype = ptype->next; - } -// type->name = "FUNC PARAMETER"; - - if (definenames) - strcpy (pr_parm_names[ftype->num_parms], name); - ftype->num_parms++; - } while (QCC_PR_CheckToken (";")); - - QCC_PR_Expect (")"); - } - recursivefunctiontype--; - if (newtype) - return ftype; - return QCC_PR_FindType (ftype); -} -QCC_type_t *QCC_PR_PointerType (QCC_type_t *pointsto) -{ - QCC_type_t *ptype, *e; - ptype = QCC_PR_NewType("ptr", ev_pointer, false); - ptype->aux_type = pointsto; - e = QCC_PR_FindType (ptype); - if (e == ptype) - { - char name[128]; - sprintf(name, "ptr to %s", pointsto->name); - e->name = strdup(name); - } - return e; -} -QCC_type_t *QCC_PR_FieldType (QCC_type_t *pointsto) -{ - QCC_type_t *ptype; - char name[128]; - sprintf(name, "FIELD TYPE(%s)", pointsto->name); - ptype = QCC_PR_NewType(name, ev_field, false); - ptype->aux_type = pointsto; - ptype->size = ptype->aux_type->size; - return QCC_PR_FindType (ptype); -} - -pbool type_inlinefunction; -/*newtype=true: creates a new type always - silentfail=true: function is permitted to return NULL if it was not given a type, otherwise never returns NULL -*/ -QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail) -{ - QCC_type_t *newparm; - QCC_type_t *newt; - QCC_type_t *type; - char *name; - int i; - - type_inlinefunction = false; //doesn't really matter so long as its not from an inline function type - -// int ofs; - - if (QCC_PR_CheckToken ("..")) //so we don't end up with the user specifying '. .vector blah' (hexen2 added the .. token for array ranges) - { - newt = QCC_PR_NewType("FIELD TYPE", ev_field, false); - newt->aux_type = QCC_PR_ParseType (false, false); - - newt->size = newt->aux_type->size; - - newt = QCC_PR_FindType (newt); - - type = QCC_PR_NewType("FIELD TYPE", ev_field, false); - type->aux_type = newt; - - type->size = type->aux_type->size; - - if (newtype) - return type; - return QCC_PR_FindType (type); - } - if (QCC_PR_CheckToken (".")) - { - newt = QCC_PR_NewType("FIELD TYPE", ev_field, false); - newt->aux_type = QCC_PR_ParseType (false, false); - - newt->size = newt->aux_type->size; - - if (newtype) - return newt; - return QCC_PR_FindType (newt); - } - - name = QCC_PR_CheakCompConstString(pr_token); - - if (QCC_PR_CheckKeyword (keyword_class, "class")) - { -// int parms; - QCC_type_t *fieldtype; - char membername[2048]; - char *classname = QCC_PR_ParseName(); - int forwarddeclaration; - - newt = 0; - - /* Don't advance the line number yet */ - forwarddeclaration = pr_token[0] == ';'; - - /* Look to see if this type is already defined */ - for(i=0;inum_parms != 0) - QCC_PR_ParseError(ERR_REDECLARATION, "Redeclaration of class %s", classname); - - if (!newt) - newt = QCC_PR_NewType(classname, ev_entity, true); - - newt->size=type_entity->size; - - type = NULL; - - if (forwarddeclaration) - { - QCC_PR_CheckToken(";"); - return NULL; - } - - - - if (QCC_PR_CheckToken(":")) - { - char *parentname = QCC_PR_ParseName(); - newt->parentclass = QCC_TypeForName(parentname); - if (!newt->parentclass) - QCC_PR_ParseError(ERR_NOTANAME, "Parent class %s was not defined", parentname); - } - else - newt->parentclass = type_entity; - - - QCC_PR_Expect("{"); - if (QCC_PR_CheckToken(",")) - QCC_PR_ParseError(ERR_NOTANAME, "member missing name"); - while (!QCC_PR_CheckToken("}")) - { -// if (QCC_PR_CheckToken(",")) -// type->next = QCC_PR_NewType(type->name, type->type); -// else - newparm = QCC_PR_ParseType(true, false); - - if (newparm->type == ev_struct || newparm->type == ev_union) //we wouldn't be able to handle it. - QCC_PR_ParseError(ERR_INTERNAL, "Struct or union in class %s", classname); - - if (!QCC_PR_CheckToken(";")) - { - newparm->name = QCC_CopyString(pr_token)+strings; - QCC_PR_Lex(); - if (QCC_PR_CheckToken("[")) - { - type->next->size*=atoi(pr_token); - QCC_PR_Lex(); - QCC_PR_Expect("]"); - } - QCC_PR_CheckToken(";"); - } - else - newparm->name = QCC_CopyString("")+strings; - - sprintf(membername, "%s::"MEMBERFIELDNAME, classname, newparm->name); - fieldtype = QCC_PR_NewType(newparm->name, ev_field, false); - fieldtype->aux_type = newparm; - fieldtype->size = newparm->size; - QCC_PR_GetDef(fieldtype, membername, pr_scope, 2, 0, false); - - - newparm->ofs = 0;//newt->size; - newt->num_parms++; - - if (type) - type->next = newparm; - else - newt->param = newparm; - - type = newparm; - } - - - QCC_PR_Expect(";"); - return NULL; - } - if (QCC_PR_CheckKeyword (keyword_struct, "struct")) - { - newt = QCC_PR_NewType("struct", ev_struct, false); - newt->size=0; - QCC_PR_Expect("{"); - - type = NULL; - if (QCC_PR_CheckToken(",")) - QCC_PR_ParseError(ERR_NOTANAME, "element missing name"); - - newparm = NULL; - while (!QCC_PR_CheckToken("}")) - { - if (QCC_PR_CheckToken(",")) - { - if (!newparm) - QCC_PR_ParseError(ERR_NOTANAME, "element missing type"); - newparm = QCC_PR_NewType(newparm->name, newparm->type, false); - } - else - newparm = QCC_PR_ParseType(true, false); - - if (!QCC_PR_CheckToken(";")) - { - newparm->name = QCC_CopyString(pr_token)+strings; - QCC_PR_Lex(); - if (QCC_PR_CheckToken("[")) - { - newparm->arraysize=QCC_PR_IntConstExpr(); - QCC_PR_Expect("]"); - } - QCC_PR_CheckToken(";"); - } - else - newparm->name = QCC_CopyString("")+strings; - newparm->ofs = newt->size; - newt->size += newparm->size*(newparm->arraysize?newparm->arraysize:1); - newt->num_parms++; - - if (type) - type->next = newparm; - else - newt->param = newparm; - type = newparm; - } - return newt; - } - if (QCC_PR_CheckKeyword (keyword_union, "union")) - { - newt = QCC_PR_NewType("union", ev_union, false); - newt->size=0; - QCC_PR_Expect("{"); - - type = NULL; - if (QCC_PR_CheckToken(",")) - QCC_PR_ParseError(ERR_NOTANAME, "element missing name"); - newparm = NULL; - while (!QCC_PR_CheckToken("}")) - { - int arraysize; - if (QCC_PR_CheckToken(",")) - { - if (!newparm) - QCC_PR_ParseError(ERR_NOTANAME, "element missing type"); - newparm = QCC_PR_NewType(newparm->name, newparm->type, false); - } - else - newparm = QCC_PR_ParseType(true, false); - if (QCC_PR_CheckToken(";")) - newparm->name = QCC_CopyString("")+strings; - else - { - newparm->name = QCC_CopyString(pr_token)+strings; - QCC_PR_Lex(); - if (QCC_PR_CheckToken("[")) - { - newparm->arraysize=QCC_PR_IntConstExpr(); - QCC_PR_Expect("]"); - } - QCC_PR_Expect(";"); - } - newparm->ofs = 0; - arraysize = newparm->arraysize; - if (!arraysize) - arraysize = 1; - if (newparm->size*arraysize > newt->size) - newt->size = newparm->size*arraysize; - newt->num_parms++; - - if (type) - type->next = newparm; - else - newt->param = newparm; - type = newparm; - } - return newt; - } - type = NULL; - for (i = 0; i < numtypeinfos; i++) - { - if (!qcc_typeinfo[i].typedefed) - continue; - if (!STRCMP(qcc_typeinfo[i].name, name)) - { - type = &qcc_typeinfo[i]; - break; - } - } - - if (i == numtypeinfos) - { - if (!*name) - return NULL; - if (!stricmp("Void", name)) - type = type_void; - else if (!stricmp("Real", name)) - type = type_float; - else if (!stricmp("Vector", name)) - type = type_vector; - else if (!stricmp("Object", name)) - type = type_entity; - else if (!stricmp("String", name)) - type = type_string; - else if (!stricmp("PFunc", name)) - type = type_function; - else - { - if (silentfail) - return NULL; - - QCC_PR_ParseError (ERR_NOTATYPE, "\"%s\" is not a type", name); - type = type_float; // shut up compiler warning - } - } - QCC_PR_Lex (); - - while (QCC_PR_CheckToken("*")) - type = QCC_PointerTypeTo(type); - - if (QCC_PR_CheckToken ("(")) //this is followed by parameters. Must be a function. - { - type_inlinefunction = true; - type = QCC_PR_ParseFunctionType(newtype, type); - } - else - { - if (newtype) - { - type = QCC_PR_DuplicateType(type); - } - } - return type; -} - -#endif - - - diff --git a/misc/source/fteqcc-src/qccgui.c b/misc/source/fteqcc-src/qccgui.c deleted file mode 100644 index 9bcd9077..00000000 --- a/misc/source/fteqcc-src/qccgui.c +++ /dev/null @@ -1,2419 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include "qcc.h" -#include "gui.h" - - -/* -============== -LoadFile -============== -*/ -unsigned char *QCC_ReadFile (const char *fname, void *buffer, int len) -{ - long length; - FILE *f; - f = fopen(fname, "rb"); - if (!f) - return NULL; - length = fread(buffer, 1, len, f); - fclose(f); - - if (length != len) - return NULL; - - return buffer; -} -int QCC_FileSize (const char *fname) -{ - long length; - FILE *f; - f = fopen(fname, "rb"); - if (!f) - return -1; - fseek(f, 0, SEEK_END); - length = ftell(f); - fclose(f); - - return length; -} - -pbool QCC_WriteFile (const char *name, void *data, int len) -{ - long length; - FILE *f; - f = fopen(name, "wb"); - if (!f) - return false; - length = fwrite(data, 1, len, f); - fclose(f); - - if (length != len) - return false; - - return true; -} - -#undef printf -#undef Sys_Error - -void Sys_Error(const char *text, ...) -{ - va_list argptr; - static char msg[2048]; - - va_start (argptr,text); - QC_vsnprintf (msg,sizeof(msg)-1, text,argptr); - va_end (argptr); - - QCC_Error(ERR_INTERNAL, "%s", msg); -} - - -FILE *logfile; -int logprintf(const char *format, ...) -{ - va_list argptr; - static char string[1024]; - - va_start (argptr, format); -#ifdef _WIN32 - _vsnprintf (string,sizeof(string)-1, format,argptr); -#else - vsnprintf (string,sizeof(string), format,argptr); -#endif - va_end (argptr); - - printf("%s", string); - if (logfile) - fputs(string, logfile); - - return 0; -} - - - - - - - - - - - -#define Edit_Redo(hwndCtl) ((BOOL)(DWORD)SNDMSG((hwndCtl), EM_REDO, 0L, 0L)) - - -#define MAIN_WINDOW_CLASS_NAME "FTEMAINWINDOW" -#define MDI_WINDOW_CLASS_NAME "FTEMDIWINDOW" -#define EDIT_WINDOW_CLASS_NAME "FTEEDITWINDOW" -#define OPTIONS_WINDOW_CLASS_NAME "FTEOPTIONSWINDOW" - -#define EM_GETSCROLLPOS (WM_USER + 221) -#define EM_SETSCROLLPOS (WM_USER + 222) - - - -int GUIprintf(const char *msg, ...); -void GUIPrint(HWND wnd, char *msg); - -char finddef[256]; - -void RunCompiler(char *args); - -HINSTANCE ghInstance; -HMODULE richedit; - -pbool resetprogssrc; //progs.src was changed, reload project info. - - -HWND mainwindow; -HWND mdibox; -HWND outputwindow; -HWND outputbox; -HWND projecttree; -HWND gotodefbox; -HWND gotodefaccept; - -FILE *logfile; - -struct{ - char *text; - HWND hwnd; - int washit; -} buttons[] = { - {"Compile"}, - {"Edit"}, - {"Options"}, - {"Quit"} -}; - -#define ID_COMPILE 0 -#define ID_EDIT 1 -#define ID_OPTIONS 2 -#define ID_QUIT 3 - -#define NUMBUTTONS sizeof(buttons)/sizeof(buttons[0]) - - - -void GUI_DialogPrint(char *title, char *text) -{ - MessageBox(mainwindow, text, title, 0); -} - -HWND CreateAnEditControl(HWND parent) -{ - HWND newc; - - if (!richedit) - richedit = LoadLibrary("RICHED32.DLL"); - - newc=CreateWindowEx(WS_EX_CLIENTEDGE, - richedit?RICHEDIT_CLASS:"EDIT", - "", - WS_CHILD /*| ES_READONLY*/ | WS_VISIBLE | - WS_HSCROLL | WS_VSCROLL | ES_LEFT | ES_WANTRETURN | - ES_MULTILINE | ES_AUTOVSCROLL, - 0, 0, 0, 0, - parent, - NULL, - ghInstance, - NULL); - - if (!newc) - newc=CreateWindowEx(WS_EX_CLIENTEDGE, - richedit?RICHEDIT_CLASS10A:"EDIT", //fall back to the earlier version - "", - WS_CHILD /*| ES_READONLY*/ | WS_VISIBLE | - WS_HSCROLL | WS_VSCROLL | ES_LEFT | ES_WANTRETURN | - ES_MULTILINE | ES_AUTOVSCROLL, - 0, 0, 0, 0, - parent, - NULL, - ghInstance, - NULL); - - if (!newc) - { //you've not got RICHEDIT installed properly, I guess - FreeLibrary(richedit); - richedit = NULL; - newc=CreateWindowEx(WS_EX_CLIENTEDGE, - "EDIT", - "", - WS_CHILD /*| ES_READONLY*/ | WS_VISIBLE | - WS_HSCROLL | WS_VSCROLL | ES_LEFT | ES_WANTRETURN | - ES_MULTILINE | ES_AUTOVSCROLL, - 0, 0, 0, 0, - parent, - NULL, - ghInstance, - NULL); - } - - //go to lucidia console, 10pt - { - CHARFORMAT cf; - memset(&cf, 0, sizeof(cf)); - cf.cbSize = sizeof(cf); - cf.dwMask = CFM_BOLD | CFM_FACE;// | CFM_SIZE; - strcpy(cf.szFaceName, "Lucida Console"); - cf.yHeight = 5; - - SendMessage(newc, EM_SETCHARFORMAT, SCF_ALL, (WPARAM)&cf); - } - - if (richedit) - { - SendMessage(newc, EM_EXLIMITTEXT, 0, 1<<20); - } - - ShowWindow(newc, SW_SHOW); - - return newc; -} - - - - -enum { - IDM_OPENDOCU=32, - IDM_OPENNEW, - IDM_GOTODEF, - IDM_SAVE, - IDM_FIND, - IDM_QUIT, - IDM_UNDO, - IDM_REDO, - IDM_ABOUT, - IDM_HIGHTLIGHT, - IDM_CASCADE, - IDM_TILE_HORIZ, - IDM_TILE_VERT, - - IDI_O_LEVEL0, - IDI_O_LEVEL1, - IDI_O_LEVEL2, - IDI_O_LEVEL3, - IDI_O_DEFAULT, - IDI_O_DEBUG, - IDI_O_CHANGE_PROGS_SRC, - IDI_O_ADDITIONALPARAMETERS, - IDI_O_OPTIMISATION, - IDI_O_COMPILER_FLAG, - IDI_O_USE, - IDI_O_APPLY, - IDI_O_TARGET, - IDI_O_SYNTAX_HIGHLIGHTING, - - IDM_FIRSTCHILD -}; - - -typedef struct editor_s { - char filename[MAX_PATH]; //abs - HWND window; - HWND editpane; - pbool modified; - time_t filemodifiedtime; - struct editor_s *next; -} editor_t; - -editor_t *editors; - -void EditorReload(editor_t *editor); -int EditorSave(editor_t *edit); -void EditFile(char *name, int line); -pbool EditorModified(editor_t *e); -int Rehighlight(editor_t *edit); - -void QueryOpenFile(void) -{ - char filename[MAX_PATH]; - char oldpath[MAX_PATH+10]; - OPENFILENAME ofn; - memset(&ofn, 0, sizeof(ofn)); - ofn.lStructSize = sizeof(ofn); - ofn.hInstance = ghInstance; - ofn.lpstrFile = filename; - ofn.nMaxFile = sizeof(filename)-1; - memset(filename, 0, sizeof(filename)); - GetCurrentDirectory(sizeof(oldpath)-1, oldpath); - if (GetOpenFileName(&ofn)) - EditFile(filename, -1); - SetCurrentDirectory(oldpath); -} - -//IDM_ stuff that needs no active menu -void GenericMenu(WPARAM wParam) -{ - switch(LOWORD(wParam)) - { - case IDM_OPENNEW: - QueryOpenFile(); - break; - - case IDM_ABOUT: - MessageBox(NULL, "FTE QuakeC Compiler\nWritten by Forethough Entertainment.\n\nIt has a few cool features, like a semi-useful IDE.\n\nSupports:\nPrecompiler (with macros)\nArrays\n+= / -= / *= / /= operations.\nSwitch statements\nfor loops\nLots of optimisations.", "About", 0); - break; - - case IDM_CASCADE: - SendMessage(mdibox, WM_MDICASCADE, 0, 0); - break; - case IDM_TILE_HORIZ: - SendMessage(mdibox, WM_MDITILE, MDITILE_HORIZONTAL, 0); - break; - case IDM_TILE_VERT: - SendMessage(mdibox, WM_MDITILE, MDITILE_VERTICAL, 0); - break; - } -} - -void EditorMenu(editor_t *editor, WPARAM wParam) -{ - switch(LOWORD(wParam)) - { - case IDM_OPENDOCU: - { - char buffer[1024]; - int total; - total = SendMessage(editor->editpane, EM_GETSELTEXT, (WPARAM)sizeof(buffer)-1, (LPARAM)buffer); - buffer[total]='\0'; - if (!total) - { - MessageBox(NULL, "There is no name currently selected.", "Whoops", 0); - break; - } - else - EditFile(buffer, -1); - } - break; - case IDM_SAVE: - EditorSave(editor); - break; - case IDM_GOTODEF: - { - char buffer[1024]; - int total; - total = SendMessage(editor->editpane, EM_GETSELTEXT, (WPARAM)sizeof(buffer)-1, (LPARAM)buffer); - buffer[total]='\0'; - if (!total) - { - MessageBox(NULL, "There is no name currently selected.", "Whoops", 0); - break; - } - else - GoToDefinition(buffer); - } - break; - case IDM_HIGHTLIGHT: - Rehighlight(editor); - break; - - case IDM_UNDO: - Edit_Undo(editor->editpane); - break; - case IDM_REDO: - Edit_Redo(editor->editpane); - break; - - default: - GenericMenu(wParam); - break; - } -} - -static LONG CALLBACK EditorWndProc(HWND hWnd,UINT message, - WPARAM wParam,LPARAM lParam) -{ - RECT rect; - HDC hdc; - PAINTSTRUCT ps; - - editor_t *editor; - for (editor = editors; editor; editor = editor->next) - { - if (editor->window == hWnd) - break; - if (editor->window == NULL) - break; //we're actually creating it now. - } - if (!editor) - goto gdefault; - - switch (message) - { - case WM_CLOSE: - case WM_QUIT: - if (editor->modified) - { - switch (MessageBox(hWnd, "Would you like to save?", editor->filename, MB_YESNOCANCEL)) - { - case IDCANCEL: - return false; - case IDYES: - if (!EditorSave(editor)) - return false; - case IDNO: - default: - break; - } - } - goto gdefault; - case WM_DESTROY: - { - editor_t *e; - if (editor == editors) - { - editors = editor->next; - free(editor); - return 0; - } - for (e = editors; e; e = e->next) - { - if (e->next == editor) - { - e->next = editor->next; - free(editor); - return 0; - } - } - MessageBox(0, "Couldn't destroy file reference", "WARNING", 0); - } - goto gdefault; - case WM_CREATE: - editor->editpane = CreateAnEditControl(hWnd); - /* - editor->editpane=CreateWindowEx(WS_EX_CLIENTEDGE, - richedit?RICHEDIT_CLASS:"EDIT", - "", - WS_CHILD | WS_VISIBLE | - WS_HSCROLL | WS_VSCROLL | ES_LEFT | ES_WANTRETURN | - ES_MULTILINE | ES_AUTOVSCROLL, - 0, 0, 0, 0, - hWnd, - NULL, - ghInstance, - NULL); -*/ - if (richedit) - { - SendMessage(editor->editpane, EM_EXLIMITTEXT, 0, 1<<31); - - SendMessage(editor->editpane, EM_SETUNDOLIMIT, 256, 256); - } - goto gdefault; - case WM_SIZE: - GetClientRect(hWnd, &rect); - SetWindowPos(editor->editpane, NULL, 0, 0, rect.right-rect.left, rect.bottom-rect.top, 0); - goto gdefault; - case WM_PAINT: - hdc=BeginPaint(hWnd,(LPPAINTSTRUCT)&ps); - - EndPaint(hWnd,(LPPAINTSTRUCT)&ps); - return TRUE; - break; - case WM_COMMAND: - if (HIWORD(wParam) == EN_CHANGE && (HWND)lParam == editor->editpane) - { - if (!editor->modified) - { - char title[2048]; - CHARRANGE chrg; - - editor->modified = true; - if (EditorModified(editor)) - if (MessageBox(NULL, "warning: file was modified externally. reload?", "Modified!", MB_YESNO) == IDYES) - EditorReload(editor); - - - SendMessage(editor->editpane, EM_EXGETSEL, 0, (LPARAM) &chrg); - if (editor->modified) - sprintf(title, "*%s:%i - FTEQCC Editor", editor->filename, 1+Edit_LineFromChar(editor->editpane, chrg.cpMin)); - else - sprintf(title, "%s:%i - FTEQCC Editor", editor->filename, 1+Edit_LineFromChar(editor->editpane, chrg.cpMin)); - SetWindowText(editor->window, title); - } - } - else - { - if (mdibox) - goto gdefault; - EditorMenu(editor, wParam); - } - break; - case WM_NOTIFY: - { - NMHDR *nmhdr; - SELCHANGE *sel; - char title[2048]; - nmhdr = (NMHDR *)lParam; - switch(nmhdr->code) - { - case EN_SELCHANGE: - sel = (SELCHANGE *)nmhdr; - if (editor->modified) - sprintf(title, "*%s:%i - FTEQCC Editor", editor->filename, 1+Edit_LineFromChar(editor->editpane, sel->chrg.cpMin)); - else - sprintf(title, "%s:%i - FTEQCC Editor", editor->filename, 1+Edit_LineFromChar(editor->editpane, sel->chrg.cpMin)); - SetWindowText(editor->window, title); - break; - } - } - default: - gdefault: - if (mdibox) - return DefMDIChildProc(hWnd,message,wParam,lParam); - else - return DefWindowProc(hWnd,message,wParam,lParam); - } - return 0; -} - -#if 1 -static DWORD lastcolour; -int GUIEmitText(HWND wnd, int start, char *text, int len) -{ - int c, cr; - DWORD colour; - CHARFORMAT cf; - - if (!len) - return start; - - c = text[len]; - text[len] = '\0'; - Edit_SetSel(wnd,start,start); - Edit_ReplaceSel(wnd,text); - - if (!strcmp(text, "void")) - colour = RGB(0, 0, 255); - else if (!strcmp(text, "float")) - colour = RGB(0, 0, 255); - else if (!strcmp(text, "vector")) - colour = RGB(0, 0, 255); - else if (!strcmp(text, "entity")) - colour = RGB(0, 0, 255); - else if (!strcmp(text, "local")) - colour = RGB(0, 0, 255); - else if (!strcmp(text, "string")) - colour = RGB(0, 0, 255); - else if (!strcmp(text, "struct")) - colour = RGB(0, 0, 255); - else if (!strcmp(text, "class")) - colour = RGB(0, 0, 255); - else if (!strcmp(text, "union")) - colour = RGB(0, 0, 255); - else if (!strcmp(text, "const")) - colour = RGB(0, 0, 255); - else if (!strcmp(text, "var")) - colour = RGB(0, 0, 255); - else if (!strcmp(text, "nosave")) - colour = RGB(0, 0, 255); - - else if (!strcmp(text, "goto")) - colour = RGB(255, 0, 0); - else if (!strcmp(text, "thinktime")) - colour = RGB(0, 0, 255); - else if (!strcmp(text, "if")) - colour = RGB(0, 0, 255); - else if (!strcmp(text, "else")) - colour = RGB(0, 0, 255); - else if (!strcmp(text, "switch")) - colour = RGB(0, 0, 255); - else if (!strcmp(text, "case")) - colour = RGB(0, 0, 255); - else if (!strcmp(text, "default")) - colour = RGB(0, 0, 255); - else if (!strcmp(text, "break")) - colour = RGB(0, 0, 255); - else if (!strcmp(text, "continue")) - colour = RGB(0, 0, 255); - else if (!strcmp(text, "do")) - colour = RGB(0, 0, 255); - else if (!strcmp(text, "while")) - colour = RGB(0, 0, 255); - else if (!strcmp(text, "for")) - colour = RGB(0, 0, 255); - else if (!strcmp(text, "return")) - colour = RGB(0, 0, 255); - - else if (!strcmp(text, "self")) - colour = RGB(0, 0, 127); - else if (!strcmp(text, "this")) - colour = RGB(0, 0, 127); - else if (!strcmp(text, "other")) - colour = RGB(0, 0, 127); - else if (!strcmp(text, "world")) - colour = RGB(0, 0, 127); - else if (!strcmp(text, "time")) - colour = RGB(0, 0, 127); - - - else if (!strcmp(text, "#define")) - colour = RGB(0, 128, 255); - else if (!strcmp(text, "#ifdef")) - colour = RGB(0, 128, 255); - else if (!strcmp(text, "#ifndef")) - colour = RGB(0, 128, 255); - else if (!strcmp(text, "#else")) - colour = RGB(0, 128, 255); - else if (!strcmp(text, "#endif")) - colour = RGB(0, 128, 255); - else if (!strcmp(text, "#undef")) - colour = RGB(0, 128, 255); - else if (!strcmp(text, "#pragma")) - colour = RGB(0, 128, 255); - else if (!strcmp(text, "#includelist")) - colour = RGB(0, 128, 255); - else if (!strcmp(text, "#endlist")) - colour = RGB(0, 128, 255); - - - else if (*text == '\"') - colour = RGB(128, 0, 0); - - else if (!strncmp(text, "//", 2)) - colour = RGB(0, 127, 0); - else if (!strncmp(text, "/*", 2)) - colour = RGB(0, 127, 0); - else - colour = RGB(0, 0, 0); - - text[len] = c; - - cr = 0; - for (c = 0; c < len; c++) - if (text[c] == '\r') - cr++; - if (cr) - len-=cr; - - if (colour == lastcolour) - return start+len; - - lastcolour = colour; - - Edit_SetSel(wnd,start,start+len); - memset(&cf, 0, sizeof(cf)); - cf.cbSize = sizeof(cf); - cf.dwMask = CFM_COLOR; - cf.crTextColor = colour; - SendMessage(wnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf); - Edit_SetSel(wnd,start+len,start+len); - - return start + len; -} -void GUIFormattingPrint(HWND wnd, char *msg) -{ - int len=Edit_GetTextLength(wnd); - char *start; - CHARRANGE chrg; - lastcolour = RGB(0,0,0); - SendMessage(wnd, WM_SETREDRAW, false, 0); - chrg.cpMin = chrg.cpMax = 0; - SendMessage(wnd, EM_EXSETSEL, 0, (LPARAM) &chrg); - - for(start = msg;;) - { - if (!*msg) - break; - else if (*msg == '/' && msg[1] == '/') - { - len = GUIEmitText(wnd, len, start, msg - start); - start = msg; - - msg+=2; - while(*msg && *msg != '\n' && *msg != '\r') - msg++; - } - else if (*msg == '/' && msg[1] == '*') - { - len = GUIEmitText(wnd, len, start, msg - start); - start = msg; - msg+=2; - while(*msg) - { - if (msg[0] == '*' && msg[1] == '/') - { - msg+=2; - break; - } - msg++; - } - } - else if (*msg == '#' || *msg == '_' || (*msg >= 'A' && *msg <= 'Z') || (*msg >= 'a' && *msg <= 'z')) - { - len = GUIEmitText(wnd, len, start, msg - start); - start = msg; - msg++; - while (*msg == '_' || (*msg >= 'A' && *msg <= 'Z') || (*msg >= 'a' && *msg <= 'z' || *msg >= '0' && *msg <= '9')) - msg++; - } - else if (*msg == '\"') - { - len = GUIEmitText(wnd, len, start, msg - start); - start = msg; - msg++; - while(*msg) - { - if (*msg == '\\') - msg++; - else if (*msg == '\"') - { - msg++; - break; - } - - msg++; - } - } -/* else if (*msg <= ' ') - { - while (*msg <= ' ' && *msg) - msg++; - }*/ - else - { - msg++; - continue; - } - - len = GUIEmitText(wnd, len, start, msg - start); - start = msg; - } - len = GUIEmitText(wnd, len, start, msg - start); - start = msg; - SendMessage(wnd, WM_SETREDRAW, true, 0); -} - -int Rehighlight(editor_t *edit) -{ - int len; - char *file; - - CHARRANGE chrg; - POINT scrollpos; - - SendMessage(edit->editpane, EM_SETEVENTMASK, 0, 0); - - SendMessage(edit->editpane, EM_GETSCROLLPOS, 0, (LPARAM)&scrollpos); - SendMessage(edit->editpane, EM_EXGETSEL, 0, (LPARAM) &chrg); - - len = Edit_GetTextLength(edit->editpane); - file = malloc(len+1); - if (!file) - { - MessageBox(NULL, "Save failed - not enough mem", "Error", 0); - return false; - } - Edit_GetText(edit->editpane, file, len); - file[len] = '\0'; - - SetWindowText(edit->editpane,""); - -// GUIPrint(edit->editpane, file); - GUIFormattingPrint(edit->editpane, file); - free(file); - -// Edit_SetSel(edit->editpane, Edit_LineIndex(neweditor->editpane, 0), Edit_LineIndex(neweditor->editpane, 0)); - - InvalidateRect(edit->editpane, NULL, true); - InvalidateRect(edit->window, NULL, true); - - SendMessage(edit->editpane, EM_SETEVENTMASK, 0, ENM_SELCHANGE|ENM_CHANGE); - - SendMessage(edit->editpane, EM_SETSCROLLPOS, 0, (LPARAM)&scrollpos); - SendMessage(edit->editpane, EM_EXSETSEL, 0, (LPARAM) &chrg); - - UpdateWindow(edit->editpane); - RedrawWindow(edit->window, NULL, NULL, 0); - - return true; -} -#else -static void chunkcolour(HWND pane, int start, int end, DWORD colour) -{ - CHARFORMAT cf; - if (colour == RGB(0,0,0)) - return; //don't need to - Edit_SetSel(pane,start,end); - memset(&cf, 0, sizeof(cf)); - cf.cbSize = sizeof(cf); - cf.dwMask = CFM_COLOR; - cf.crTextColor = colour; - SendMessage(pane, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf); -} -void GUIFormattingPrint(HWND wnd, char *msg) -{ -} -int Rehighlight(editor_t *edit) -{ - char *file; - int c, last, len; - DWORD color; - CHARRANGE chrg; - POINT scrollpos; - - //Dsiable redraws - SendMessage(edit->editpane, WM_SETREDRAW, false, 0); - - //Don't notify us for a bit.. - SendMessage(edit->editpane, EM_SETEVENTMASK, 0, 0); - - //get state so we can restore scroll positions and things. - SendMessage(edit->editpane, EM_GETSCROLLPOS, 0, (LPARAM)&scrollpos); - SendMessage(edit->editpane, EM_EXGETSEL, 0, (LPARAM) &chrg); - - - len = Edit_GetTextLength(edit->editpane); - file = malloc(len+1); - if (!file) - { - MessageBox(NULL, "Highlight failed - not enough mem", "Error", 0); - return false; - } - Edit_GetText(edit->editpane, file, len); - file[len] = '\0'; - SetWindowText(edit->editpane,file); //this is so that we guarentee that the \rs or whatever that windows insists on inserting don't get in the way - - color = RGB(0,0,0); - for (last = 0, c = 0; c < len; c++) - { - if (file[c] == '/' && file[c+1] == '/') //do special syntax - { - chunkcolour(edit->editpane, last, c, color); - last = c; - - while(file[c] != '\n') - c++; - color = RGB(0, 127, 0); - } - else - { - chunkcolour(edit->editpane, last, c, color); - last = c; - - while(file[c] >= 'a' && file[c] <= 'z' || file[c] >= 'A' && file[c] <= 'Z' || file[c] == '_') - c++; - - color = RGB(rand(), rand(), rand()); - } - } - - free(file); - - //reenable drawing - SendMessage(edit->editpane, WM_SETREDRAW, true, 0); -} -#endif - -void EditorReload(editor_t *editor) -{ - struct stat sbuf; - int flen; - char *file; - - flen = QCC_FileSize(editor->filename); - if (flen >= 0) - { - file = malloc(flen+1); - QCC_ReadFile(editor->filename, file, flen); - file[flen] = 0; - } - - SendMessage(editor->editpane, EM_SETEVENTMASK, 0, 0); - - /*clear it out*/ - Edit_SetSel(editor->editpane,0,Edit_GetTextLength(editor->editpane)); - Edit_ReplaceSel(editor->editpane,""); - - if (!fl_autohighlight) - { - GUIPrint(editor->editpane, file); - } - else - { - GUIFormattingPrint(editor->editpane, file); - } - free(file); - - editor->modified = false; - stat(editor->filename, &sbuf); - editor->filemodifiedtime = sbuf.st_mtime; - - SendMessage(editor->editpane, EM_SETEVENTMASK, 0, ENM_SELCHANGE|ENM_CHANGE); -} - -void EditFile(char *name, int line) -{ - char title[1024]; - editor_t *neweditor; - WNDCLASS wndclass; - HMENU menu, menufile, menuhelp, menunavig; - - for (neweditor = editors; neweditor; neweditor = neweditor->next) - { - if (neweditor->window && !strcmp(neweditor->filename, name)) - { - if (line >= 0) - { - Edit_SetSel(neweditor->editpane, Edit_LineIndex(neweditor->editpane, line), Edit_LineIndex(neweditor->editpane, line+1)); - Edit_ScrollCaret(neweditor->editpane); - } - if (mdibox) - SendMessage(mdibox, WM_MDIACTIVATE, (WPARAM)neweditor->window, 0); - SetFocus(neweditor->window); - SetFocus(neweditor->editpane); - return; - } - } - - if (QCC_FileSize(name) == -1) - { - MessageBox(NULL, "File not found.", "Error", 0); - return; - } - - neweditor = malloc(sizeof(editor_t)); - if (!neweditor) - { - MessageBox(NULL, "Low memory", "Error", 0); - return; - } - - neweditor->next = editors; - editors = neweditor; - - strncpy(neweditor->filename, name, sizeof(neweditor->filename)-1); - - if (!mdibox) - { - menu = CreateMenu(); - menufile = CreateMenu(); - menuhelp = CreateMenu(); - menunavig = CreateMenu(); - AppendMenu(menu, MF_POPUP, (UINT_PTR)menufile, "&File"); - AppendMenu(menu, MF_POPUP, (UINT_PTR)menunavig, "&Navigation"); - AppendMenu(menu, MF_POPUP, (UINT_PTR)menuhelp, "&Help"); - AppendMenu(menufile, 0, IDM_OPENNEW, "Open &new file "); - AppendMenu(menufile, 0, IDM_SAVE, "&Save "); - // AppendMenu(menufile, 0, IDM_FIND, "&Find"); - AppendMenu(menufile, 0, IDM_UNDO, "&Undo Ctrl+Z"); - AppendMenu(menufile, 0, IDM_REDO, "&Redo Ctrl+Y"); - AppendMenu(menunavig, 0, IDM_GOTODEF, "Go to definition"); - AppendMenu(menunavig, 0, IDM_OPENDOCU, "Open selected file"); - AppendMenu(menuhelp, 0, IDM_ABOUT, "About"); - AppendMenu(menu, 0, IDM_HIGHTLIGHT, "H&ighlight"); - } - - - - wndclass.style = 0; - wndclass.lpfnWndProc = (WNDPROC)EditorWndProc; - wndclass.cbClsExtra = 0; - wndclass.cbWndExtra = 0; - wndclass.hInstance = ghInstance; - wndclass.hIcon = 0; - wndclass.hCursor = LoadCursor (NULL,IDC_ARROW); - wndclass.hbrBackground = (void *)COLOR_WINDOW; - wndclass.lpszMenuName = 0; - wndclass.lpszClassName = EDIT_WINDOW_CLASS_NAME; - RegisterClass(&wndclass); - - neweditor->window = NULL; - if (mdibox) - { - MDICREATESTRUCT mcs; - - sprintf(title, "%s", name); - - mcs.szClass = EDIT_WINDOW_CLASS_NAME; - mcs.szTitle = name; - mcs.hOwner = ghInstance; - mcs.x = mcs.cx = CW_USEDEFAULT; - mcs.y = mcs.cy = CW_USEDEFAULT; - mcs.style = WS_OVERLAPPEDWINDOW; - mcs.lParam = 0; - - neweditor->window = (HWND) SendMessage (mdibox, WM_MDICREATE, 0, - (LONG_PTR) (LPMDICREATESTRUCT) &mcs); - } - else - { - sprintf(title, "%s - FTEEditor", name); - - neweditor->window=CreateWindow(EDIT_WINDOW_CLASS_NAME, title, WS_OVERLAPPEDWINDOW, - 0, 0, 640, 480, NULL, NULL, ghInstance, NULL); - } - - if (!mdibox) - SetMenu(neweditor->window, menu); - - if (!neweditor->window) - { - MessageBox(NULL, "Failed to create editor window", "Error", 0); - return; - } - - EditorReload(neweditor); - - if (line >= 0) - Edit_SetSel(neweditor->editpane, Edit_LineIndex(neweditor->editpane, line), Edit_LineIndex(neweditor->editpane, line+1)); - else - Edit_SetSel(neweditor->editpane, Edit_LineIndex(neweditor->editpane, 0), Edit_LineIndex(neweditor->editpane, 0)); - - Edit_ScrollCaret(neweditor->editpane); - - ShowWindow(neweditor->window, SW_SHOW); - SetFocus(mainwindow); - SetFocus(neweditor->window); - SetFocus(neweditor->editpane); -} - -int EditorSave(editor_t *edit) -{ - struct stat sbuf; - int len; - char *file; - len = Edit_GetTextLength(edit->editpane); - file = malloc(len+1); - if (!file) - { - MessageBox(NULL, "Save failed - not enough mem", "Error", 0); - return false; - } - Edit_GetText(edit->editpane, file, len); - if (!QCC_WriteFile(edit->filename, file, len)) - { - MessageBox(NULL, "Save failed\nCheck path and ReadOnly flags", "Failure", 0); - return false; - } - free(file); - - /*now whatever is on disk should have the current time*/ - edit->modified = false; - stat(edit->filename, &sbuf); - edit->filemodifiedtime = sbuf.st_mtime; - - return true; -} -void EditorsRun(void) -{ -} - - -char *GUIReadFile(const char *fname, void *buffer, int blen) -{ - editor_t *e; - for (e = editors; e; e = e->next) - { - if (e->window && !strcmp(e->filename, fname)) - { - int elen = Edit_GetTextLength(e->editpane); - Edit_GetText(e->editpane, buffer, blen); - return buffer; - } - } - - return QCC_ReadFile(fname, buffer, blen); -} - -int GUIFileSize(const char *fname) -{ - editor_t *e; - for (e = editors; e; e = e->next) - { - if (e->window && !strcmp(e->filename, fname)) - { - int len = Edit_GetTextLength(e->editpane); - return len; - } - } - return QCC_FileSize(fname); -} - -/*checks if the file has been modified externally*/ -pbool EditorModified(editor_t *e) -{ - struct stat sbuf; - stat(e->filename, &sbuf); - if (e->filemodifiedtime != sbuf.st_mtime) - return true; - - return false; -} - - - - - - - - - - - - - -HWND optionsmenu; -HWND hexen2item; -HWND nokeywords_coexistitem; -HWND autoprototype_item; -HWND autohighlight_item; -HWND extraparmsitem; -static LONG CALLBACK OptionsWndProc(HWND hWnd,UINT message, - WPARAM wParam,LPARAM lParam) -{ - int i; - switch (message) - { - case WM_DESTROY: - optionsmenu = NULL; - break; - - case WM_COMMAND: - switch(wParam) - { - case IDI_O_USE: - case IDI_O_APPLY: - for (i = 0; optimisations[i].enabled; i++) - { - if (optimisations[i].flags & FLAG_HIDDENINGUI) - continue; - - if (Button_GetCheck(optimisations[i].guiinfo)) - optimisations[i].flags |= FLAG_SETINGUI; - else - optimisations[i].flags &= ~FLAG_SETINGUI; - } - fl_hexen2 = Button_GetCheck(hexen2item); - for (i = 0; compiler_flag[i].enabled; i++) - { - if (compiler_flag[i].flags & FLAG_HIDDENINGUI) - continue; - if (Button_GetCheck(compiler_flag[i].guiinfo)) - compiler_flag[i].flags |= FLAG_SETINGUI; - else - compiler_flag[i].flags &= ~FLAG_SETINGUI; - } - fl_autohighlight = Button_GetCheck(autohighlight_item); - Edit_GetText(extraparmsitem, parameters, sizeof(parameters)-1); - - if (wParam == IDI_O_USE) - buttons[ID_COMPILE].washit = true; - break; - case IDI_O_CHANGE_PROGS_SRC: - { - char *s, *s2; - char filename[MAX_PATH]; - char oldpath[MAX_PATH+10]; - OPENFILENAME ofn; - memset(&ofn, 0, sizeof(ofn)); - ofn.lStructSize = sizeof(ofn); - ofn.hInstance = ghInstance; - ofn.lpstrFile = filename; - ofn.lpstrTitle = "Please find progs.src"; - ofn.nMaxFile = sizeof(filename)-1; - ofn.lpstrFilter = "QuakeC source\0*.src\0All files\0*.*\0"; - memset(filename, 0, sizeof(filename)); - GetCurrentDirectory(sizeof(oldpath)-1, oldpath); - ofn.lpstrInitialDir = oldpath; - if (GetOpenFileName(&ofn)) - { - strcpy(progssrcdir, filename); - for(s = progssrcdir; s; s = s2) - { - s2 = strchr(s+1, '\\'); - if (!s2) - break; - s = s2; - } - if (s) - { - *s = '\0'; - strcpy(progssrcname, s+1); - } - else - strcpy(progssrcname, filename); - - SetCurrentDirectory(progssrcdir); - *progssrcdir = '\0'; - } - resetprogssrc = true; - } - break; - case IDI_O_LEVEL0: - case IDI_O_LEVEL1: - case IDI_O_LEVEL2: - case IDI_O_LEVEL3: - for (i = 0; optimisations[i].enabled; i++) - { - if (optimisations[i].flags & FLAG_HIDDENINGUI) - continue; - - if (optimisations[i].optimisationlevel<=(int)wParam-IDI_O_LEVEL0) - Button_SetCheck(optimisations[i].guiinfo, 1); - else - Button_SetCheck(optimisations[i].guiinfo, 0); - } - break; - case IDI_O_DEBUG: - for (i = 0; optimisations[i].enabled; i++) - { - if (optimisations[i].flags & FLAG_HIDDENINGUI) - continue; - - if (optimisations[i].flags&FLAG_KILLSDEBUGGERS) - Button_SetCheck(optimisations[i].guiinfo, 0); - } - break; - case IDI_O_DEFAULT: - for (i = 0; optimisations[i].enabled; i++) - { - if (optimisations[i].flags & FLAG_HIDDENINGUI) - continue; - - if (optimisations[i].flags & FLAG_ASDEFAULT) - Button_SetCheck(optimisations[i].guiinfo, 1); - else - Button_SetCheck(optimisations[i].guiinfo, 0); - } - break; - } - break; - case WM_HELP: - { - HELPINFO *hi; - hi = (HELPINFO *)lParam; - switch(hi->iCtrlId) - { - case IDI_O_DEFAULT: - MessageBox(hWnd, "Sets the default optimisations", "Help", MB_OK|MB_ICONINFORMATION); - break; - case IDI_O_DEBUG: - MessageBox(hWnd, "Clears all optimisations which can make your progs harder to debug", "Help", MB_OK|MB_ICONINFORMATION); - break; - case IDI_O_LEVEL0: - case IDI_O_LEVEL1: - case IDI_O_LEVEL2: - case IDI_O_LEVEL3: - MessageBox(hWnd, "Sets a specific optimisation level", "Help", MB_OK|MB_ICONINFORMATION); - break; - case IDI_O_CHANGE_PROGS_SRC: - MessageBox(hWnd, "Use this button to change your root source file.\nNote that fteqcc compiles sourcefiles from editors first, rather than saving. This means that changes are saved ONLY when you save them, but means that switching project mid-compile can result in problems.", "Help", MB_OK|MB_ICONINFORMATION); - break; - case IDI_O_ADDITIONALPARAMETERS: - MessageBox(hWnd, "Type in additional commandline parameters here. Use -Dname to define a named precompiler constant before compiling.", "Help", MB_OK|MB_ICONINFORMATION); - break; - case IDI_O_APPLY: - MessageBox(hWnd, "Apply changes shown, but do not recompile yet.", "Help", MB_OK|MB_ICONINFORMATION); - break; - case IDI_O_USE: - MessageBox(hWnd, "Apply changes shown here and recompile.", "Help", MB_OK|MB_ICONINFORMATION); - break; - case IDI_O_OPTIMISATION: - for (i = 0; optimisations[i].enabled; i++) - { - if (optimisations[i].guiinfo == hi->hItemHandle) - { - MessageBox(hWnd, optimisations[i].description, "Help", MB_OK|MB_ICONINFORMATION); - break; - } - } - break; - case IDI_O_COMPILER_FLAG: - for (i = 0; compiler_flag[i].enabled; i++) - { - if (compiler_flag[i].guiinfo == hi->hItemHandle) - { - MessageBox(hWnd, compiler_flag[i].description, "Help", MB_OK|MB_ICONINFORMATION); - break; - } - } - break; - case IDI_O_TARGET: - MessageBox(hWnd, "Click here to compile a hexen2 compatible progs. Note that this uses the -Thexen2. There are other targets available.", "Help", MB_OK|MB_ICONINFORMATION); - break; - case IDI_O_SYNTAX_HIGHLIGHTING: - MessageBox(hWnd, "Should syntax be highlighted automatically when a file is opened?", "Help", MB_OK|MB_ICONINFORMATION); - break; - } - } - break; - default: - return DefWindowProc(hWnd,message,wParam,lParam); - } - return 0; -} -void OptionsDialog(void) -{ - HWND subsection; - RECT r; - WNDCLASS wndclass; - HWND wnd; - int i; - int flagcolums=1; - - int x; - int y; - int my; - int height; - int num; - int cflagsshown; - - if (optionsmenu) - { - BringWindowToTop(optionsmenu); - return; - } - - - memset(&wndclass, 0, sizeof(wndclass)); - wndclass.style = 0; - wndclass.lpfnWndProc = (WNDPROC)OptionsWndProc; - wndclass.cbClsExtra = 0; - wndclass.cbWndExtra = 0; - wndclass.hInstance = ghInstance; - wndclass.hIcon = 0; - wndclass.hCursor = LoadCursor (NULL,IDC_ARROW); - wndclass.hbrBackground = (void *)COLOR_WINDOW; - wndclass.lpszMenuName = 0; - wndclass.lpszClassName = OPTIONS_WINDOW_CLASS_NAME; - RegisterClass(&wndclass); - - height = 0; - for (i = 0; optimisations[i].enabled; i++) - { - if (optimisations[i].flags & FLAG_HIDDENINGUI) - continue; - - height++; - } - - cflagsshown = 2; - for (i = 0; compiler_flag[i].enabled; i++) - { - if (compiler_flag[i].flags & FLAG_HIDDENINGUI) - continue; - - cflagsshown++; - } - - height = (height-1)/2; - - while (cflagsshown > ((480-(88+40))/16)*(flagcolums)) - flagcolums++; - - if (height < (cflagsshown+flagcolums-1)/flagcolums) - height = (cflagsshown+flagcolums-1)/flagcolums; - - r.right = 408 + flagcolums*168; - if (r.right < 640) - r.right = 640; - - height *= 16; - - height += 88+40; - - r.left = GetSystemMetrics(SM_CXSCREEN)/2-320; - r.top = GetSystemMetrics(SM_CYSCREEN)/2-240; - r.bottom = r.top + height; - r.right += r.left; - - - - AdjustWindowRectEx (&r, WS_CAPTION|WS_SYSMENU, FALSE, 0); - - optionsmenu=CreateWindowEx(WS_EX_CONTEXTHELP, OPTIONS_WINDOW_CLASS_NAME, "Options - FTE QuakeC compiler", WS_CAPTION|WS_SYSMENU, - r.left, r.top, r.right-r.left, r.bottom-r.top, NULL, NULL, ghInstance, NULL); - - subsection = CreateWindow("BUTTON", "Optimisations", WS_CHILD|WS_VISIBLE|BS_GROUPBOX, - 0, 0, 400, height-48, optionsmenu, NULL, ghInstance, NULL); - - num = 0; - for (i = 0; optimisations[i].enabled; i++) - { - if (optimisations[i].flags & FLAG_HIDDENINGUI) - { - optimisations[i].guiinfo = NULL; - continue; - } - - optimisations[i].guiinfo = wnd = CreateWindow("BUTTON",optimisations[i].fullname, - WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX, - 8+200*(num&1),16+16*(num/2),200-16,16, - subsection, - (HMENU)IDI_O_OPTIMISATION, - ghInstance, - NULL); - - if (optimisations[i].flags&FLAG_SETINGUI) - Button_SetCheck(wnd, 1); - else - Button_SetCheck(wnd, 0); - - num++; - } - - CreateWindow("BUTTON","O0", - WS_CHILD | WS_VISIBLE, - 8,height-88,64,32, - optionsmenu, - (HMENU)IDI_O_LEVEL0, - ghInstance, - NULL); - CreateWindow("BUTTON","O1", - WS_CHILD | WS_VISIBLE, - 8+64,height-88,64,32, - optionsmenu, - (HMENU)IDI_O_LEVEL1, - ghInstance, - NULL); - CreateWindow("BUTTON","O2", - WS_CHILD | WS_VISIBLE, - 8+64*2,height-88,64,32, - optionsmenu, - (HMENU)IDI_O_LEVEL2, - ghInstance, - NULL); - CreateWindow("BUTTON","O3", - WS_CHILD | WS_VISIBLE, - 8+64*3,height-88,64,32, - optionsmenu, - (HMENU)IDI_O_LEVEL3, - ghInstance, - NULL); - CreateWindow("BUTTON","Debug", - WS_CHILD | WS_VISIBLE, - 8+64*4,height-88,64,32, - optionsmenu, - (HMENU)IDI_O_DEBUG, - ghInstance, - NULL); - CreateWindow("BUTTON","Default", - WS_CHILD | WS_VISIBLE, - 8+64*5,height-88,64,32, - optionsmenu, - (HMENU)IDI_O_DEFAULT, - ghInstance, - NULL); - CreateWindow("BUTTON","Apply", - WS_CHILD | WS_VISIBLE, - 8,height-40,64,32, - optionsmenu, - (HMENU)IDI_O_APPLY, - ghInstance, - NULL); - CreateWindow("BUTTON","Use", - WS_CHILD | WS_VISIBLE, - 8+64,height-40,64,32, - optionsmenu, - (HMENU)IDI_O_USE, - ghInstance, - NULL); - CreateWindow("BUTTON","progs.src", - WS_CHILD | WS_VISIBLE, - 8+64*2,height-40,64,32, - optionsmenu, - (HMENU)IDI_O_CHANGE_PROGS_SRC, - ghInstance, - NULL); - - - - y=4; - hexen2item = wnd = CreateWindow("BUTTON","HexenC", - WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX, - 408,y,200-16,16, - optionsmenu, - (HMENU)IDI_O_TARGET, - ghInstance, - NULL); - y+=16; - if (fl_hexen2) - Button_SetCheck(wnd, 1); - else - Button_SetCheck(wnd, 0); - - autohighlight_item = wnd = CreateWindow("BUTTON","Syntax Highlighting", - WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX, - 408,y,200-16,16, - optionsmenu, - (HMENU)IDI_O_SYNTAX_HIGHLIGHTING, - ghInstance, - NULL); - y+=16; - if (fl_autohighlight) - Button_SetCheck(wnd, 1); - else - Button_SetCheck(wnd, 0); - - x = 408; - my = y; - for (i = 0; compiler_flag[i].enabled; i++) - { - if (compiler_flag[i].flags & FLAG_HIDDENINGUI) - { - compiler_flag[i].guiinfo = NULL; - continue; - } - - if (y > height-(88+40)) - { - y = 4; - x += 168; - } - - compiler_flag[i].guiinfo = wnd = CreateWindow("BUTTON",compiler_flag[i].fullname, - WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX, - x,y,168,16, - optionsmenu, - (HMENU)IDI_O_COMPILER_FLAG, - ghInstance, - NULL); - y+=16; - - if (my < y) - my = y; - - if (compiler_flag[i].flags & FLAG_SETINGUI) - Button_SetCheck(wnd, 1); - else - Button_SetCheck(wnd, 0); - } - - CreateWindow("STATIC","Extra Parameters:", - WS_CHILD | WS_VISIBLE, - 408,my,200-16,16, - optionsmenu, - (HMENU)0, - ghInstance, - NULL); - my+=16; - extraparmsitem = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT",parameters, - WS_CHILD | WS_VISIBLE|ES_LEFT | ES_WANTRETURN | - ES_MULTILINE | ES_AUTOVSCROLL, - 408,my,r.right-r.left - 408 - 8,height-my-4, - optionsmenu, - (HMENU)IDI_O_ADDITIONALPARAMETERS, - ghInstance, - NULL); - - ShowWindow(optionsmenu, SW_SHOWDEFAULT); -} - - - - - - - - - - - -#undef printf - - - -static LONG CALLBACK MainWndProc(HWND hWnd,UINT message, - WPARAM wParam,LPARAM lParam) -{ - int width; - int i; - RECT rect; - HDC hdc; - PAINTSTRUCT ps; - switch (message) - { - case WM_CREATE: - { - CLIENTCREATESTRUCT ccs; - - HMENU rootmenu, windowmenu, m; - rootmenu = CreateMenu(); - - AppendMenu(rootmenu, MF_POPUP, (UINT_PTR)(m = CreateMenu()), "&File"); - AppendMenu(m, 0, IDM_OPENNEW, "Open &new file "); - AppendMenu(m, 0, IDM_SAVE, "&Save "); - // AppendMenu(m, 0, IDM_FIND, "&Find"); - AppendMenu(m, 0, IDM_UNDO, "&Undo Ctrl+Z"); - AppendMenu(m, 0, IDM_REDO, "&Redo Ctrl+Y"); - AppendMenu(rootmenu, MF_POPUP, (UINT_PTR)(m = CreateMenu()), "&Navigation"); - AppendMenu(m, 0, IDM_GOTODEF, "Go to definition"); - AppendMenu(m, 0, IDM_OPENDOCU, "Open selected file"); - AppendMenu(rootmenu, MF_POPUP, (UINT_PTR)(m = windowmenu = CreateMenu()), "&Window"); - AppendMenu(m, 0, IDM_CASCADE, "&Cascade"); - AppendMenu(m, 0, IDM_TILE_HORIZ, "Tile &Horizontally"); - AppendMenu(m, 0, IDM_TILE_VERT, "Tile &Vertically"); - AppendMenu(rootmenu, MF_POPUP, (UINT_PTR)(m = CreateMenu()), "&Help"); - AppendMenu(m, 0, IDM_ABOUT, "About"); - - SetMenu(hWnd, rootmenu); - - // Retrieve the handle to the window menu and assign the - // first child window identifier. - - memset(&ccs, 0, sizeof(ccs)); - ccs.hWindowMenu = windowmenu; - ccs.idFirstChild = IDM_FIRSTCHILD; - - // Create the MDI client window. - - mdibox = CreateWindow( "MDICLIENT", (LPCTSTR) NULL, - WS_CHILD | WS_CLIPCHILDREN | WS_VSCROLL | WS_HSCROLL, - 0, 0, 320, 200, hWnd, (HMENU) 0xCAC, ghInstance, (LPSTR) &ccs); - ShowWindow(mdibox, SW_SHOW); - - projecttree = CreateWindow(WC_TREEVIEW, (LPCTSTR) NULL, - WS_CHILD | WS_CLIPCHILDREN | WS_VSCROLL | WS_HSCROLL - | TVS_HASBUTTONS |TVS_LINESATROOT|TVS_HASLINES, - 0, 0, 320, 200, hWnd, (HMENU) 0xCAC, ghInstance, (LPSTR) &ccs); - ShowWindow(projecttree, SW_SHOW); - - if (projecttree) - { - gotodefbox = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", (LPCTSTR) NULL, - WS_CHILD | WS_CLIPCHILDREN, - 0, 0, 320, 200, hWnd, (HMENU) 0xCAC, ghInstance, (LPSTR) NULL); - ShowWindow(gotodefbox, SW_SHOW); - - gotodefaccept = CreateWindowEx(WS_EX_CLIENTEDGE, "BUTTON", "GO", - WS_CHILD | WS_CLIPCHILDREN | BS_DEFPUSHBUTTON, - 0, 0, 320, 200, hWnd, (HMENU) 0x4404, ghInstance, (LPSTR) NULL); - ShowWindow(gotodefaccept, SW_SHOW); - } - } - break; - - case WM_DESTROY: - mainwindow = NULL; - break; - - case WM_SIZE: - GetClientRect(mainwindow, &rect); - if (projecttree) - { - SetWindowPos(projecttree, NULL, 0, 0, 192, rect.bottom-rect.top - 34 - 24, 0); - - SetWindowPos(gotodefbox, NULL, 0, rect.bottom-rect.top - 33 - 24, 160, 24, 0); - SetWindowPos(gotodefaccept, NULL, 160, rect.bottom-rect.top - 33 - 24, 32, 24, 0); - SetWindowPos(mdibox?mdibox:outputbox, NULL, 192, 0, rect.right-rect.left-192, rect.bottom-rect.top - 32, 0); - } - else - SetWindowPos(mdibox?mdibox:outputbox, NULL, 0, 0, rect.right-rect.left, rect.bottom-rect.top - 32, 0); - width = (rect.right-rect.left); - width/=NUMBUTTONS; - for (i = 0; i < NUMBUTTONS; i++) - { - SetWindowPos(buttons[i].hwnd, NULL, width*i, rect.bottom-rect.top - 32, width, 32, 0); - } - break; - case WM_PAINT: - hdc=BeginPaint(hWnd,(LPPAINTSTRUCT)&ps); - - EndPaint(hWnd,(LPPAINTSTRUCT)&ps); - return TRUE; - break; - case WM_COMMAND: - if (wParam == 0x4404) - { - GetWindowText(gotodefbox, finddef, sizeof(finddef)-1); - return true; - } - if (LOWORD(wParam)>0 && LOWORD(wParam) <= NUMBUTTONS) - { - if (LOWORD(wParam)) - buttons[LOWORD(wParam)-1].washit = 1; - break; - } - if (LOWORD(wParam) < IDM_FIRSTCHILD) - { - HWND ew; - editor_t *editor; - - ew = (HWND)SendMessage(mdibox, WM_MDIGETACTIVE, 0, 0); - - for (editor = editors; editor; editor = editor->next) - { - if (editor->window == ew) - break; - } - if (editor) - EditorMenu(editor, wParam); - else - GenericMenu(wParam); - break; - } - break; - case WM_NOTIFY: - if (lParam) - { - NMHDR *nm; - HANDLE item; - TVITEM i; - char filename[256]; - char itemtext[256]; - int oldlen; - int newlen; - nm = (NMHDR*)lParam; - if (nm->hwndFrom == projecttree) - { - switch(nm->code) - { - case NM_DBLCLK: - item = TreeView_GetSelection(projecttree); - i.hItem = item; - i.mask = TVIF_TEXT; - i.pszText = itemtext; - i.cchTextMax = sizeof(itemtext)-1; - if (!TreeView_GetItem(projecttree, &i)) - return 0; - strcpy(filename, i.pszText); - while(item) - { - item = TreeView_GetParent(projecttree, item); - i.hItem = item; - if (!TreeView_GetItem(projecttree, &i)) - break; - if (!TreeView_GetParent(projecttree, item)) - break; - - oldlen = strlen(filename); - newlen = strlen(i.pszText); - memmove(filename+newlen+1, filename, oldlen+1); - filename[newlen] = '/'; - strncpy(filename, i.pszText, newlen); - } - EditFile(filename, -1); - break; - } - } - } - default: - if (mdibox) - return DefFrameProc(hWnd,mdibox,message,wParam,lParam); - else - return DefWindowProc(hWnd,message,wParam,lParam); - } - return 0; -} - -static LONG CALLBACK OutputWindowProc(HWND hWnd,UINT message, - WPARAM wParam,LPARAM lParam) -{ - RECT rect; - switch (message) - { - case WM_DESTROY: - outputwindow = NULL; - outputbox = NULL; - break; - case WM_CREATE: - outputbox = CreateAnEditControl(hWnd); - case WM_SIZE: - GetClientRect(hWnd, &rect); - SetWindowPos(outputbox, NULL, 0, 0, rect.right-rect.left, rect.bottom-rect.top, 0); - - default: - return DefMDIChildProc(hWnd,message,wParam,lParam); - } - return 0; -} - -void GUIPrint(HWND wnd, char *msg) -{ - MSG wmsg; - int len; - static int writing; - - if (writing) - return; - if (!mainwindow) - { - printf("%s", msg); - return; - } - writing=true; - len=Edit_GetTextLength(wnd); -/* if ((unsigned)len>(32767-strlen(msg))) - Edit_SetSel(wnd,0,len); - else*/ - Edit_SetSel(wnd,len,len); - Edit_ReplaceSel(wnd,msg); - - while (PeekMessage (&wmsg, NULL, 0, 0, PM_NOREMOVE)) - { - if (!GetMessage (&wmsg, NULL, 0, 0)) - break; - TranslateMessage (&wmsg); - DispatchMessage (&wmsg); - } - writing=false; -} -int GUIEmitOutputText(HWND wnd, int start, char *text, int len, DWORD colour) -{ - int c, cr; - CHARFORMAT cf; - - if (!len) - return start; - - c = text[len]; - text[len] = '\0'; - Edit_SetSel(wnd,start,start); - Edit_ReplaceSel(wnd,text); - - text[len] = c; - - cr = 0; - for (c = 0; c < len; c++) - if (text[c] == '\r') - cr++; - if (cr) - len-=cr; - - Edit_SetSel(wnd,start,start+len); - memset(&cf, 0, sizeof(cf)); - cf.cbSize = sizeof(cf); - cf.dwMask = CFM_COLOR; - cf.crTextColor = colour; - SendMessage(wnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf); - Edit_SetSel(wnd,start+len,start+len); - - return start + len; -} -int outlen; -int GUIprintf(const char *msg, ...) -{ - va_list argptr; - char buf[1024]; - char rn[3] = "\n"; - char *st, *s; - int args; - MSG wmsg; - - DWORD col; - - va_start (argptr,msg); - args = QC_vsnprintf (buf,sizeof(buf)-1, msg,argptr); - va_end (argptr); - - printf("%s", buf); - if (logfile) - fprintf(logfile, "%s", buf); - - if (!*buf) - { - SetWindowText(outputbox,""); - outlen = 0; - return 0; - } - - if (strstr(buf, "warning: ")) - col = RGB(128, 128, 0); - else if (strstr(buf, "error: ")) - col = RGB(255, 0, 0); - else - col = RGB(0, 0, 0); - - s = st = buf; - while(*s) - { - if (*s == '\n') - { - *s = '\0'; - if (*st) - outlen = GUIEmitOutputText(outputbox, outlen, st, strlen(st), col); - outlen = GUIEmitOutputText(outputbox, outlen, rn, 1, col); - st = s+1; - } - - s++; - } - if (*st) - outlen = GUIEmitOutputText(outputbox, outlen, st, strlen(st), col); - - while (PeekMessage (&wmsg, NULL, 0, 0, PM_NOREMOVE)) - { - if (!GetMessage (&wmsg, NULL, 0, 0)) - break; - TranslateMessage (&wmsg); - DispatchMessage (&wmsg); - } -/* - s = st = buf; - while(*s) - { - if (*s == '\n') - { - *s = '\0'; - if (*st) - GUIPrint(outputbox, st); - GUIPrint(outputbox, "\r\n"); - st = s+1; - } - - s++; - } - if (*st) - GUIPrint(outputbox, st); -*/ - return args; -} - -#undef Sys_Error - -void Sys_Error(const char *text, ...); -void RunCompiler(char *args) -{ - char *argv[128]; - int argc; - progexterns_t ext; - progfuncs_t funcs; - - editor_t *editor; - for (editor = editors; editor; editor = editor->next) - { - if (editor->modified) - { - if (EditorModified(editor)) - { - char msg[1024]; - sprintf(msg, "%s is modified in both memory and on disk. Overwrite external modification? (saying no will reload from disk)", editor->filename); - switch(MessageBox(NULL, msg, "Modification conflict", MB_YESNOCANCEL)) - { - case IDYES: - EditorSave(editor); - break; - case IDNO: - EditorReload(editor); - break; - case IDCANCEL: - break; /*compiling will use whatever is in memory*/ - } - } - else - { - /*not modified on disk, but modified in memory? try and save it, cos we might as well*/ - EditorSave(editor); - } - } - else - { - /*modified on disk but not in memory? just reload it off disk*/ - if (EditorModified(editor)) - EditorReload(editor); - } - } - - memset(&funcs, 0, sizeof(funcs)); - funcs.parms = &ext; - memset(&ext, 0, sizeof(ext)); - funcs.parms->ReadFile = GUIReadFile; - funcs.parms->FileSize = GUIFileSize; - funcs.parms->WriteFile = QCC_WriteFile; - funcs.parms->printf = GUIprintf; - funcs.parms->Sys_Error = Sys_Error; - GUIprintf(""); - - if (logfile) - fclose(logfile); - if (fl_log) - logfile = fopen("fteqcc.log", "wb"); - else - logfile = NULL; - - argc = GUI_BuildParms(args, argv); - - CompileParams(&funcs, true, argc, argv); - - if (logfile) - fclose(logfile); -} - - -void CreateOutputWindow(void) -{ - WNDCLASS wndclass; - MDICREATESTRUCT mcs; - - if (!mdibox) //should already be created - return; - - if (!outputwindow) - { - wndclass.style = 0; - wndclass.lpfnWndProc = (WNDPROC)OutputWindowProc; - wndclass.cbClsExtra = 0; - wndclass.cbWndExtra = 0; - wndclass.hInstance = ghInstance; - wndclass.hIcon = 0; - wndclass.hCursor = LoadCursor (NULL,IDC_ARROW); - wndclass.hbrBackground = (void *)COLOR_WINDOW; - wndclass.lpszMenuName = 0; - wndclass.lpszClassName = MAIN_WINDOW_CLASS_NAME; - RegisterClass(&wndclass); - - - - mcs.szClass = MAIN_WINDOW_CLASS_NAME; - mcs.szTitle = "Compiler output"; - mcs.hOwner = ghInstance; - mcs.x = mcs.cx = CW_USEDEFAULT; - mcs.y = mcs.cy = CW_USEDEFAULT; - mcs.style = WS_OVERLAPPEDWINDOW; - mcs.lParam = 0; - - outputwindow = (HWND) SendMessage (mdibox, WM_MDICREATE, 0, - (LONG_PTR) (LPMDICREATESTRUCT) &mcs); - - ShowWindow(outputwindow, SW_SHOW); - } - - //bring it to the front. - SendMessage(mdibox, WM_MDIACTIVATE, (WPARAM)outputwindow, 0); -} - -//progssrcname should already have been set. -void SetProgsSrc(void) -{ - FILE *f; - - HANDLE rootitem, pi; - TVINSERTSTRUCT item; - TV_ITEM parent; - char parentstring[256]; - memset(&item, 0, sizeof(item)); - memset(&parent, 0, sizeof(parent)); - - if (projecttree) - { - int size; - char *buffer; - char *slash; - - f = fopen (progssrcname, "rb"); - if (!f) - return; - fseek(f, 0, SEEK_END); - size = ftell(f); - fseek(f, 0, SEEK_SET); - buffer = malloc(size+1); - if (!buffer) - { - fclose(f); - return; - } - buffer[size] = '\0'; - fread(buffer, 1, size, f); - fclose(f); - - pr_file_p = QCC_COM_Parse(buffer); - if (*qcc_token == '#') - { - free(buffer); //aaaahhh! newstyle! - return; - } - - pr_file_p = QCC_COM_Parse(pr_file_p); //we dont care about the produced progs.dat - - - item.hParent = TVI_ROOT; - item.hInsertAfter = TVI_SORT; - item.item.pszText = progssrcname; - item.item.mask = TVIF_TEXT; - rootitem = (HANDLE)SendMessage(projecttree,TVM_INSERTITEM,0,(LPARAM)&item); - while(pr_file_p) - { - pi = item.hParent = rootitem; - item.item.pszText = qcc_token; - while(slash = strchr(item.item.pszText, '/')) - { - *slash = '\0'; - item.hParent = TreeView_GetChild(projecttree, item.hParent); - do - { - parent.hItem = item.hParent; - parent.mask = TVIF_TEXT; - parent.pszText = parentstring; - parent.cchTextMax = sizeof(parentstring)-1; - if (TreeView_GetItem(projecttree, &parent)) - { - if (!stricmp(parent.pszText, item.item.pszText)) - break; - } - } while(item.hParent=TreeView_GetNextSibling(projecttree, item.hParent)); - if (!item.hParent) - { //add a directory. - item.hParent = pi; - pi = (HANDLE)SendMessage(projecttree,TVM_INSERTITEM,0,(LPARAM)&item); - item.hParent = pi; - } - else pi = item.hParent; - - item.item.pszText = slash+1; - } - SendMessage(projecttree,TVM_INSERTITEM,0,(LPARAM)&item); - pr_file_p = QCC_COM_Parse(pr_file_p); - } - - free(buffer); - } -} - -int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) -{ - unsigned int i; - WNDCLASS wndclass; - ghInstance= hInstance; - - GUI_SetDefaultOpts(); - - if(strstr(lpCmdLine, "-stdout")) - { - GUI_ParseCommandLine(lpCmdLine); - RunCompiler(lpCmdLine); - return 0; - } - - if (!*lpCmdLine) - { - int len; - FILE *f; - char *s; - - f = fopen("fteqcc.cfg", "rb"); - if (f) - { - fseek(f, 0, SEEK_END); - len = ftell(f); - fseek(f, 0, SEEK_SET); - lpCmdLine = malloc(len+1); - fread(lpCmdLine, 1, len, f); - lpCmdLine[len] = '\0'; - fclose(f); - - while(s = strchr(lpCmdLine, '\r')) - *s = ' '; - while(s = strchr(lpCmdLine, '\n')) - *s = ' '; - } - } - - GUI_ParseCommandLine(lpCmdLine); - - GUI_RevealOptions(); - - if (/*!fl_acc &&*/ !*progssrcname) - { - strcpy(progssrcname, "preprogs.src"); - if (QCC_FileSize(progssrcname)==-1) - strcpy(progssrcname, "progs.src"); - if (QCC_FileSize(progssrcname)==-1) - { - char *s, *s2; - char filename[MAX_PATH]; - char oldpath[MAX_PATH+10]; - OPENFILENAME ofn; - memset(&ofn, 0, sizeof(ofn)); - ofn.lStructSize = sizeof(ofn); - ofn.hInstance = ghInstance; - ofn.lpstrFile = filename; - ofn.lpstrTitle = "Please find progs.src"; - ofn.nMaxFile = sizeof(filename)-1; - ofn.lpstrFilter = "QuakeC source\0*.src\0All files\0*.*\0"; - memset(filename, 0, sizeof(filename)); - GetCurrentDirectory(sizeof(oldpath)-1, oldpath); - ofn.lpstrInitialDir = oldpath; - if (GetOpenFileName(&ofn)) - { - strcpy(progssrcdir, filename); - for(s = progssrcdir; s; s = s2) - { - s2 = strchr(s+1, '\\'); - if (!s2) - break; - s = s2; - } - if (s) - { - *s = '\0'; - strcpy(progssrcname, s+1); - } - else - strcpy(progssrcname, filename); - } - else - { - MessageBox(NULL, "You didn't select a file", "Error", 0); - return 0; - } - SetCurrentDirectory(progssrcdir); - *progssrcdir = '\0'; - } - } - - resetprogssrc = true; - - wndclass.style = 0; - wndclass.lpfnWndProc = (WNDPROC)MainWndProc; - wndclass.cbClsExtra = 0; - wndclass.cbWndExtra = 0; - wndclass.hInstance = ghInstance; - wndclass.hIcon = 0; - wndclass.hCursor = LoadCursor (NULL,IDC_ARROW); - wndclass.hbrBackground = (void *)COLOR_WINDOW; - wndclass.lpszMenuName = 0; - wndclass.lpszClassName = MDI_WINDOW_CLASS_NAME; - RegisterClass(&wndclass); - - mainwindow=CreateWindow(MDI_WINDOW_CLASS_NAME, "FTE QuakeC compiler", WS_OVERLAPPEDWINDOW, - 0, 0, 640, 480, NULL, NULL, ghInstance, NULL); - - if (mdibox) - { - SetWindowText(mainwindow, "FTE QuakeC Development Suite"); - } - - if (!mainwindow) - { - MessageBox(NULL, "Failed to create main window", "Error", 0); - return 0; - } - - InitCommonControls(); -/* - outputbox=CreateWindowEx(WS_EX_CLIENTEDGE, - "EDIT", - "", - WS_CHILD | ES_READONLY | WS_VISIBLE | - WS_VSCROLL | ES_LEFT | ES_WANTRETURN | - ES_MULTILINE | ES_AUTOVSCROLL, - 0, 0, 0, 0, - mainwindow, - NULL, - ghInstance, - NULL); -*/ - - if (!mdibox) - outputbox = CreateAnEditControl(mainwindow); - - for (i = 0; i < NUMBUTTONS; i++) - { - buttons[i].hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, - "BUTTON", - buttons[i].text, - WS_CHILD | WS_VISIBLE, - 0, 0, 5, 5, - mainwindow, - (HMENU)(i+1), - ghInstance, - NULL); - } - - ShowWindow(mainwindow, SW_SHOWDEFAULT); - - if (fl_compileonstart) - { - CreateOutputWindow(); - RunCompiler(lpCmdLine); - } - else - { - if (mdibox) - { - buttons[ID_EDIT].washit = true; - } - else - { - GUIprintf("Welcome to FTE QCC\n"); - GUIprintf("Source file: "); - GUIprintf(progssrcname); - GUIprintf("\n"); - - RunCompiler("-?"); - } - } - - while(mainwindow || editors) - { - MSG msg; - - if (resetprogssrc) - { //this here, with the compiler below, means that we don't run recursivly. - resetprogssrc = false; - SetProgsSrc(); - } - - EditorsRun(); - - while (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) - { - if (!GetMessage (&msg, NULL, 0, 0)) - break; - if (!mdibox || !TranslateMDISysAccel(mdibox, &msg)) - { - TranslateMessage (&msg); - DispatchMessage (&msg); - } - } - - if (mainwindow) - { - i = Edit_GetSel(outputbox); - if ((i>>16) != (i&0xffff) && i != -1) //some text is selected. - { - int bytes; - char line[1024]; - char *colon1, *colon2 = NULL; - - int l1; - int l2; - - l1 = Edit_LineFromChar(outputbox, i&0xffff); - l2 = Edit_LineFromChar(outputbox, (i>>16)&0xffff); - if (l1 == l2) - { - bytes = Edit_GetLine(outputbox, Edit_LineFromChar(outputbox, i&0xffff), line, sizeof(line)-1); - line[bytes] = 0; - - for (colon1 = line+strlen(line)-1; *colon1 <= ' ' && colon1>=line; colon1--) - *colon1 = '\0'; - if (!strncmp(line, "warning: ", 9)) - memmove(line, line+9, sizeof(line)); - colon1=line; - do - { - colon1 = strchr(colon1+1, ':'); - } while (colon1 && colon1[1] == '\\'); - - if (colon1) - { - colon2 = strchr(colon1+1, ':'); - while (colon2 && colon2[1] == '\\') - { - colon2 = strchr(colon2+1, ':'); - } - if (colon2) - { - *colon1 = '\0'; - *colon2 = '\0'; - EditFile(line, atoi(colon1+1)-1); - } - else if (!strncmp(line, "Source file: ", 13)) - EditFile(line+13, -1); - else if (!strncmp(line, "Including: ", 11)) - EditFile(line+11, -1); - } - else if (!strncmp(line, "compiling ", 10)) - EditFile(line+10, -1); - else if (!strncmp(line, "prototyping ", 12)) - EditFile(line+12, -1); - Edit_SetSel(outputbox, i&0xffff, i&0xffff); //deselect it. - } - } - - if (buttons[ID_COMPILE].washit) - { - CreateOutputWindow(); - RunCompiler(parameters); - - buttons[ID_COMPILE].washit = false; - } - if (buttons[ID_EDIT].washit) - { - buttons[ID_EDIT].washit = false; - EditFile(progssrcname, -1); - } - if (buttons[ID_OPTIONS].washit) - { - buttons[ID_OPTIONS].washit = false; - OptionsDialog(); - } - if (buttons[ID_QUIT].washit) - { - buttons[ID_QUIT].washit = false; - DestroyWindow(mainwindow); - } - } - - if (*finddef) - { - GoToDefinition(finddef); - *finddef = '\0'; - } - - Sleep(10); - } - - return 0; -} diff --git a/misc/source/fteqcc-src/qccguistuff.c b/misc/source/fteqcc-src/qccguistuff.c deleted file mode 100644 index b9555312..00000000 --- a/misc/source/fteqcc-src/qccguistuff.c +++ /dev/null @@ -1,325 +0,0 @@ -#include "qcc.h" -#include "gui.h" - -//common gui things - -pbool fl_hexen2; -pbool fl_autohighlight; -pbool fl_compileonstart; -pbool fl_showall; -pbool fl_log; - -char parameters[16384]; -char progssrcname[256]; -char progssrcdir[256]; - -int qccpersisthunk = 1; -void GoToDefinition(char *name) -{ - QCC_def_t *def; - QCC_dfunction_t *fnc; - - char *strip; //trim whitespace (for convieniance). - while (*name <= ' ' && *name) - name++; - for (strip = name + strlen(name)-1; *strip; strip++) - { - if (*strip <= ' ') - *strip = '\0'; - else //got some part of a word - break; - } - - if (!globalstable.numbuckets) - { - GUI_DialogPrint("Not found", "You need to compile first."); - return; - } - - - def = QCC_PR_GetDef(NULL, name, NULL, false, 0, false); - - if (def) - { - if (def->type->type == ev_function && def->constant) - { - fnc = &functions[((int *)qcc_pr_globals)[def->ofs]]; - if (fnc->first_statement>=0 && fnc->s_file) - { - EditFile(fnc->s_file+strings, statement_linenums[fnc->first_statement]); - return; - } - } - if (!def->s_file) - GUI_DialogPrint("Not found", "Global definition of var was not specified."); - else - EditFile(def->s_file+strings, def->s_line-1); - } - else - GUI_DialogPrint("Not found", "Global instance of var was not found."); -} - - - - -//this function takes the windows specified commandline and strips out all the options menu items. -void GUI_ParseCommandLine(char *args) -{ - int paramlen=0; - int l, p; - char *next; - while(*args) - { - while (*args <= ' ' && *args) - args++; - - for (next = args; *next>' '; next++) - ; - - strncpy(parameters+paramlen, args, next-args); - parameters[paramlen+next-args] = '\0'; - l = strlen(parameters+paramlen)+1; - - if (!strnicmp(parameters+paramlen, "-O", 2) || !strnicmp(parameters+paramlen, "/O", 2)) - { //strip out all -O - if (parameters[paramlen+2]) - { - if (parameters[paramlen+2] >= '0' && parameters[paramlen+2] <= '3') - { - p = parameters[paramlen+2]-'0'; - for (l = 0; optimisations[l].enabled; l++) - { - if (optimisations[l].optimisationlevel<=p) - optimisations[l].flags |= FLAG_SETINGUI; - else - optimisations[l].flags &= ~FLAG_SETINGUI; - } - } - else if (!strncmp(parameters+paramlen+2, "no-", 3)) - { - if (parameters[paramlen+5]) - { - for (p = 0; optimisations[p].enabled; p++) - if ((*optimisations[p].abbrev && !strcmp(parameters+paramlen+5, optimisations[p].abbrev)) || !strcmp(parameters+paramlen+5, optimisations[p].fullname)) - { - optimisations[p].flags &= ~FLAG_SETINGUI; - break; - } - - if (!optimisations[p].enabled) - { - parameters[paramlen+next-args] = ' '; - paramlen += l; - } - } - } - else - { - for (p = 0; optimisations[p].enabled; p++) - if ((*optimisations[p].abbrev && !strcmp(parameters+paramlen+2, optimisations[p].abbrev)) || !strcmp(parameters+paramlen+2, optimisations[p].fullname)) - { - optimisations[p].flags |= FLAG_SETINGUI; - break; - } - - if (!optimisations[p].enabled) - { - parameters[paramlen+next-args] = ' '; - paramlen += l; - } - } - } - } -/* - else if (!strnicmp(parameters+paramlen, "-Fno-kce", 8) || !strnicmp(parameters+paramlen, "/Fno-kce", 8)) //keywords stuph - { - fl_nokeywords_coexist = true; - } - else if (!strnicmp(parameters+paramlen, "-Fkce", 5) || !strnicmp(parameters+paramlen, "/Fkce", 5)) - { - fl_nokeywords_coexist = false; - } - else if (!strnicmp(parameters+paramlen, "-Facc", 5) || !strnicmp(parameters+paramlen, "/Facc", 5)) - { - fl_acc = true; - } - else if (!strnicmp(parameters+paramlen, "-autoproto", 10) || !strnicmp(parameters+paramlen, "/autoproto", 10)) - { - fl_autoprototype = true; - } -*/ - else if (!strnicmp(parameters+paramlen, "-showall", 8) || !strnicmp(parameters+paramlen, "/showall", 8)) - { - fl_showall = true; - } - else if (!strnicmp(parameters+paramlen, "-ah", 3) || !strnicmp(parameters+paramlen, "/ah", 3)) - { - fl_autohighlight = true; - } - else if (!strnicmp(parameters+paramlen, "-ac", 3) || !strnicmp(parameters+paramlen, "/ac", 3)) - { - fl_compileonstart = true; - } - else if (!strnicmp(parameters+paramlen, "-log", 4) || !strnicmp(parameters+paramlen, "/log", 4)) - { - fl_log = true; - } - else if (!strnicmp(parameters+paramlen, "-srcfile", 8) || !strnicmp(parameters+paramlen, "/srcfile", 8)) - { - while (*next == ' ') - next++; - - l = 0; - while (*next != ' ' && *next) - progssrcname[l++] = *next++; - progssrcname[l] = 0; - } - else if (!strnicmp(parameters+paramlen, "-T", 2) || !strnicmp(parameters+paramlen, "/T", 2)) //the target - { - if (!strnicmp(parameters+paramlen+2, "h2", 2)) - { - fl_hexen2 = true; - } - else - { - fl_hexen2 = false; - parameters[paramlen+next-args] = ' '; - paramlen += l; - } - } - else - { - parameters[paramlen+next-args] = ' '; - paramlen += l; - } - - args=next; - } - if (paramlen) - parameters[paramlen-1] = '\0'; - else - *parameters = '\0'; -} - -void GUI_SetDefaultOpts(void) -{ - int i; - for (i = 0; compiler_flag[i].enabled; i++) //enabled is a pointer - { - if (compiler_flag[i].flags & FLAG_ASDEFAULT) - compiler_flag[i].flags |= FLAG_SETINGUI; - else - compiler_flag[i].flags &= ~FLAG_SETINGUI; - } - for (i = 0; optimisations[i].enabled; i++) //enabled is a pointer - { - if (optimisations[i].flags & FLAG_ASDEFAULT) - optimisations[i].flags |= FLAG_SETINGUI; - else - optimisations[i].flags &= ~FLAG_SETINGUI; - } -} - -void GUI_RevealOptions(void) -{ - int i; - for (i = 0; compiler_flag[i].enabled; i++) //enabled is a pointer - { - if (fl_showall && compiler_flag[i].flags & FLAG_HIDDENINGUI) - compiler_flag[i].flags &= ~FLAG_HIDDENINGUI; - } - for (i = 0; optimisations[i].enabled; i++) //enabled is a pointer - { - if (fl_showall && optimisations[i].flags & FLAG_HIDDENINGUI) - optimisations[i].flags &= ~FLAG_HIDDENINGUI; - - if (optimisations[i].flags & FLAG_HIDDENINGUI) //hidden optimisations are disabled as default - optimisations[i].optimisationlevel = 4; - } -} - - - - -int GUI_BuildParms(char *args, char **argv) -{ - static char param[2048]; - int paramlen = 0; - int argc; - char *next; - int i; - - - argc = 1; - argv[0] = "fteqcc"; - - while(*args) - { - while (*args <= ' '&& *args) - args++; - - for (next = args; *next>' '; next++) - ; - strncpy(param+paramlen, args, next-args); - param[paramlen+next-args] = '\0'; - argv[argc++] = param+paramlen; - paramlen += strlen(param+paramlen)+1; - - args=next; - } - - if (fl_hexen2) - { - strcpy(param+paramlen, "-Th2"); - argv[argc++] = param+paramlen; - paramlen += strlen(param+paramlen)+1; - } - - for (i = 0; optimisations[i].enabled; i++) //enabled is a pointer - { - if (optimisations[i].flags & FLAG_SETINGUI) - sprintf(param+paramlen, "-O%s", optimisations[i].abbrev); - else - sprintf(param+paramlen, "-Ono-%s", optimisations[i].abbrev); - argv[argc++] = param+paramlen; - paramlen += strlen(param+paramlen)+1; - } - - for (i = 0; compiler_flag[i].enabled; i++) //enabled is a pointer - { - if (compiler_flag[i].flags & FLAG_SETINGUI) - sprintf(param+paramlen, "-F%s", compiler_flag[i].abbrev); - else - sprintf(param+paramlen, "-Fno-%s", compiler_flag[i].abbrev); - argv[argc++] = param+paramlen; - paramlen += strlen(param+paramlen)+1; - } - - -/* while(*args) - { - while (*args <= ' '&& *args) - args++; - - for (next = args; *next>' '; next++) - ; - strncpy(param+paramlen, args, next-args); - param[paramlen+next-args] = '\0'; - argv[argc++] = param+paramlen; - paramlen += strlen(param+paramlen)+1; - args=next; - }*/ - - if (*progssrcname) - { - argv[argc++] = "-srcfile"; - argv[argc++] = progssrcname; - } - if (*progssrcdir) - { - argv[argc++] = "-src"; - argv[argc++] = progssrcdir; - } - - return argc; -} diff --git a/misc/source/fteqcc-src/qccmain.c b/misc/source/fteqcc-src/qccmain.c deleted file mode 100644 index e19b9430..00000000 --- a/misc/source/fteqcc-src/qccmain.c +++ /dev/null @@ -1,3654 +0,0 @@ -#ifndef MINIMAL - -#define PROGSUSED -#include "qcc.h" -#include -#ifdef _WIN32 -#include -#endif - -#include "errno.h" - -char QCC_copyright[1024]; -int QCC_packid; -char QCC_Packname[5][128]; - -extern int optres_test1; -extern int optres_test2; - -int writeasm; -static pbool pr_werror; -pbool verbose; - - -pbool QCC_PR_SimpleGetToken (void); -void QCC_PR_LexWhitespace (void); - -void *FS_ReadToMem(char *fname, void *membuf, int *len); -void FS_CloseFromMem(void *mem); - -unsigned int MAX_REGS; - -int MAX_STRINGS; -int MAX_GLOBALS; -int MAX_FIELDS; -int MAX_STATEMENTS; -int MAX_FUNCTIONS; -int MAX_CONSTANTS; -int max_temps; - -int *qcc_tempofs; -int tempsstart; -int numtemps; - -#define MAXSOURCEFILESLIST 8 -char sourcefileslist[MAXSOURCEFILESLIST][1024]; -int currentsourcefile; -int numsourcefiles; - -void QCC_PR_ResetErrorScope(void); - -pbool compressoutput; - -pbool newstylesource; -char destfile[1024]; - -float *qcc_pr_globals; -unsigned int numpr_globals; - -char *strings; -int strofs; - -QCC_dstatement_t *statements; -int numstatements; -int *statement_linenums; - -QCC_dfunction_t *functions; -int numfunctions; - -QCC_ddef_t *qcc_globals; -int numglobaldefs; - -QCC_ddef_t *fields; -int numfielddefs; - -//typedef char PATHSTRING[MAX_DATA_PATH]; - -PATHSTRING *precache_sounds; -int *precache_sounds_block; -int *precache_sounds_used; -int numsounds; - -PATHSTRING *precache_textures; -int *precache_textures_block; -int *precache_textures_block; -int numtextures; - -PATHSTRING *precache_models; -int *precache_models_block; -int *precache_models_used; -int nummodels; - -PATHSTRING *precache_files; -int *precache_files_block; -int numfiles; - -extern int numCompilerConstants; -hashtable_t compconstantstable; -hashtable_t globalstable; -hashtable_t localstable; -#ifdef WRITEASM -FILE *asmfile; -#endif -hashtable_t floatconstdefstable; -hashtable_t stringconstdefstable; -hashtable_t stringconstdefstable_trans; -extern int dotranslate_count; - -pbool qccwarningdisabled[WARN_MAX]; - -qcc_targetformat_t qcc_targetformat; - -pbool bodylessfuncs; - -QCC_type_t *qcc_typeinfo; -int numtypeinfos; -int maxtypeinfos; - - -struct { - char *name; - int index; -} warningnames[] = -{ - {"Q302", WARN_NOTREFERENCED}, -// {"", WARN_NOTREFERENCEDCONST}, -// {"", WARN_CONFLICTINGRETURNS}, - {"Q105", WARN_TOOFEWPARAMS}, - {"Q101", WARN_TOOMANYPARAMS}, -// {"", WARN_UNEXPECTEDPUNCT}, - {"Q106", WARN_ASSIGNMENTTOCONSTANT}, - {"Q203", WARN_MISSINGRETURNVALUE}, - {"Q204", WARN_WRONGRETURNTYPE}, - {"Q205", WARN_POINTLESSSTATEMENT}, - {"Q206", WARN_MISSINGRETURN}, - {"Q207", WARN_DUPLICATEDEFINITION}, - {"Q100", WARN_PRECOMPILERMESSAGE}, -// {"", WARN_STRINGTOOLONG}, -// {"", WARN_BADTARGET}, - {"Q120", WARN_BADPRAGMA}, -// {"", WARN_HANGINGSLASHR}, -// {"", WARN_NOTDEFINED}, -// {"", WARN_SWITCHTYPEMISMATCH}, -// {"", WARN_CONFLICTINGUNIONMEMBER}, -// {"", WARN_KEYWORDDISABLED}, -// {"", WARN_ENUMFLAGS_NOTINTEGER}, -// {"", WARN_ENUMFLAGS_NOTBINARY}, -// {"", WARN_CASEINSENSATIVEFRAMEMACRO}, - {"Q111", WARN_DUPLICATELABEL}, - {"Q201", WARN_ASSIGNMENTINCONDITIONAL}, - {"F300", WARN_DEADCODE}, - {NULL} -}; - -int QCC_WarningForName(char *name) -{ - int i; - for (i = 0; warningnames[i].name; i++) - { - if (!stricmp(name, warningnames[i].name)) - return warningnames[i].index; - } - return -1; -} - -optimisations_t optimisations[] = -{ - //level 0 = no optimisations - //level 1 = size optimisations - //level 2 = speed optimisations - //level 3 = dodgy optimisations. - //level 4 = experimental... - - {&opt_assignments, "t", 1, FLAG_ASDEFAULT, "assignments", "c = a*b is performed in one operation rather than two, and can cause older decompilers to fail."}, - {&opt_shortenifnots, "i", 1, FLAG_ASDEFAULT, "shortenifs", "if (!a) was traditionally compiled in two statements. This optimisation does it in one, but can cause some decompilers to get confused."}, - {&opt_nonvec_parms, "p", 1, FLAG_ASDEFAULT, "nonvec_parms", "In the original qcc, function parameters were specified as a vector store even for floats. This fixes that."}, - {&opt_constant_names, "c", 2, FLAG_KILLSDEBUGGERS, "constant_names", "This optimisation strips out the names of constants (but not strings) from your progs, resulting in smaller files. It makes decompilers leave out names or fabricate numerical ones."}, - {&opt_constant_names_strings, "cs", 3, FLAG_KILLSDEBUGGERS, "constant_names_strings", "This optimisation strips out the names of string constants from your progs. However, this can break addons, so don't use it in those cases."}, - {&opt_dupconstdefs, "d", 1, FLAG_ASDEFAULT, "dupconstdefs", "This will merge definitions of constants which are the same value. Pay extra attention to assignment to constant warnings."}, - {&opt_noduplicatestrings, "s", 1, 0, "noduplicatestrings", "This will compact the string table that is stored in the progs. It will be considerably smaller with this."}, - {&opt_locals, "l", 1, FLAG_KILLSDEBUGGERS, "locals", "Strips out local names and definitions. This makes it REALLY hard to decompile"}, - {&opt_function_names, "n", 1, FLAG_KILLSDEBUGGERS, "function_names", "This strips out the names of functions which are never called. Doesn't make much of an impact though."}, - {&opt_filenames, "f", 1, FLAG_KILLSDEBUGGERS, "filenames", "This strips out the filenames of the progs. This can confuse the really old decompilers, but is nothing to the more recent ones."}, - {&opt_unreferenced, "u", 1, FLAG_ASDEFAULT, "unreferenced", "Removes the entries of unreferenced variables. Doesn't make a difference in well maintained code."}, - {&opt_overlaptemps, "r", 1, FLAG_ASDEFAULT, "overlaptemps", "Optimises the pr_globals count by overlapping temporaries. In QC, every multiplication, division or operation in general produces a temporary variable. This optimisation prevents excess, and in the case of Hexen2's gamecode, reduces the count by 50k. This is the most important optimisation, ever."}, - {&opt_constantarithmatic, "a", 1, FLAG_ASDEFAULT, "constantarithmatic", "5*6 actually emits an operation into the progs. This prevents that happening, effectivly making the compiler see 30"}, - {&opt_precache_file, "pf", 2, 0, "precache_file", "Strip out stuff wasted used in function calls and strings to the precache_file builtin (which is actually a stub in quake)."}, - {&opt_return_only, "ro", 3, FLAG_KILLSDEBUGGERS, "return_only", "Functions ending in a return statement do not need a done statement at the end of the function. This can confuse some decompilers, making functions appear larger than they were."}, - {&opt_compound_jumps, "cj", 3, FLAG_KILLSDEBUGGERS, "compound_jumps", "This optimisation plays an effect mostly with nested if/else statements, instead of jumping to an unconditional jump statement, it'll jump to the final destination instead. This will bewilder decompilers."}, -// {&opt_comexprremoval, "cer", 4, 0, "expression_removal", "Eliminate common sub-expressions"}, //this would be too hard... - {&opt_stripfunctions, "sf", 4, 0, "strip_functions", "Strips out the 'defs' of functions that were only ever called directly. This does not affect saved games. This can affect FTE_MULTIPROGS."}, - {&opt_locals_marshalling, "lm", 4, FLAG_KILLSDEBUGGERS, "locals_marshalling", "Store all locals in one section of the pr_globals. Vastly reducing it. This effectivly does the job of overlaptemps. It's been noticed as buggy by a few, however, and the curcumstances where it causes problems are not yet known."}, - {&opt_vectorcalls, "vc", 4, FLAG_KILLSDEBUGGERS, "vectorcalls", "Where a function is called with just a vector, this causes the function call to store three floats instead of one vector. This can save a good number of pr_globals where those vectors contain many duplicate coordinates but do not match entirly."}, - {NULL} -}; - -#define defaultkeyword FLAG_HIDDENINGUI|FLAG_ASDEFAULT|FLAG_MIDCOMPILE -#define nondefaultkeyword FLAG_HIDDENINGUI|0|FLAG_MIDCOMPILE -//global to store useage to, flags, codename, human-readable name, help text -compiler_flag_t compiler_flag[] = { - //keywords - {&keyword_asm, defaultkeyword, "asm", "Keyword: asm", "Disables the 'asm' keyword. Use the writeasm flag to see an example of the asm."}, - {&keyword_break, defaultkeyword, "break", "Keyword: break", "Disables the 'break' keyword."}, - {&keyword_case, defaultkeyword, "case", "Keyword: case", "Disables the 'case' keyword."}, - {&keyword_class, defaultkeyword, "class", "Keyword: class", "Disables the 'class' keyword."}, - {&keyword_const, defaultkeyword, "const", "Keyword: const", "Disables the 'const' keyword."}, - {&keyword_continue, defaultkeyword, "continue", "Keyword: continue", "Disables the 'continue' keyword."}, - {&keyword_default, defaultkeyword, "default", "Keyword: default", "Disables the 'default' keyword."}, - {&keyword_entity, defaultkeyword, "entity", "Keyword: entity", "Disables the 'entity' keyword."}, - {&keyword_enum, defaultkeyword, "enum", "Keyword: enum", "Disables the 'enum' keyword."}, //kinda like in c, but typedef not supported. - {&keyword_enumflags, defaultkeyword, "enumflags", "Keyword: enumflags", "Disables the 'enumflags' keyword."}, //like enum, but doubles instead of adds 1. - {&keyword_extern, defaultkeyword, "extern", "Keyword: extern", "Disables the 'extern' keyword. Use only on functions inside addons."}, //function is external, don't error or warn if the body was not found - {&keyword_float, defaultkeyword, "float", "Keyword: float", "Disables the 'float' keyword. (Disables the float keyword without 'local' preceeding it)"}, - {&keyword_for, defaultkeyword, "for", "Keyword: for", "Disables the 'for' keyword. Syntax: for(assignment; while; increment) {codeblock;}"}, - {&keyword_goto, defaultkeyword, "goto", "Keyword: goto", "Disables the 'goto' keyword."}, - {&keyword_int, defaultkeyword, "int", "Keyword: int", "Disables the 'int' keyword."}, - {&keyword_integer, defaultkeyword, "integer", "Keyword: integer", "Disables the 'integer' keyword."}, - {&keyword_noref, defaultkeyword, "noref", "Keyword: noref", "Disables the 'noref' keyword."}, //nowhere else references this, don't strip it. - {&keyword_nosave, defaultkeyword, "nosave", "Keyword: nosave", "Disables the 'nosave' keyword."}, //don't write the def to the output. - {&keyword_shared, defaultkeyword, "shared", "Keyword: shared", "Disables the 'shared' keyword."}, //mark global to be copied over when progs changes (part of FTE_MULTIPROGS) - {&keyword_state, nondefaultkeyword,"state", "Keyword: state", "Disables the 'state' keyword."}, - {&keyword_optional, defaultkeyword,"optional", "Keyword: optional", "Disables the 'optional' keyword."}, - {&keyword_string, defaultkeyword, "string", "Keyword: string", "Disables the 'string' keyword."}, - {&keyword_struct, defaultkeyword, "struct", "Keyword: struct", "Disables the 'struct' keyword."}, - {&keyword_switch, defaultkeyword, "switch", "Keyword: switch", "Disables the 'switch' keyword."}, - {&keyword_thinktime, nondefaultkeyword,"thinktime", "Keyword: thinktime", "Disables the 'thinktime' keyword which is used in HexenC"}, - {&keyword_typedef, defaultkeyword, "typedef", "Keyword: typedef", "Disables the 'typedef' keyword."}, //fixme - {&keyword_union, defaultkeyword, "union", "Keyword: union", "Disables the 'union' keyword."}, //you surly know what a union is! - {&keyword_var, defaultkeyword, "var", "Keyword: var", "Disables the 'var' keyword."}, - {&keyword_vector, defaultkeyword, "vector", "Keyword: vector", "Disables the 'vector' keyword."}, - - - //options - {&keywords_coexist, FLAG_ASDEFAULT, "kce", "Keywords Coexist", "If you want keywords to NOT be disabled when they a variable by the same name is defined, check here."}, - {&output_parms, 0, "parms", "Define offset parms", "if PARM0 PARM1 etc should be defined by the compiler. These are useful if you make use of the asm keyword for function calls, or you wish to create your own variable arguments. This is an easy way to break decompilers."}, //controls weather to define PARMx for the parms (note - this can screw over some decompilers) - {&autoprototype, 0, "autoproto", "Automatic Prototyping","Causes compilation to take two passes instead of one. The first pass, only the definitions are read. The second pass actually compiles your code. This means you never have to remember to prototype functions again."}, //so you no longer need to prototype functions and things in advance. - {&writeasm, 0, "wasm", "Dump Assembler", "Writes out a qc.asm which contains all your functions but in assembler. This is a great way to look for bugs in fteqcc, but can also be used to see exactly what your functions turn into, and thus how to optimise statements better."}, //spit out a qc.asm file, containing an assembler dump of the ENTIRE progs. (Doesn't include initialisation of constants) - {&flag_ifstring, FLAG_MIDCOMPILE,"ifstring", "if(string) fix", "Causes if(string) to behave identically to if(string!="") This is most useful with addons of course, but also has adverse effects with FRIK_FILE's fgets, where it becomes impossible to determin the end of the file. In such a case, you can still use asm {IF string 2;RETURN} to detect eof and leave the function."}, //correction for if(string) no-ifstring to get the standard behaviour. - {&flag_iffloat, FLAG_MIDCOMPILE,"iffloat","if(-0.0) fix","Fixes certain floating point logic."}, - {&flag_acc, 0, "acc", "Reacc support", "Reacc is a pascall like compiler. It was released before the Quake source was released. This flag has a few effects. It sorts all qc files in the current directory into alphabetical order to compile them. It also allows Reacc global/field distinctions, as well as allows ¦ as EOF. Whilst case insensativity and lax type checking are supported by reacc, they are seperate compiler flags in fteqcc."}, //reacc like behaviour of src files. - {&flag_caseinsensative, 0, "caseinsens", "Case insensativity", "Causes fteqcc to become case insensative whilst compiling names. It's generally not advised to use this as it compiles a little more slowly and provides little benefit. However, it is required for full reacc support."}, //symbols will be matched to an insensative case if the specified case doesn't exist. This should b usable for any mod - {&flag_laxcasts, FLAG_MIDCOMPILE,"lax", "Lax type checks", "Disables many errors (generating warnings instead) when function calls or operations refer to two normally incompatible types. This is required for reacc support, and can also allow certain (evil) mods to compile that were originally written for frikqcc."}, //Allow lax casting. This'll produce loadsa warnings of course. But allows compilation of certain dodgy code. - {&flag_hashonly, FLAG_MIDCOMPILE,"hashonly", "Hash-only constants", "Allows use of only #constant for precompiler constants, allows certain preqcc using mods to compile"}, - {&opt_logicops, FLAG_MIDCOMPILE,"lo", "Logic ops", "This changes the behaviour of your code. It generates additional if operations to early-out in if statements. With this flag, the line if (0 && somefunction()) will never call the function. It can thus be considered an optimisation. However, due to the change of behaviour, it is not considered so by fteqcc. Note that due to inprecisions with floats, this flag can cause runaway loop errors within the player walk and run functions (without iffloat also enabled). This code is advised:\nplayer_stand1:\n if (self.velocity_x || self.velocity_y)\nplayer_run\n if (!(self.velocity_x || self.velocity_y))"}, - {&flag_msvcstyle, FLAG_MIDCOMPILE,"msvcstyle", "MSVC-style errors", "Generates warning and error messages in a format that msvc understands, to facilitate ide integration."}, - {&flag_filetimes, 0, "filetimes", "Check Filetimes", "Recompiles the progs only if the file times are modified."}, - {&flag_fasttrackarrays, FLAG_MIDCOMPILE|FLAG_ASDEFAULT,"fastarrays","fast arrays where possible", "Generates extra instructions inside array handling functions to detect engine and use extension opcodes only in supporting engines.\nAdds a global which is set by the engine if the engine supports the extra opcodes. Note that this applies to all arrays or none."}, - {&flag_assume_integer, FLAG_MIDCOMPILE,"assumeint", "Assume Integers", "Numerical constants are assumed to be integers, instead of floats."}, - {&pr_subscopedlocals, FLAG_MIDCOMPILE,"subscope", "Subscoped Locals", "Restrict the scope of locals to the block they are actually defined within, as in C."}, - {&verbose, FLAG_MIDCOMPILE,"verbose", "Verbose", "Lots of extra compiler messages."}, - {&flag_typeexplicit, FLAG_MIDCOMPILE,"typeexplicit", "Explicit types", "All type conversions must be explicit or directly supported by instruction set."}, - {NULL} -}; - -struct { - qcc_targetformat_t target; - char *name; -} targets[] = { - {QCF_STANDARD, "standard"}, - {QCF_STANDARD, "q1"}, - {QCF_STANDARD, "quakec"}, - {QCF_HEXEN2, "hexen2"}, - {QCF_HEXEN2, "h2"}, - {QCF_KK7, "kkqwsv"}, - {QCF_KK7, "kk7"}, - {QCF_KK7, "bigprogs"}, - {QCF_KK7, "version7"}, - {QCF_KK7, "kkqwsv"}, - {QCF_FTE, "fte"}, - {QCF_DARKPLACES,"darkplaces"}, - {QCF_DARKPLACES,"dp"}, - {QCF_QTEST, "qtest"}, - {0, NULL} -}; - -/* -================= -BspModels - -Runs qbsp and light on all of the models with a .bsp extension -================= -*/ -int QCC_CheckParm (char *check); - -void QCC_BspModels (void) -{ -/* - int p; - char *gamedir; - int i; - char *m; - char cmd[1024]; - char name[256]; - size_t result; - - p = QCC_CheckParm ("-bspmodels"); - if (!p) - return; - if (p == myargc-1) - QCC_Error (ERR_BADPARMS, "-bspmodels must preceed a game directory"); - gamedir = myargv[p+1]; - - for (i=0 ; istrings ; s--) - if (!strcmp(s, str)) - return s-strings; - - old = strofs; - strcpy (strings+strofs, str); - strofs += strlen(str)+1; - return old; -} - -void QCC_PrintStrings (void) -{ - int i, l, j; - - for (i=0 ; is_file, strings + d->s_name, d->first_statement, d->parm_start); - for (j=0 ; jnumparms ; j++) - printf ("%i ",d->parm_size[j]); - printf (")\n"); - } -}*/ - -void QCC_PrintFields (void) -{ - int i; - QCC_ddef_t *d; - - for (i=0 ; iofs, d->type, strings + d->s_name); - } -} - -void QCC_PrintGlobals (void) -{ - int i; - QCC_ddef_t *d; - - for (i=0 ; iofs, d->type, strings + d->s_name); - } -} - -int encode(int len, int method, char *in, int handle); -int WriteSourceFiles(int h, dprograms_t *progs, pbool sourceaswell) -{ - includeddatafile_t *idf; - qcc_cachedsourcefile_t *f; - int num=0; - int ofs; - - /* - for (f = qcc_sourcefile; f ; f=f->next) - { - if (f->type == FT_CODE && !sourceaswell) - continue; - - SafeWrite(h, f->filename, strlen(f->filename)+1); - i = PRLittleLong(f->size); - SafeWrite(h, &i, sizeof(int)); - - i = PRLittleLong(encrpytmode); - SafeWrite(h, &i, sizeof(int)); - - if (encrpytmode) - for (i = 0; i < f->size; i++) - f->file[i] ^= 0xA5; - - SafeWrite(h, f->file, f->size); - }*/ - - for (f = qcc_sourcefile,num=0; f ; f=f->next) - { - if (f->type == FT_CODE && !sourceaswell) - continue; - - num++; - } - if (!num) - return 0; - idf = qccHunkAlloc(sizeof(includeddatafile_t)*num); - for (f = qcc_sourcefile,num=0; f ; f=f->next) - { - if (f->type == FT_CODE && !sourceaswell) - continue; - - strcpy(idf[num].filename, f->filename); - idf[num].size = f->size; -#ifdef AVAIL_ZLIB - idf[num].compmethod = 2; -#else - idf[num].compmethod = 1; -#endif - idf[num].ofs = SafeSeek(h, 0, SEEK_CUR); - idf[num].compsize = QC_encode(progfuncs, f->size, idf[num].compmethod, f->file, h); - num++; - } - - ofs = SafeSeek(h, 0, SEEK_CUR); - SafeWrite(h, &num, sizeof(int)); - SafeWrite(h, idf, sizeof(includeddatafile_t)*num); - - qcc_sourcefile = NULL; - - return ofs; -} - -void QCC_InitData (void) -{ - static char parmname[12][MAX_PARMS]; - static temp_t ret_temp; - int i; - - qcc_sourcefile = NULL; - - numstatements = 1; - strofs = 2; - numfunctions = 1; - numglobaldefs = 1; - numfielddefs = 1; - - memset(&ret_temp, 0, sizeof(ret_temp)); - - def_ret.ofs = OFS_RETURN; - def_ret.name = "return"; - def_ret.temp = &ret_temp; - def_ret.constant = false; - def_ret.type = NULL; - ret_temp.ofs = def_ret.ofs; - ret_temp.scope = NULL; - ret_temp.size = 3; - ret_temp.next = NULL; - for (i=0 ; inext) - { - if (d->type->type == ev_function && !d->scope)// function parms are ok - { - if (!(d->initialized & 1) && d->references>0) - { - SafeWrite(handle, d->name, strlen(d->name)+1); - ret++; - } - } - } - - return ret; -} - -//marshalled locals remaps all the functions to use the range MAX_REGS onwards for the offset to their locals. -//this function remaps all the locals back into the function. -void QCC_UnmarshalLocals(void) -{ - QCC_def_t *def; - unsigned int ofs; - unsigned int maxo; - int i; - - ofs = numpr_globals; - maxo = ofs; - - for (def = pr.def_head.next ; def ; def = def->next) - { - if (def->ofs >= MAX_REGS) //unmap defs. - { - def->ofs = def->ofs + ofs - MAX_REGS; - if (maxo < def->ofs) - maxo = def->ofs; - } - } - - for (i = 0; i < numfunctions; i++) - { - if (functions[i].parm_start == MAX_REGS) - functions[i].parm_start = ofs; - } - - QCC_RemapOffsets(0, numstatements, MAX_REGS, MAX_REGS + maxo-numpr_globals + 3, ofs); - - numpr_globals = maxo+3; - if (numpr_globals > MAX_REGS) - QCC_Error(ERR_TOOMANYGLOBALS, "Too many globals are in use to unmarshal all locals"); - - if (maxo-ofs) - printf("Total of %i marshalled globals\n", maxo-ofs); -} - -CompilerConstant_t *QCC_PR_CheckCompConstDefined(char *def); -pbool QCC_WriteData (int crc) -{ - char element[MAX_NAME]; - QCC_def_t *def, *comp_x, *comp_y, *comp_z; - QCC_ddef_t *dd; - dprograms_t progs; - int h; - int i, len; - pbool debugtarget = false; - pbool types = false; - int outputsttype = PST_DEFAULT; - pbool warnedunref = false; - - if (numstatements==1 && numfunctions==1 && numglobaldefs==1 && numfielddefs==1) - { - printf("nothing to write\n"); - return false; - } - - progs.blockscompressed=0; - - if (numstatements > MAX_STATEMENTS) - QCC_Error(ERR_TOOMANYSTATEMENTS, "Too many statements - %i\nAdd \"MAX_STATEMENTS\" \"%i\" to qcc.cfg", numstatements, (numstatements+32768)&~32767); - - if (strofs > MAX_STRINGS) - QCC_Error(ERR_TOOMANYSTRINGS, "Too many strings - %i\nAdd \"MAX_STRINGS\" \"%i\" to qcc.cfg", strofs, (strofs+32768)&~32767); - - QCC_UnmarshalLocals(); - - switch (qcc_targetformat) - { - case QCF_HEXEN2: - case QCF_STANDARD: - if (bodylessfuncs) - printf("Warning: There are some functions without bodies.\n"); - - if (numpr_globals > 65530 ) - { - printf("Forcing target to FTE32 due to numpr_globals\n"); - outputsttype = PST_FTE32; - } - else if (qcc_targetformat == QCF_HEXEN2) - { - printf("Progs execution requires a Hexen2 compatible engine\n"); - break; - } - else - { - if (numpr_globals >= 32768) //not much of a different format. Rewrite output to get it working on original executors? - printf("An enhanced executor will be required (FTE/QF/KK)\n"); - else - printf("Progs should run on any Quake executor\n"); - break; - } - //intentional - qcc_targetformat = QCF_FTE; - case QCF_FTEDEBUG: - case QCF_FTE: - case QCF_DARKPLACES: - if (qcc_targetformat == QCF_FTEDEBUG) - debugtarget = true; - - if (numpr_globals > 65530) - { - printf("Using 32 bit target due to numpr_globals\n"); - outputsttype = PST_FTE32; - } - - if (qcc_targetformat == QCF_DARKPLACES) - compressoutput = 0; - - - //compression of blocks? - if (compressoutput) progs.blockscompressed |=1; //statements - if (compressoutput) progs.blockscompressed |=2; //defs - if (compressoutput) progs.blockscompressed |=4; //fields - if (compressoutput) progs.blockscompressed |=8; //functions - if (compressoutput) progs.blockscompressed |=16; //strings - if (compressoutput) progs.blockscompressed |=32; //globals - if (compressoutput) progs.blockscompressed |=64; //line numbers - if (compressoutput) progs.blockscompressed |=128; //types - //include a type block? - types = debugtarget; - if (types && sizeof(char *) != sizeof(string_t)) - { - //qcc_typeinfo_t has a char* inside it, which changes size - printf("AMD64 builds cannot write typeinfo structures\n"); - types = false; - } - - if (verbose) - { - if (qcc_targetformat == QCF_DARKPLACES) - printf("DarkPlaces or FTE will be required\n"); - else - printf("An FTE executor will be required\n"); - } - break; - case QCF_KK7: - if (bodylessfuncs) - printf("Warning: There are some functions without bodies.\n"); - if (numpr_globals > 65530) - printf("Warning: Saving is not supported. Ensure all engine read fields and globals are defined early on.\n"); - - printf("A KK compatible executor will be required (FTE/KK)\n"); - outputsttype = PST_KKQWSV; - break; - case QCF_QTEST: - printf("Compiled QTest progs will most likely not work at all. YOU'VE BEEN WARNED!\n"); - outputsttype = PST_QTEST; - break; - default: - Sys_Error("invalid progs type chosen!"); - } - - //part of how compilation works. This def is always present, and never used. - def = QCC_PR_GetDef(NULL, "end_sys_globals", NULL, false, 0, false); - if (def) - def->references++; - - def = QCC_PR_GetDef(NULL, "end_sys_fields", NULL, false, 0, false); - if (def) - def->references++; - - for (def = pr.def_head.next ; def ; def = def->next) - { - if ((def->type->type == ev_struct || def->type->type == ev_union || def->arraysize) && def->deftail) - { - QCC_def_t *d; - d = def; - while (d != def->deftail) - { - d = d->next; - h = d->references; - d->references += def->references; - def->references += h; - } - } - if (def->type->type == ev_vector || (def->type->type == ev_field && def->type->aux_type->type == ev_vector)) - { //do the references, so we don't get loadsa not referenced VEC_HULL_MINS_x - sprintf(element, "%s_x", def->name); - comp_x = QCC_PR_GetDef(NULL, element, def->scope, false, 0, false); - sprintf(element, "%s_y", def->name); - comp_y = QCC_PR_GetDef(NULL, element, def->scope, false, 0, false); - sprintf(element, "%s_z", def->name); - comp_z = QCC_PR_GetDef(NULL, element, def->scope, false, 0, false); - - h = def->references; - if (comp_x && comp_y && comp_z) - { - h += comp_x->references; - h += comp_y->references; - h += comp_z->references; - - if (!def->references) - if (!comp_x->references || !comp_y->references || !comp_z->references) //one of these vars is useless... - h=0; - - def->references = h; - - - if (!h) - h = 1; - if (comp_x) - comp_x->references = h; - if (comp_y) - comp_y->references = h; - if (comp_z) - comp_z->references = h; - } - } - if (def->references<=0) - { - int wt = def->constant?WARN_NOTREFERENCEDCONST:WARN_NOTREFERENCED; - if (QCC_PR_Warning(wt, strings + def->s_file, def->s_line, "%s no references", def->name)) - { - if (!warnedunref) - { - QCC_PR_Note(WARN_NOTREFERENCED, NULL, 0, "You can use the noref prefix or pragma to silence this message."); - warnedunref = true; - } - } - - if (opt_unreferenced && def->type->type != ev_field) - { - optres_unreferenced++; - continue; - } - } - - if (def->type->type == ev_function) - { - if (opt_function_names && functions[G_FUNCTION(def->ofs)].first_statement<0) - { - optres_function_names++; - def->name = ""; - } - if (!def->timescalled) - { - if (def->references<=1) - QCC_PR_Warning(WARN_DEADCODE, strings + def->s_file, def->s_line, "%s is never directly called or referenced (spawn function or dead code)", def->name); -// else -// QCC_PR_Warning(WARN_DEADCODE, strings + def->s_file, def->s_line, "%s is never directly called", def->name); - } - if (opt_stripfunctions && def->timescalled >= def->references-1) //make sure it's not copied into a different var. - { //if it ever does self.think then it could be needed for saves. - optres_stripfunctions++; //if it's only ever called explicitly, the engine doesn't need to know. - continue; - } - -// df = &functions[numfunctions]; -// numfunctions++; - - } - else if (def->type->type == ev_field && def->constant) - { - if (numfielddefs >= MAX_FIELDS) - QCC_PR_ParseError(0, "Too many fields. Limit is %u\n", MAX_FIELDS); - dd = &fields[numfielddefs]; - numfielddefs++; - dd->type = def->type->aux_type->type; - dd->s_name = QCC_CopyString (def->name); - dd->ofs = G_INT(def->ofs); - } - else if ((def->scope||def->constant) && (def->type->type != ev_string || (strncmp(def->name, "dotranslate_", 12) && opt_constant_names_strings))) - { - if (opt_constant_names) - { - if (def->type->type == ev_string) - optres_constant_names_strings += strlen(def->name); - else - optres_constant_names += strlen(def->name); - continue; - } - } - -// if (!def->saved && def->type->type != ev_string) -// continue; - dd = &qcc_globals[numglobaldefs]; - numglobaldefs++; - - if (types) - dd->type = def->type-qcc_typeinfo; - else - dd->type = def->type->type; -#ifdef DEF_SAVEGLOBAL - if ( def->saved && ((!def->initialized || def->type->type == ev_function) -// && def->type->type != ev_function - && def->type->type != ev_field - && def->scope == NULL)) - { - dd->type |= DEF_SAVEGLOBAL; - } -#endif - if (def->shared) - dd->type |= DEF_SHARED; - - if (opt_locals && (def->scope || !strcmp(def->name, "IMMEDIATE"))) - { - dd->s_name = 0; - optres_locals += strlen(def->name); - } - else - dd->s_name = QCC_CopyString (def->name); - dd->ofs = def->ofs; - } - - for (i = 0; i < numglobaldefs; i++) - { - dd = &qcc_globals[i]; - if (!(dd->type & DEF_SAVEGLOBAL)) //only warn about saved ones. - continue; - - for (h = 0; h < numglobaldefs; h++) - { - if (i == h) - continue; - if (dd->ofs == qcc_globals[h].ofs) - { - if ((dd->type&~DEF_SAVEGLOBAL) != (qcc_globals[h].type&~DEF_SAVEGLOBAL)) - { - if (!(((dd->type&~DEF_SAVEGLOBAL) == ev_vector && (qcc_globals[h].type&~DEF_SAVEGLOBAL) == ev_float) || - ((dd->type&~DEF_SAVEGLOBAL) == ev_struct || (dd->type&~DEF_SAVEGLOBAL) == ev_union))) - QCC_PR_Warning(0, NULL, 0, "Mismatched union global types (%s and %s)", strings+dd->s_name, strings+qcc_globals[h].s_name); - } - //remove the saveglobal flag on the duplicate globals. - qcc_globals[h].type &= ~DEF_SAVEGLOBAL; - } - } - } - for (i = 1; i < numfielddefs; i++) - { - dd = &fields[i]; - - if (dd->type == ev_vector) //just ignore vectors. - continue; - - for (h = 1; h < numfielddefs; h++) - { - if (i == h) - continue; - if (dd->ofs == fields[h].ofs) - { - if (dd->type != fields[h].type) - { - if (fields[h].type != ev_vector) - { - QCC_PR_Warning(0, NULL, 0, "Mismatched union field types (%s and %s)", strings+dd->s_name, strings+fields[h].s_name); - } - } - } - } - } - - if (numglobaldefs > MAX_GLOBALS) - QCC_Error(ERR_TOOMANYGLOBALS, "Too many globals - %i\nAdd \"MAX_GLOBALS\" \"%i\" to qcc.cfg", numglobaldefs, (numglobaldefs+32768)&~32767); - - - for (i = 0; i < nummodels; i++) - { - if (!precache_models_used[i]) - QCC_PR_Warning(WARN_EXTRAPRECACHE, NULL, 0, "Model %s was precached but not directly used", precache_models[i]); - else if (!precache_models_block[i]) - QCC_PR_Warning(WARN_NOTPRECACHED, NULL, 0, "Model %s was used but not precached", precache_models[i]); - } -//PrintStrings (); -//PrintFunctions (); -//PrintFields (); -//PrintGlobals (); -strofs = (strofs+3)&~3; - - if (verbose) - { - printf ("%6i strofs (of %i)\n", strofs, MAX_STRINGS); - printf ("%6i numstatements (of %i)\n", numstatements, MAX_STATEMENTS); - printf ("%6i numfunctions (of %i)\n", numfunctions, MAX_FUNCTIONS); - printf ("%6i numglobaldefs (of %i)\n", numglobaldefs, MAX_GLOBALS); - printf ("%6i numfielddefs (%i unique) (of %i)\n", numfielddefs, pr.size_fields, MAX_FIELDS); - printf ("%6i numpr_globals (of %i)\n", numpr_globals, MAX_REGS); - } - - if (!*destfile) - strcpy(destfile, "progs.dat"); - if (verbose) - printf("Writing %s\n", destfile); - h = SafeOpenWrite (destfile, 2*1024*1024); - SafeWrite (h, &progs, sizeof(progs)); - SafeWrite (h, "\r\n\r\n", 4); - SafeWrite (h, QCC_copyright, strlen(QCC_copyright)+1); - SafeWrite (h, "\r\n\r\n", 4); - while(SafeSeek (h, 0, SEEK_CUR) & 3)//this is a lame way to do it - { - SafeWrite (h, "\0", 1); - } - - progs.ofs_strings = SafeSeek (h, 0, SEEK_CUR); - progs.numstrings = strofs; - - if (progs.blockscompressed&16) - { - SafeWrite (h, &len, sizeof(int)); //save for later - len = QC_encode(progfuncs, strofs*sizeof(char), 2, (char *)strings, h); //write - i = SafeSeek (h, 0, SEEK_CUR); - SafeSeek(h, progs.ofs_strings, SEEK_SET);//seek back - len = PRLittleLong(len); - SafeWrite (h, &len, sizeof(int)); //write size. - SafeSeek(h, i, SEEK_SET); - } - else - SafeWrite (h, strings, strofs); - - progs.ofs_statements = SafeSeek (h, 0, SEEK_CUR); - progs.numstatements = numstatements; - - if (qcc_targetformat == QCF_HEXEN2) - { - for (i=0 ; i= OP_CALL1 && statements[i].op <= OP_CALL8) - QCC_Error(ERR_BADTARGETSWITCH, "Target switching produced incompatible instructions"); - else if (statements[i].op >= OP_CALL1H && statements[i].op <= OP_CALL8H) - statements[i].op = statements[i].op - OP_CALL1H + OP_CALL1; - } - } - - for (i=0 ; iMAX_PARMS)?MAX_PARMS:functions[i].numparms); - qtestfuncs[i].locals = PRLittleLong (functions[i].locals); - for (j = 0; j < MAX_PARMS; j++) - qtestfuncs[i].parm_size[j] = PRLittleLong((int)functions[i].parm_size[j]); - } - - SafeWrite (h, qtestfuncs, numfunctions*sizeof(qtest_function_t)); - } - break; - case PST_DEFAULT: - case PST_KKQWSV: - case PST_FTE32: - for (i=0 ; iMAX_PARMS)?MAX_PARMS:functions[i].numparms); - functions[i].locals = PRLittleLong (functions[i].locals); - } - - if (progs.blockscompressed&8) - { - SafeWrite (h, &len, sizeof(int)); //save for later - len = QC_encode(progfuncs, numfunctions*sizeof(QCC_dfunction_t), 2, (char *)functions, h); //write - i = SafeSeek (h, 0, SEEK_CUR); - SafeSeek(h, progs.ofs_functions, SEEK_SET);//seek back - len = PRLittleLong(len); - SafeWrite (h, &len, sizeof(int)); //write size. - SafeSeek(h, i, SEEK_SET); - } - else - SafeWrite (h, functions, numfunctions*sizeof(QCC_dfunction_t)); - break; - default: - Sys_Error("structtype error"); - } - - switch(outputsttype) - { - case PST_QTEST: - // qtest needs a struct remap but should be able to get away with a simple swap here - for (i=0 ; i 60) - { - *s++ = '.'; - *s++ = '.'; - *s++ = '.'; - break; - } - } - *s++ = '"'; - *s++ = 0; - return buf; -} - - - -QCC_def_t *QCC_PR_DefForFieldOfs (gofs_t ofs) -{ - QCC_def_t *d; - - for (d=pr.def_head.next ; d ; d=d->next) - { - if (d->type->type != ev_field) - continue; - if (*((unsigned int *)&qcc_pr_globals[d->ofs]) == ofs) - return d; - } - QCC_Error (ERR_NOTDEFINED, "PR_DefForFieldOfs: couldn't find %i",ofs); - return NULL; -} - -/* -============ -PR_ValueString - -Returns a string describing *data in a type specific manner -============= -*/ -char *QCC_PR_ValueString (etype_t type, void *val) -{ - static char line[256]; - QCC_def_t *def; - QCC_dfunction_t *f; - - switch (type) - { - case ev_string: - sprintf (line, "%s", QCC_PR_String(strings + *(int *)val)); - break; - case ev_entity: - sprintf (line, "entity %i", *(int *)val); - break; - case ev_function: - f = functions + *(int *)val; - if (!f) - sprintf (line, "undefined function"); - else - sprintf (line, "%s()", strings + f->s_name); - break; - case ev_field: - def = QCC_PR_DefForFieldOfs ( *(int *)val ); - sprintf (line, ".%s", def->name); - break; - case ev_void: - sprintf (line, "void"); - break; - case ev_float: - sprintf (line, "%5.1f", *(float *)val); - break; - case ev_integer: - sprintf (line, "%i", *(int *)val); - break; - case ev_vector: - sprintf (line, "'%5.1f %5.1f %5.1f'", ((float *)val)[0], ((float *)val)[1], ((float *)val)[2]); - break; - case ev_pointer: - sprintf (line, "pointer"); - break; - default: - sprintf (line, "bad type %i", type); - break; - } - - return line; -} - -/* -============ -PR_GlobalString - -Returns a string with a description and the contents of a global, -padded to 20 field width -============ -*/ -/*char *QCC_PR_GlobalStringNoContents (gofs_t ofs) -{ - int i; - QCC_def_t *def; - void *val; - static char line[128]; - - val = (void *)&qcc_pr_globals[ofs]; - def = pr_global_defs[ofs]; - if (!def) -// Error ("PR_GlobalString: no def for %i", ofs); - sprintf (line,"%i(?""?""?)", ofs); - else - sprintf (line,"%i(%s)", ofs, def->name); - - i = strlen(line); - for ( ; i<16 ; i++) - strcat (line," "); - strcat (line," "); - - return line; -} - -char *QCC_PR_GlobalString (gofs_t ofs) -{ - char *s; - int i; - QCC_def_t *def; - void *val; - static char line[128]; - - val = (void *)&qcc_pr_globals[ofs]; - def = pr_global_defs[ofs]; - if (!def) - return QCC_PR_GlobalStringNoContents(ofs); - if (def->initialized && def->type->type != ev_function) - { - s = QCC_PR_ValueString (def->type->type, &qcc_pr_globals[ofs]); - sprintf (line,"%i(%s)", ofs, s); - } - else - sprintf (line,"%i(%s)", ofs, def->name); - - i = strlen(line); - for ( ; i<16 ; i++) - strcat (line," "); - strcat (line," "); - - return line; -}*/ - -/* -============ -PR_PrintOfs -============ -*/ -/*void QCC_PR_PrintOfs (gofs_t ofs) -{ - printf ("%s\n",QCC_PR_GlobalString(ofs)); -}*/ - -/* -================= -PR_PrintStatement -================= -*/ -/*void QCC_PR_PrintStatement (QCC_dstatement_t *s) -{ - int i; - - printf ("%4i : %4i : %s ", (int)(s - statements), statement_linenums[s-statements], pr_opcodes[s->op].opname); - i = strlen(pr_opcodes[s->op].opname); - for ( ; i<10 ; i++) - printf (" "); - - if (s->op == OP_IF || s->op == OP_IFNOT) - printf ("%sbranch %i",QCC_PR_GlobalString(s->a),s->b); - else if (s->op == OP_GOTO) - { - printf ("branch %i",s->a); - } - else if ( (unsigned)(s->op - OP_STORE_F) < 6) - { - printf ("%s",QCC_PR_GlobalString(s->a)); - printf ("%s", QCC_PR_GlobalStringNoContents(s->b)); - } - else - { - if (s->a) - printf ("%s",QCC_PR_GlobalString(s->a)); - if (s->b) - printf ("%s",QCC_PR_GlobalString(s->b)); - if (s->c) - printf ("%s", QCC_PR_GlobalStringNoContents(s->c)); - } - printf ("\n"); -}*/ - - -/* -============ -PR_PrintDefs -============ -*/ -/*void QCC_PR_PrintDefs (void) -{ - QCC_def_t *d; - - for (d=pr.def_head.next ; d ; d=d->next) - QCC_PR_PrintOfs (d->ofs); -}*/ - -QCC_type_t *QCC_PR_NewType (char *name, int basictype, pbool typedefed) -{ - if (numtypeinfos>= maxtypeinfos) - QCC_Error(ERR_TOOMANYTYPES, "Too many types"); - memset(&qcc_typeinfo[numtypeinfos], 0, sizeof(QCC_type_t)); - qcc_typeinfo[numtypeinfos].type = basictype; - qcc_typeinfo[numtypeinfos].name = name; - qcc_typeinfo[numtypeinfos].num_parms = 0; - qcc_typeinfo[numtypeinfos].param = NULL; - qcc_typeinfo[numtypeinfos].size = type_size[basictype]; - qcc_typeinfo[numtypeinfos].arraysize = 0; - qcc_typeinfo[numtypeinfos].typedefed = typedefed; - - - numtypeinfos++; - - return &qcc_typeinfo[numtypeinfos-1]; -} - -/* -============== -PR_BeginCompilation - -called before compiling a batch of files, clears the pr struct -============== -*/ -void QCC_PR_BeginCompilation (void *memory, int memsize) -{ - extern int recursivefunctiontype; - extern struct freeoffset_s *freeofs; - int i; - char name[16]; - - pr.memory = memory; - pr.max_memory = memsize; - - pr.def_tail = &pr.def_head; - - QCC_PR_ResetErrorScope(); - pr_scope = NULL; - -/* numpr_globals = RESERVED_OFS; - - for (i=0 ; iaux_type = type_float; - type_pointer->aux_type = QCC_PR_NewType("pointeraux", ev_float, false); - - type_intpointer = QCC_PR_NewType("__intpointer", ev_pointer, false); - type_intpointer->aux_type = type_integer; - type_floatpointer = QCC_PR_NewType("__floatpointer", ev_pointer, false); - type_floatpointer->aux_type = type_float; - - type_function->aux_type = type_void; - - //type_field->aux_type = type_float; - - type_integer = QCC_PR_NewType("integer", ev_integer, keyword_integer?true:false); - type_integer = QCC_PR_NewType("int", ev_integer, keyword_integer?true:false); - - - - if (output_parms) - { //this tends to confuse the brains out of decompilers. :) - numpr_globals = 1; - QCC_PR_GetDef(type_vector, "RETURN", NULL, true, 0, false)->references++; - for (i = 0; i < MAX_PARMS; i++) - { - sprintf(name, "PARM%i", i); - QCC_PR_GetDef(type_vector, name, NULL, true, 0, false)->references++; - } - } - else - { - numpr_globals = RESERVED_OFS; -// for (i=0 ; inext = NULL; - pr_error_count = 0; - pr_warning_count = 0; - recursivefunctiontype = 0; - - freeofs = NULL; -} - -/* -============== -PR_FinishCompilation - -called after all files are compiled to check for errors -Returns false if errors were detected. -============== -*/ -int QCC_PR_FinishCompilation (void) -{ - QCC_def_t *d; - int errors; - - errors = false; - -// check to make sure all functions prototyped have code - for (d=pr.def_head.next ; d ; d=d->next) - { - if (d->type->type == ev_function && !d->scope)// function parms are ok - { -// f = G_FUNCTION(d->ofs); -// if (!f || (!f->code && !f->builtin) ) - if (d->initialized==0) - { - s_file = d->s_file; - if (!strncmp(d->name, "ArrayGet*", 9)) - { - QCC_PR_EmitArrayGetFunction(d, d->name+9); - pr_scope = NULL; - } - else if (!strncmp(d->name, "ArraySet*", 9)) - { - QCC_PR_EmitArraySetFunction(d, d->name+9); - pr_scope = NULL; - } - else if (!strncmp(d->name, "Class*", 6)) - { - QCC_PR_EmitClassFromFunction(d, d->name+6); - pr_scope = NULL; - } - else - { - QCC_PR_ParseWarning(ERR_NOFUNC, "function %s was not defined",d->name); - QCC_PR_ParsePrintDef(ERR_NOFUNC, d); - bodylessfuncs = true; - errors = true; - } - s_file = 0; -// errors = true; - } - else if (d->initialized==2) - bodylessfuncs = true; - } - } - pr_scope = NULL; - - return !errors; -} - -//============================================================================= - -// FIXME: byte swap? - -// this is a 16 bit, non-reflected CRC using the polynomial 0x1021 -// and the initial and final xor values shown below... in other words, the -// CCITT standard CRC used by XMODEM - - -#define CRC_INIT_VALUE 0xffff -#define CRC_XOR_VALUE 0x0000 - -static unsigned short QCC_crctable[256] = -{ - 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, - 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, - 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, - 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, - 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, - 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, - 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, - 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, - 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, - 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, - 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, - 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, - 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, - 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, - 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, - 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, - 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, - 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, - 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, - 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, - 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, - 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, - 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, - 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, - 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, - 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, - 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, - 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, - 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, - 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, - 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, - 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 -}; - -void QCC_CRC_Init(unsigned short *crcvalue) -{ - *crcvalue = CRC_INIT_VALUE; -} - -void QCC_CRC_ProcessByte(unsigned short *crcvalue, qbyte data) -{ - *crcvalue = (*crcvalue << 8) ^ QCC_crctable[(*crcvalue >> 8) ^ data]; -} - -unsigned short QCC_CRC_Value(unsigned short crcvalue) -{ - return crcvalue ^ CRC_XOR_VALUE; -} -//============================================================================= - - -/* -============ -PR_WriteProgdefs - -Writes the global and entity structures out -Returns a crc of the header, to be stored in the progs file for comparison -at load time. -============ -*/ -/* -char *Sva(char *msg, ...) -{ - va_list l; - static char buf[1024]; - - va_start(l, msg); - QC_vsnprintf (buf,sizeof(buf)-1, msg, l); - va_end(l); - - return buf; -} -*/ - -#define PROGDEFS_MAX_SIZE 16384 -//write (to file buf) and add to the crc -static void Add(char *p, unsigned short *crc, char *file) -{ - char *s; - int i = strlen(file); - if (i + strlen(p)+1 >= PROGDEFS_MAX_SIZE) - return; - for(s=p;*s;s++,i++) - { - QCC_CRC_ProcessByte(crc, *s); - file[i] = *s; - } - file[i]='\0'; -} -#define ADD(p) Add(p, &crc, file) -//#define ADD(p) {char *s;int i = strlen(p);for(s=p;*s;s++,i++){QCC_CRC_ProcessByte(&crc, *s);file[i] = *s;}file[i]='\0';} - -static void Add3(char *p, unsigned short *crc, char *file) -{ - char *s; - for(s=p;*s;s++) - QCC_CRC_ProcessByte(crc, *s); -} -#define ADD3(p) Add3(p, &crc, file) - -unsigned short QCC_PR_WriteProgdefs (char *filename) -{ -#define ADD2(p) strncat(file, p, PROGDEFS_MAX_SIZE-1 - strlen(file)) //no crc (later changes) - char file[PROGDEFS_MAX_SIZE]; - QCC_def_t *d; - int f; - unsigned short crc; - QCC_def_t *ld; -// int c; - - file[0] = '\0'; - - QCC_CRC_Init (&crc); - -// print global vars until the first field is defined - - //ADD: crc and dump - //ADD2: dump but don't crc - //ADD3: crc but don't dump - - ADD("\n/* "); - if (qcc_targetformat == QCF_HEXEN2) - ADD3("generated by hcc, do not modify"); - else - ADD3("file generated by qcc, do not modify"); - ADD2("File generated by FTEQCC, relevent for engine modding only, the generated crc must be the same as your engine expects."); - ADD(" */\n\ntypedef struct"); - ADD2(" globalvars_s"); - ADD(qcva("\n{")); - ADD2("\tint pad;\n" - "\tint ofs_return[3];\n" //makes it easier with the get globals func - "\tint ofs_parm0[3];\n" - "\tint ofs_parm1[3];\n" - "\tint ofs_parm2[3];\n" - "\tint ofs_parm3[3];\n" - "\tint ofs_parm4[3];\n" - "\tint ofs_parm5[3];\n" - "\tint ofs_parm6[3];\n" - "\tint ofs_parm7[3];\n"); - ADD3(qcva("\tint\tpad[%i];\n", RESERVED_OFS)); - for (d=pr.def_head.next ; d ; d=d->next) - { - ld = d; - if (!strcmp (d->name, "end_sys_globals")) - break; - if (d->ofstype->type) - { - case ev_float: - ADD(qcva("\tfloat\t%s;\n",d->name)); - break; - case ev_vector: - ADD(qcva("\tvec3_t\t%s;\n",d->name)); - if (d->deftail) - d=d->deftail; // skip the elements - break; - case ev_string: - ADD(qcva("\tstring_t\t%s;\n",d->name)); - break; - case ev_function: - ADD(qcva("\tfunc_t\t%s;\n",d->name)); - break; - case ev_entity: - ADD(qcva("\tint\t%s;\n",d->name)); - break; - case ev_integer: - ADD(qcva("\tint\t%s;\n",d->name)); - break; - default: - ADD(qcva("\tint\t%s;\n",d->name)); - break; - } - } - ADD("} globalvars_t;\n\n"); - -// print all fields - ADD("typedef struct"); - ADD2(" entvars_s"); - ADD("\n{\n"); - for (d=pr.def_head.next ; d ; d=d->next) - { - if (!strcmp (d->name, "end_sys_fields")) - break; - - if (d->type->type != ev_field) - continue; - - switch (d->type->aux_type->type) - { - case ev_float: - ADD(qcva("\tfloat\t%s;\n",d->name)); - break; - case ev_vector: - ADD(qcva("\tvec3_t\t%s;\n",d->name)); - if (d->deftail) - d=d->deftail; // skip the elements - break; - case ev_string: - ADD(qcva("\tstring_t\t%s;\n",d->name)); - break; - case ev_function: - ADD(qcva("\tfunc_t\t%s;\n",d->name)); - break; - case ev_entity: - ADD(qcva("\tint\t%s;\n",d->name)); - break; - case ev_integer: - ADD(qcva("\tint\t%s;\n",d->name)); - break; - default: - ADD(qcva("\tint\t%s;\n",d->name)); - break; - } - } - ADD("} entvars_t;\n\n"); - - ///temp - ADD2("//with this the crc isn't needed for fields.\n#ifdef FIELDSSTRUCT\nstruct fieldvars_s {\n\tint ofs;\n\tint type;\n\tchar *name;\n} fieldvars[] = {\n"); - f=0; - for (d=pr.def_head.next ; d ; d=d->next) - { - if (!strcmp (d->name, "end_sys_fields")) - break; - - if (d->type->type != ev_field) - continue; - if (f) - ADD2(",\n"); - ADD2(qcva("\t{%i,\t%i,\t\"%s\"}",G_INT(d->ofs), d->type->aux_type->type, d->name)); - f = 1; - } - ADD2("\n};\n#endif\n\n"); - //end temp - - ADD2(qcva("#define PROGHEADER_CRC %i\n", crc)); - - if (QCC_CheckParm("-progdefs")) - { - printf ("writing %s\n", filename); - f = SafeOpenWrite(filename, 16384); - SafeWrite(f, file, strlen(file)); - SafeClose(f); - } - - - if (ForcedCRC) - crc = ForcedCRC; - - switch (crc) - { - case 12923: //#pragma sourcefile usage - break; - case 54730: - if (verbose) - printf("Recognised progs as QuakeWorld\n"); - break; - case 5927: - if (verbose) - printf("Recognised progs as NetQuake server gamecode\n"); - break; - - case 26940: - if (verbose) - printf("Recognised progs as Quake pre-release...\n"); - break; - - case 38488: - if (verbose) - printf("Recognised progs as original Hexen2\n"); - break; - case 26905: - if (verbose) - printf("Recognised progs as Hexen2 Mission Pack\n"); - break; - case 14046: - if (verbose) - printf("Recognised progs as Hexen2 (demo)\n"); - break; - - case 22390: //EXT_CSQC_1 - if (verbose) - printf("Recognised progs as an EXT_CSQC_1 module\n"); - break; - case 17105: - case 32199: //outdated ext_csqc - printf("Recognised progs as outdated CSQC module\n"); - break; - case 52195: - printf("Recognised progs as outdated CSQC module\n"); - break; - case 10020: - if (verbose) - printf("Recognised progs as a DP/FTE Menu module\n"); - break; - - case 32401: - printf("Warning: please update your tenebrae system defs.\n"); - break; - default: - printf("Warning: progs CRC not recognised from quake nor clones\n"); - break; - } - - - return crc; -} - - -/*void QCC_PrintFunction (char *name) -{ - int i; - QCC_dstatement_t *ds; - QCC_dfunction_t *df; - - for (i=0 ; ifirst_statement; - while (1) - { - QCC_PR_PrintStatement (ds); - if (!ds->op) - break; - ds++; - } -}*/ -/* -void QCC_PrintOfs(unsigned int ofs) -{ - int i; - bool printfunc; - QCC_dstatement_t *ds; - QCC_dfunction_t *df; - - for (i=0 ; ifirst_statement; - printfunc = false; - while (1) - { - if (!ds->op) - break; - if (ds->a == ofs || ds->b == ofs || ds->c == ofs) - { - QCC_PR_PrintStatement (ds); - printfunc = true; - } - ds++; - } - if (printfunc) - { - QCC_PrintFunction(strings + functions[i].s_name); - printf(" \n \n"); - } - } - -} -*/ -/* -============================================================================== - -DIRECTORY COPYING / PACKFILE CREATION - -============================================================================== -*/ - -typedef struct -{ - char name[56]; - int filepos, filelen; -} packfile_t; - -typedef struct -{ - char id[4]; - int dirofs; - int dirlen; -} packheader_t; - -packfile_t pfiles[4096], *pf; -int packhandle; -int packbytes; - - -/* -============ -CreatePath -============ -*/ -void QCC_CreatePath (char *path) -{ - /* - char *ofs; - - for (ofs = path+1 ; *ofs ; ofs++) - { - if (*ofs == '/') - { // create the directory - *ofs = 0; -#ifdef QCC - mkdir(path); -#else - QCC_mkdir (path); -#endif - *ofs = '/'; - } - } - */ -} - - -/* -=========== -PackFile - -Copy a file into the pak file -=========== -*/ -void QCC_PackFile (char *src, char *name) -{ - int remaining; -#if 1 - char *f; -#else - int in; - int count; - char buf[4096]; -#endif - - - if ( (qbyte *)pf - (qbyte *)pfiles > sizeof(pfiles) ) - QCC_Error (ERR_TOOMANYPAKFILES, "Too many files in pak file"); - -#if 1 - f = FS_ReadToMem(src, NULL, &remaining); - if (!f) - { - printf ("%64s : %7s\n", name, ""); -// QCC_Error("Failed to open file %s", src); - return; - } - - pf->filepos = PRLittleLong (SafeSeek (packhandle, 0, SEEK_CUR)); - pf->filelen = PRLittleLong (remaining); - strcpy (pf->name, name); - printf ("%64s : %7i\n", pf->name, remaining); - - packbytes += remaining; - - SafeWrite (packhandle, f, remaining); - - FS_CloseFromMem(f); -#else - in = SafeOpenRead (src); - remaining = filelength (in); - - pf->filepos = PRLittleLong (lseek (packhandle, 0, SEEK_CUR)); - pf->filelen = PRLittleLong (remaining); - strcpy (pf->name, name); - printf ("%64s : %7i\n", pf->name, remaining); - - packbytes += remaining; - - while (remaining) - { - if (remaining < sizeof(buf)) - count = remaining; - else - count = sizeof(buf); - SafeRead (in, buf, count); - SafeWrite (packhandle, buf, count); - remaining -= count; - } - - close (in); -#endif - pf++; -} - - -/* -=========== -CopyFile - -Copies a file, creating any directories needed -=========== -*/ -void QCC_CopyFile (char *src, char *dest) -{ - /* - int in, out; - int remaining, count; - char buf[4096]; - - print ("%s to %s\n", src, dest); - - in = SafeOpenRead (src); - remaining = filelength (in); - - QCC_CreatePath (dest); - out = SafeOpenWrite (dest, remaining+10); - - while (remaining) - { - if (remaining < sizeof(buf)) - count = remaining; - else - count = sizeof(buf); - SafeRead (in, buf, count); - SafeWrite (out, buf, count); - remaining -= count; - } - - close (in); - SafeClose (out); - */ -} - - -/* -=========== -CopyFiles -=========== -*/ - -void _QCC_CopyFiles (int blocknum, int copytype, char *srcdir, char *destdir) -{ - int i; - int dirlen; - unsigned short crc; - packheader_t header; - char name[1024]; - char srcfile[1024], destfile[1024]; - - packbytes = 0; - - if (copytype == 2) - { - pf = pfiles; - packhandle = SafeOpenWrite (destdir, 1024*1024); - SafeWrite (packhandle, &header, sizeof(header)); - } - - for (i=0 ; i 0) - printf ("%3i unique precache_sounds\n", numsounds); - if (nummodels > 0) - printf ("%3i unique precache_models\n", nummodels); - if (numtextures > 0) - printf ("%3i unique precache_textures\n", numtextures); - if (numfiles > 0) - printf ("%3i unique precache_files\n", numfiles); - } - - p = QCC_CheckParm ("-copy"); - if (p && p < myargc-2) - { // create a new directory tree - - strcpy (srcdir, myargv[p+1]); - strcpy (destdir, myargv[p+2]); - if (srcdir[strlen(srcdir)-1] != '/') - strcat (srcdir, "/"); - if (destdir[strlen(destdir)-1] != '/') - strcat (destdir, "/"); - - _QCC_CopyFiles(0, 1, srcdir, destdir); - return; - } - - for ( p = 0; p < 5; p++) - { - s = QCC_Packname[p]; - if (!*s) - continue; - strcpy(destdir, s); - strcpy(srcdir, ""); - _QCC_CopyFiles(p+1, 2, srcdir, destdir); - } - return; - /* - - blocknum = 1; - p = QCC_CheckParm ("-pak2"); - if (p && p value = qccHunkAlloc(strlen(val)+1); - memcpy(cnst->value, val, strlen(val)+1); - } - } - - //optimisations. - else if ( !strnicmp(myargv[i], "-O", 2) || !strnicmp(myargv[i], "/O", 2) ) - { - p = 0; - if (myargv[i][2] >= '0' && myargv[i][2] <= '3') - { - } - else if (!strnicmp(myargv[i]+2, "no-", 3)) - { - if (myargv[i][5]) - { - for (p = 0; optimisations[p].enabled; p++) - { - if ((*optimisations[p].abbrev && !stricmp(myargv[i]+5, optimisations[p].abbrev)) || !stricmp(myargv[i]+5, optimisations[p].fullname)) - { - *optimisations[p].enabled = false; - break; - } - } - } - } - else - { - if (myargv[i][2]) - for (p = 0; optimisations[p].enabled; p++) - if ((*optimisations[p].abbrev && !stricmp(myargv[i]+2, optimisations[p].abbrev)) || !stricmp(myargv[i]+2, optimisations[p].fullname)) - { - *optimisations[p].enabled = true; - break; - } - } - if (!optimisations[p].enabled) - QCC_PR_Warning(0, NULL, WARN_BADPARAMS, "Unrecognised optimisation parameter (%s)", myargv[i]); - } - - else if ( !strnicmp(myargv[i], "-K", 2) || !strnicmp(myargv[i], "/K", 2) ) - { - p = 0; - if (!strnicmp(myargv[i]+2, "no-", 3)) - { - for (p = 0; compiler_flag[p].enabled; p++) - if (!stricmp(myargv[i]+5, compiler_flag[p].abbrev)) - { - *compiler_flag[p].enabled = false; - break; - } - } - else - { - for (p = 0; compiler_flag[p].enabled; p++) - if (!stricmp(myargv[i]+2, compiler_flag[p].abbrev)) - { - *compiler_flag[p].enabled = true; - break; - } - } - - if (!compiler_flag[p].enabled) - QCC_PR_Warning(0, NULL, WARN_BADPARAMS, "Unrecognised keyword parameter (%s)", myargv[i]); - } - else if ( !strnicmp(myargv[i], "-F", 2) || !strnicmp(myargv[i], "/F", 2) ) - { - p = 0; - if (!strnicmp(myargv[i]+2, "no-", 3)) - { - for (p = 0; compiler_flag[p].enabled; p++) - if (!stricmp(myargv[i]+5, compiler_flag[p].abbrev)) - { - *compiler_flag[p].enabled = false; - break; - } - } - else - { - for (p = 0; compiler_flag[p].enabled; p++) - if (!stricmp(myargv[i]+2, compiler_flag[p].abbrev)) - { - *compiler_flag[p].enabled = true; - break; - } - } - - if (!compiler_flag[p].enabled) - QCC_PR_Warning(0, NULL, WARN_BADPARAMS, "Unrecognised flag parameter (%s)", myargv[i]); - } - - - else if ( !strncmp(myargv[i], "-T", 2) || !strncmp(myargv[i], "/T", 2) ) - { - p = 0; - for (p = 0; targets[p].name; p++) - if (!stricmp(myargv[i]+2, targets[p].name)) - { - qcc_targetformat = targets[p].target; - break; - } - - if (!targets[p].name) - QCC_PR_Warning(0, NULL, WARN_BADPARAMS, "Unrecognised target parameter (%s)", myargv[i]); - } - - else if ( !strnicmp(myargv[i], "-W", 2) || !strnicmp(myargv[i], "/W", 2) ) - { - if (!stricmp(myargv[i]+2, "all")) - memset(qccwarningdisabled, 0, sizeof(qccwarningdisabled)); - else if (!stricmp(myargv[i]+2, "none")) - memset(qccwarningdisabled, 1, sizeof(qccwarningdisabled)); - else if(!stricmp(myargv[i]+2, "error")) - pr_werror = true; - else if (!stricmp(myargv[i]+2, "no-mundane")) - { //disable mundane performance/efficiency/blah warnings that don't affect code. - qccwarningdisabled[WARN_SAMENAMEASGLOBAL] = true; - qccwarningdisabled[WARN_DUPLICATEDEFINITION] = true; - qccwarningdisabled[WARN_CONSTANTCOMPARISON] = true; - qccwarningdisabled[WARN_ASSIGNMENTINCONDITIONAL] = true; - qccwarningdisabled[WARN_DEADCODE] = true; - qccwarningdisabled[WARN_NOTREFERENCEDCONST] = true; - qccwarningdisabled[WARN_NOTREFERENCED] = true; - qccwarningdisabled[WARN_POINTLESSSTATEMENT] = true; - qccwarningdisabled[WARN_ASSIGNMENTTOCONSTANTFUNC] = true; - qccwarningdisabled[WARN_BADPRAGMA] = true; //C specs say that these should be ignored. We're close enough to C that I consider that a valid statement. - qccwarningdisabled[WARN_IDENTICALPRECOMPILER] = true; - qccwarningdisabled[WARN_UNDEFNOTDEFINED] = true; - qccwarningdisabled[WARN_FIXEDRETURNVALUECONFLICT] = true; - qccwarningdisabled[WARN_EXTRAPRECACHE] = true; - qccwarningdisabled[WARN_CORRECTEDRETURNTYPE] = true; - } - else - { - p = 0; - if (!strnicmp(myargv[i]+2, "no-", 3)) - { - for (p = 0; warningnames[p].name; p++) - if (!strcmp(myargv[i]+5, warningnames[p].name)) - { - qccwarningdisabled[warningnames[p].index] = true; - break; - } - } - else - { - for (p = 0; warningnames[p].name; p++) - if (!stricmp(myargv[i]+2, warningnames[p].name)) - { - qccwarningdisabled[warningnames[p].index] = false; - break; - } - } - - if (!warningnames[p].name) - QCC_PR_Warning(0, NULL, WARN_BADPARAMS, "Unrecognised warning parameter (%s)", myargv[i]); - } - } - } -} - -/* -============ -main -============ -*/ - -char *qccmsrc; -char *qccmsrc2; -char qccmfilename[1024]; -char qccmprogsdat[1024]; -char qccmsourcedir[1024]; - -void QCC_FinishCompile(void); - - -void SetEndian(void); - - - -void QCC_SetDefaultProperties (void) -{ - int level; - int i; - - Hash_InitTable(&compconstantstable, MAX_CONSTANTS, qccHunkAlloc(Hash_BytesForBuckets(MAX_CONSTANTS))); - - ForcedCRC = 0; - defaultstatic = 0; - - QCC_PR_DefineName("FTEQCC"); - - if (QCC_CheckParm("/Oz")) - { - qcc_targetformat = QCF_FTE; - QCC_PR_DefineName("OP_COMP_STATEMENTS"); - QCC_PR_DefineName("OP_COMP_DEFS"); - QCC_PR_DefineName("OP_COMP_FIELDS"); - QCC_PR_DefineName("OP_COMP_FUNCTIONS"); - QCC_PR_DefineName("OP_COMP_STRINGS"); - QCC_PR_DefineName("OP_COMP_GLOBALS"); - QCC_PR_DefineName("OP_COMP_LINES"); - QCC_PR_DefineName("OP_COMP_TYPES"); - } - - if (QCC_CheckParm("/O0") || QCC_CheckParm("-O0")) - level = 0; - else if (QCC_CheckParm("/O1") || QCC_CheckParm("-O1")) - level = 1; - else if (QCC_CheckParm("/O2") || QCC_CheckParm("-O2")) - level = 2; - else if (QCC_CheckParm("/O3") || QCC_CheckParm("-O3")) - level = 3; - else - level = -1; - - if (level == -1) - { - for (i = 0; optimisations[i].enabled; i++) - { - if (optimisations[i].flags & FLAG_ASDEFAULT) - *optimisations[i].enabled = true; - else - *optimisations[i].enabled = false; - } - } - else - { - for (i = 0; optimisations[i].enabled; i++) - { - if (level >= optimisations[i].optimisationlevel) - *optimisations[i].enabled = true; - else - *optimisations[i].enabled = false; - } - } - - if (QCC_CheckParm ("-h2")) - qcc_targetformat = QCF_HEXEN2; - else if (QCC_CheckParm ("-fte")) - qcc_targetformat = QCF_FTE; - else if (QCC_CheckParm ("-dp")) - qcc_targetformat = QCF_DARKPLACES; - else - qcc_targetformat = QCF_STANDARD; - - - //enable all warnings - memset(qccwarningdisabled, 0, sizeof(qccwarningdisabled)); - - //play with default warnings. - qccwarningdisabled[WARN_NOTREFERENCEDCONST] = true; - qccwarningdisabled[WARN_MACROINSTRING] = true; -// qccwarningdisabled[WARN_ASSIGNMENTTOCONSTANT] = true; - qccwarningdisabled[WARN_FIXEDRETURNVALUECONFLICT] = true; - qccwarningdisabled[WARN_EXTRAPRECACHE] = true; - qccwarningdisabled[WARN_DEADCODE] = true; - qccwarningdisabled[WARN_INEFFICIENTPLUSPLUS] = true; - qccwarningdisabled[WARN_FTE_SPECIFIC] = true; - qccwarningdisabled[WARN_EXTENSION_USED] = true; - qccwarningdisabled[WARN_IFSTRING_USED] = true; - qccwarningdisabled[WARN_CORRECTEDRETURNTYPE] = true; - - - - if (QCC_CheckParm("-nowarn") || QCC_CheckParm("-Wnone")) - memset(qccwarningdisabled, 1, sizeof(qccwarningdisabled)); - if (QCC_CheckParm("-Wall")) - memset(qccwarningdisabled, 0, sizeof(qccwarningdisabled)); - - if (QCC_CheckParm("-h2")) - qccwarningdisabled[WARN_CASEINSENSATIVEFRAMEMACRO] = true; - - //Check the command line - QCC_PR_CommandLinePrecompilerOptions(); - - - if (qcc_targetformat == QCF_HEXEN2) //force on the thinktime keyword if hexen2 progs. - keyword_thinktime = true; - - if (QCC_CheckParm("/Debug")) //disable any debug optimisations - { - for (i = 0; optimisations[i].enabled; i++) - { - if (optimisations[i].flags & FLAG_KILLSDEBUGGERS) - *optimisations[i].enabled = false; - } - } -} - -//builds a list of files, pretends that they came from a progs.src -int QCC_FindQCFiles() -{ -#ifdef _WIN32 - WIN32_FIND_DATA fd; - HANDLE h; -#endif - - int numfiles = 0, i, j; - char *filelist[256], *temp; - - - qccmsrc = qccHunkAlloc(8192); - strcat(qccmsrc, "progs.dat\n");//"#pragma PROGS_DAT progs.dat\n"); - -#ifdef _WIN32 - h = FindFirstFile("*.qc", &fd); - if (h == INVALID_HANDLE_VALUE) - return 0; - - do - { - filelist[numfiles] = qccHunkAlloc (strlen(fd.cFileName)+1); - strcpy(filelist[numfiles], fd.cFileName); - numfiles++; - } while(FindNextFile(h, &fd)!=0); - FindClose(h); -#else - printf("-Facc is not supported on this platform. Please make a progs.src file instead\n"); -#endif - - //Sort alphabetically. - //bubble. :( - - for (i = 0; i < numfiles-1; i++) - { - for (j = i+1; j < numfiles; j++) - { - if (stricmp(filelist[i], filelist[j]) > 0) - { - temp = filelist[j]; - filelist[j] = filelist[i]; - filelist[i] = temp; - } - } - } - for (i = 0; i < numfiles; i++) - { - strcat(qccmsrc, filelist[i]); - strcat(qccmsrc, "\n"); -// strcat(qccmsrc, "#include \""); -// strcat(qccmsrc, filelist[i]); -// strcat(qccmsrc, "\"\n"); - } - - return numfiles; -} - -int qcc_compileactive = false; -extern int accglobalsblock; -char *originalqccmsrc; //for autoprototype. -void QCC_main (int argc, char **argv) //as part of the quake engine -{ - extern int pr_bracelevel; - - int p; - -#ifndef QCCONLY - char destfile2[1024], *s2; -#endif - char *s; - - SetEndian(); - - myargc = argc; - myargv = argv; - - qcc_compileactive = true; - - pHash_Get = &Hash_Get; - pHash_GetNext = &Hash_GetNext; - pHash_Add = &Hash_Add; - - MAX_REGS = 1<<17; - MAX_STRINGS = 1000000; - MAX_GLOBALS = 1<<17; - MAX_FIELDS = 1<<12; - MAX_STATEMENTS = 0x80000; - MAX_FUNCTIONS = 16384; - maxtypeinfos = 16384; - MAX_CONSTANTS = 2048; - - compressoutput = 0; - - p = externs->FileSize("qcc.cfg"); - if (p < 0) - p = externs->FileSize("src/qcc.cfg"); - if (p>0) - { - s = qccHunkAlloc(p+1); - s = externs->ReadFile("qcc.cfg", s, p); - - while(1) - { - s = QCC_COM_Parse(s); - if (!strcmp(qcc_token, "MAX_REGS")) - { - s = QCC_COM_Parse(s); - MAX_REGS = atoi(qcc_token); - } else if (!strcmp(qcc_token, "MAX_STRINGS")) { - s = QCC_COM_Parse(s); - MAX_STRINGS = atoi(qcc_token); - } else if (!strcmp(qcc_token, "MAX_GLOBALS")) { - s = QCC_COM_Parse(s); - MAX_GLOBALS = atoi(qcc_token); - } else if (!strcmp(qcc_token, "MAX_FIELDS")) { - s = QCC_COM_Parse(s); - MAX_FIELDS = atoi(qcc_token); - } else if (!strcmp(qcc_token, "MAX_STATEMENTS")) { - s = QCC_COM_Parse(s); - MAX_STATEMENTS = atoi(qcc_token); - } else if (!strcmp(qcc_token, "MAX_FUNCTIONS")) { - s = QCC_COM_Parse(s); - MAX_FUNCTIONS = atoi(qcc_token); - } else if (!strcmp(qcc_token, "MAX_TYPES")) { - s = QCC_COM_Parse(s); - maxtypeinfos = atoi(qcc_token); - } else if (!strcmp(qcc_token, "MAX_TEMPS")) { - s = QCC_COM_Parse(s); - max_temps = atoi(qcc_token); - } else if (!strcmp(qcc_token, "CONSTANTS")) { - s = QCC_COM_Parse(s); - MAX_CONSTANTS = atoi(qcc_token); - } - else if (!s) - break; - else - printf("Bad token in qcc.cfg file\n"); - } - } - /* don't try to be clever - else if (p < 0) - { - s = qccHunkAlloc(8192); - sprintf(s, "MAX_REGS\t%i\r\nMAX_STRINGS\t%i\r\nMAX_GLOBALS\t%i\r\nMAX_FIELDS\t%i\r\nMAX_STATEMENTS\t%i\r\nMAX_FUNCTIONS\t%i\r\nMAX_TYPES\t%i\r\n", - MAX_REGS, MAX_STRINGS, MAX_GLOBALS, MAX_FIELDS, MAX_STATEMENTS, MAX_FUNCTIONS, maxtypeinfos); - externs->WriteFile("qcc.cfg", s, strlen(s)); - } - */ - - strcpy(QCC_copyright, "This file was created with ForeThought's modified QuakeC compiler\nThanks to ID Software"); - for (p = 0; p < 5; p++) - strcpy(QCC_Packname[p], ""); - - for (p = 0; compiler_flag[p].enabled; p++) - { - *compiler_flag[p].enabled = compiler_flag[p].flags & FLAG_ASDEFAULT; - } - pr_werror = false; - - QCC_SetDefaultProperties(); - - optres_shortenifnots = 0; - optres_overlaptemps = 0; - optres_noduplicatestrings = 0; - optres_constantarithmatic = 0; - optres_nonvec_parms = 0; - optres_constant_names = 0; - optres_constant_names_strings = 0; - optres_precache_file = 0; - optres_filenames = 0; - optres_assignments = 0; - optres_unreferenced = 0; - optres_function_names = 0; - optres_locals = 0; - optres_dupconstdefs = 0; - optres_return_only = 0; - optres_compound_jumps = 0; -// optres_comexprremoval = 0; - optres_stripfunctions = 0; - optres_locals_marshalling = 0; - optres_logicops = 0; - - optres_test1 = 0; - optres_test2 = 0; - - accglobalsblock = 0; - - - numtemps = 0; - - QCC_PurgeTemps(); - - strings = (void *)qccHunkAlloc(sizeof(char) * MAX_STRINGS); - strofs = 2; - - statements = (void *)qccHunkAlloc(sizeof(QCC_dstatement_t) * MAX_STATEMENTS); - numstatements = 0; - statement_linenums = (void *)qccHunkAlloc(sizeof(int) * MAX_STATEMENTS); - - functions = (void *)qccHunkAlloc(sizeof(QCC_dfunction_t) * MAX_FUNCTIONS); - numfunctions=0; - - pr_bracelevel = 0; - - qcc_pr_globals = (void *)qccHunkAlloc(sizeof(float) * MAX_REGS); - numpr_globals=0; - - Hash_InitTable(&globalstable, MAX_REGS/2, qccHunkAlloc(Hash_BytesForBuckets(MAX_REGS/2))); - Hash_InitTable(&localstable, MAX_REGS/2, qccHunkAlloc(Hash_BytesForBuckets(MAX_REGS/2))); - Hash_InitTable(&floatconstdefstable, MAX_REGS/2+1, qccHunkAlloc(Hash_BytesForBuckets(MAX_REGS/2+1))); - Hash_InitTable(&stringconstdefstable, MAX_REGS/2, qccHunkAlloc(Hash_BytesForBuckets(MAX_REGS/2))); - Hash_InitTable(&stringconstdefstable_trans, 1000, qccHunkAlloc(Hash_BytesForBuckets(1000))); - dotranslate_count = 0; - -// pr_global_defs = (QCC_def_t **)qccHunkAlloc(sizeof(QCC_def_t *) * MAX_REGS); - - qcc_globals = (void *)qccHunkAlloc(sizeof(QCC_ddef_t) * MAX_GLOBALS); - numglobaldefs=0; - - fields = (void *)qccHunkAlloc(sizeof(QCC_ddef_t) * MAX_FIELDS); - numfielddefs=0; - -memset(pr_immediate_string, 0, sizeof(pr_immediate_string)); - - precache_sounds = (void *)qccHunkAlloc(sizeof(char)*MAX_DATA_PATH*MAX_SOUNDS); - precache_sounds_block = (void *)qccHunkAlloc(sizeof(int)*MAX_SOUNDS); - precache_sounds_used = (void *)qccHunkAlloc(sizeof(int)*MAX_SOUNDS); - numsounds=0; - - precache_textures = (void *)qccHunkAlloc(sizeof(char)*MAX_DATA_PATH*MAX_TEXTURES); - precache_textures_block = (void *)qccHunkAlloc(sizeof(int)*MAX_TEXTURES); - numtextures=0; - - precache_models = (void *)qccHunkAlloc(sizeof(char)*MAX_DATA_PATH*MAX_MODELS); - precache_models_block = (void *)qccHunkAlloc(sizeof(int)*MAX_MODELS); - precache_models_used = (void *)qccHunkAlloc(sizeof(int)*MAX_MODELS); - nummodels=0; - - precache_files = (void *)qccHunkAlloc(sizeof(char)*MAX_DATA_PATH*MAX_FILES); - precache_files_block = (void *)qccHunkAlloc(sizeof(int)*MAX_FILES); - numfiles = 0; - - qcc_typeinfo = (void *)qccHunkAlloc(sizeof(QCC_type_t)*maxtypeinfos); - numtypeinfos = 0; - - qcc_tempofs = qccHunkAlloc(sizeof(int) * max_temps); - tempsstart = 0; - - bodylessfuncs=0; - - memset(&pr, 0, sizeof(pr)); -#ifdef MAX_EXTRA_PARMS - memset(&extra_parms, 0, sizeof(extra_parms)); -#endif - - if ( QCC_CheckParm ("/?") || QCC_CheckParm ("?") || QCC_CheckParm ("-?") || QCC_CheckParm ("-help") || QCC_CheckParm ("--help")) - { - printf ("qcc looks for progs.src in the current directory.\n"); - printf ("to look in a different directory: qcc -src \n"); -// printf ("to build a clean data tree: qcc -copy \n"); -// printf ("to build a clean pak file: qcc -pak \n"); -// printf ("to bsp all bmodels: qcc -bspmodels \n"); - printf ("-Fwasm causes FTEQCC to dump all asm to qc.asm\n"); - printf ("-O0 to disable optimisations\n"); - printf ("-O1 to optimise for size\n"); - printf ("-O2 to optimise more - some behaviours may change\n"); - printf ("-O3 to optimise lots - experimental or non-future-proof\n"); - printf ("-Oname to enable an optimisation\n"); - printf ("-Ono-name to disable optimisations\n"); - printf ("-Kkeyword to activate keyword\n"); - printf ("-Kno-keyword to disable keyword\n"); - printf ("-Wall to give a stupid number of warnings\n"); - printf ("-Ttarget to set a output format\n"); - printf ("-Fautoproto to enable automatic prototyping\n"); - printf ("-Fsubscope to make locals specific to their subscope\n"); - - qcc_compileactive = false; - return; - } - - if (flag_caseinsensative) - { - printf("Compiling without case sensativity\n"); - pHash_Get = &Hash_GetInsensative; - pHash_GetNext = &Hash_GetNextInsensative; - pHash_Add = &Hash_AddInsensative; - } - - - if (opt_locals_marshalling) - printf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\nLocals marshalling might be buggy. Use with caution\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"); - - p = QCC_CheckParm ("-src"); - if (p && p < argc-1 ) - { - strcpy (qccmsourcedir, argv[p+1]); - strcat (qccmsourcedir, "/"); - printf ("Source directory: %s\n", qccmsourcedir); - } - else - *qccmsourcedir = '\0'; - - QCC_InitData (); - - QCC_PR_BeginCompilation ((void *)qccHunkAlloc (0x100000), 0x100000); - - if (flag_acc) - { - if (!QCC_FindQCFiles()) - QCC_Error (ERR_COULDNTOPENFILE, "Couldn't open file for asm output."); - } - else - { - if (!numsourcefiles) - { - p = QCC_CheckParm ("-qc"); - if (!p || p >= argc-1 || argv[p+1][0] == '-') - p = QCC_CheckParm ("-srcfile"); - if (p && p < argc-1 ) - sprintf (qccmprogsdat, "%s", argv[p+1]); - else - { //look for a preprogs.src... :o) - sprintf (qccmprogsdat, "preprogs.src"); - if (externs->FileSize(qccmprogsdat) <= 0) - sprintf (qccmprogsdat, "progs.src"); - } - - numsourcefiles = 0; - strcpy(sourcefileslist[numsourcefiles++], qccmprogsdat); - currentsourcefile = 0; - } - else if (currentsourcefile == numsourcefiles) - { - //no more. - qcc_compileactive = false; - numsourcefiles = 0; - currentsourcefile = 0; - return; - } - - if (currentsourcefile) - printf("-------------------------------------\n"); - - sprintf (qccmprogsdat, "%s%s", qccmsourcedir, sourcefileslist[currentsourcefile++]); - printf ("Source file: %s\n", qccmprogsdat); - - if (QCC_LoadFile (qccmprogsdat, (void *)&qccmsrc) == -1) - { - return; - } - } - -#ifdef WRITEASM - if (writeasm) - { - asmfile = fopen("qc.asm", "wb"); - if (!asmfile) - QCC_Error (ERR_INTERNAL, "Couldn't open file for asm output."); - } -#endif - - newstylesource = false; - while(*qccmsrc && *qccmsrc < ' ') - qccmsrc++; - pr_file_p = QCC_COM_Parse(qccmsrc); - - if (QCC_CheckParm ("-qc")) - { - strcpy(destfile, qccmprogsdat); - StripExtension(destfile); - strcat(destfile, ".qco"); - - p = QCC_CheckParm ("-o"); - if (!p || p >= argc-1 || argv[p+1][0] == '-') - if (p && p < argc-1 ) - sprintf (destfile, "%s%s", qccmsourcedir, argv[p+1]); - goto newstyle; - } - - if (*qcc_token == '#') - { - void StartNewStyleCompile(void); -newstyle: - newstylesource = true; - StartNewStyleCompile(); - return; - } - - pr_file_p = qccmsrc; - QCC_PR_LexWhitespace(); - qccmsrc = pr_file_p; - - s = qccmsrc; - pr_file_p = qccmsrc; - QCC_PR_SimpleGetToken (); - strcpy(qcc_token, pr_token); - qccmsrc = pr_file_p; - - if (!qccmsrc) - QCC_Error (ERR_NOOUTPUT, "No destination filename. qcc -help for info."); - strcpy (destfile, qcc_token); - -#ifndef QCCONLY - p=0; - s2 = strcpy(destfile2, destfile); - if (!strncmp(s2, "./", 2)) - s2+=2; - else - { - while(!strncmp(s2, "../", 3)) - { - s2+=3; - p++; - } - } - strcpy(qccmfilename, qccmsourcedir); - for (s=qccmfilename+strlen(qccmfilename);p && s>=qccmfilename; s--) - { - if (*s == '/' || *s == '\\') - { - *(s+1) = '\0'; - p--; - } - } - sprintf(destfile, "%s", s2); - - while (p>0) - { - memmove(destfile+3, destfile, strlen(destfile)+1); - destfile[0] = '.'; - destfile[1] = '.'; - destfile[2] = '/'; - p--; - } -#endif - - if (flag_filetimes) - { - struct stat s, os; - pbool modified = false; - - if (stat(destfile, &os) != -1) - { - while ((pr_file_p=QCC_COM_Parse(pr_file_p))) - { - if (stat(qcc_token, &s) == -1 || s.st_mtime > os.st_mtime) - { - printf("%s changed\n", qcc_token); - modified = true; - break; - } - } - if (!modified) - { - printf("No changes\n"); - qcc_compileactive = false; - return; - } - else - { - pr_file_p = qccmsrc; - } - } - } - - printf ("outputfile: %s\n", destfile); - - pr_dumpasm = false; - - currentchunk = NULL; - - originalqccmsrc = qccmsrc; -} - -void new_QCC_ContinueCompile(void); -//called between exe frames - won't loose net connection (is the theory)... -void QCC_ContinueCompile(void) -{ - char *s, *s2; - currentchunk = NULL; - if (!qcc_compileactive) - //HEY! - return; - - if (newstylesource) - { - new_QCC_ContinueCompile(); - return; - } - - qccmsrc = QCC_COM_Parse(qccmsrc); - if (!qccmsrc) - { - if (autoprototype) - { - qccmsrc = originalqccmsrc; - QCC_SetDefaultProperties(); - autoprototype = false; - return; - } - QCC_FinishCompile(); - - PostCompile(); - if (!PreCompile()) - return; - QCC_main(myargc, myargv); - return; - } - s = qcc_token; - - strcpy (qccmfilename, qccmsourcedir); - while(1) - { - if (!strncmp(s, "..\\", 3) || !strncmp(s, "../", 3)) - { - s2 = qccmfilename + strlen(qccmfilename)-2; - while (s2>=qccmfilename) - { - if (*s2 == '/' || *s2 == '\\') - { - s2[1] = '\0'; - break; - } - s2--; - } - if (s2>=qccmfilename) - { - s+=3; - continue; - } - } - if (!strncmp(s, ".\\", 2) || !strncmp(s, "./", 2)) - { - s+=2; - continue; - } - - break; - } - strcat (qccmfilename, s); - if (autoprototype) - printf ("prototyping %s\n", qccmfilename); - else - { - printf ("compiling %s\n", qccmfilename); - } - QCC_LoadFile (qccmfilename, (void *)&qccmsrc2); - - if (!QCC_PR_CompileFile (qccmsrc2, qccmfilename) ) - QCC_Error (ERR_PARSEERRORS, "Errors have occured\n"); -} -void QCC_FinishCompile(void) -{ - pbool donesomething; - int crc; -// int p; - currentchunk = NULL; - - if (setjmp(pr_parse_abort)) - QCC_Error(ERR_INTERNAL, ""); - - if (!QCC_PR_FinishCompilation ()) - { - QCC_Error (ERR_PARSEERRORS, "compilation errors"); - } - -/* p = QCC_CheckParm ("-asm"); - if (p) - { - for (p++ ; p MAX_ERRORS) - return; - if (setjmp(pr_parse_abort)) - return; - - QCC_PR_SkipToSemicolon (); - if (pr_token_type == tt_eof) - return; - } - - - QCC_PR_ClearGrabMacros (); // clear the frame macros - - compilingfile = qccmprogsdat; - - pr_file_p = qccmsrc; - s_file = s_file2 = QCC_CopyString (compilingfile); - - pr_source_line = 0; - - QCC_PR_NewLine (false); - - QCC_PR_Lex (); // read first token -} -void new_QCC_ContinueCompile(void) -{ - if (setjmp(pr_parse_abort)) - { -// if (pr_error_count != 0) - { - QCC_Error (ERR_PARSEERRORS, "Errors have occured"); - return; - } - QCC_PR_SkipToSemicolon (); - if (pr_token_type == tt_eof) - return; - } - - if (pr_token_type == tt_eof) - { - if (pr_error_count) - QCC_Error (ERR_PARSEERRORS, "Errors have occured"); - QCC_FinishCompile(); - - PostCompile(); - if (!PreCompile()) - return; - QCC_main(myargc, myargv); - return; - } - - pr_scope = NULL; // outside all functions - - QCC_PR_ParseDefs (NULL); -} - -/*void new_QCC_ContinueCompile(void) -{ - char *s, *s2; - if (!qcc_compileactive) - //HEY! - return; - -// compile all the files - - qccmsrc = QCC_COM_Parse(qccmsrc); - if (!qccmsrc) - { - QCC_FinishCompile(); - return; - } - s = qcc_token; - - strcpy (qccmfilename, qccmsourcedir); - while(1) - { - if (!strncmp(s, "..\\", 3)) - { - s2 = qccmfilename + strlen(qccmfilename)-2; - while (s2>=qccmfilename) - { - if (*s2 == '/' || *s2 == '\\') - { - s2[1] = '\0'; - break; - } - s2--; - } - s+=3; - continue; - } - if (!strncmp(s, ".\\", 2)) - { - s+=2; - continue; - } - - break; - } -// strcat (qccmfilename, s); -// printf ("compiling %s\n", qccmfilename); -// QCC_LoadFile (qccmfilename, (void *)&qccmsrc2); - -// if (!new_QCC_PR_CompileFile (qccmsrc2, qccmfilename) ) -// QCC_Error ("Errors have occured\n"); - - - { - - if (!pr.memory) - QCC_Error ("PR_CompileFile: Didn't clear"); - - QCC_PR_ClearGrabMacros (); // clear the frame macros - - compilingfile = filename; - - pr_file_p = qccmsrc2; - s_file = QCC_CopyString (filename); - - pr_source_line = 0; - - QCC_PR_NewLine (); - - QCC_PR_Lex (); // read first token - - while (pr_token_type != tt_eof) - { - if (setjmp(pr_parse_abort)) - { - if (++pr_error_count > MAX_ERRORS) - return false; - QCC_PR_SkipToSemicolon (); - if (pr_token_type == tt_eof) - return false; - } - - pr_scope = NULL; // outside all functions - - QCC_PR_ParseDefs (); - } - } - return (pr_error_count == 0); - -}*/ - - -#endif diff --git a/misc/source/fteqcc-src/qcctui.c b/misc/source/fteqcc-src/qcctui.c deleted file mode 100644 index 7f33e913..00000000 --- a/misc/source/fteqcc-src/qcctui.c +++ /dev/null @@ -1,117 +0,0 @@ -#include "qcc.h" - -#include -#include - -/* -============== -LoadFile -============== -*/ -unsigned char *QCC_ReadFile (char *fname, void *buffer, int len) -{ - long length; - FILE *f; - f = fopen(fname, "rb"); - if (!f) - return NULL; - length = fread(buffer, 1, len, f); - fclose(f); - - if (length != len) - return NULL; - - return buffer; -} -int QCC_FileSize (char *fname) -{ - long length; - FILE *f; - f = fopen(fname, "rb"); - if (!f) - return -1; - fseek(f, 0, SEEK_END); - length = ftell(f); - fclose(f); - - return length; -} - -pbool QCC_WriteFile (char *name, void *data, int len) -{ - long length; - FILE *f; - f = fopen(name, "wb"); - if (!f) - return false; - length = fwrite(data, 1, len, f); - fclose(f); - - if (length != len) - return false; - - return true; -} - -#undef printf -#undef Sys_Error - -void Sys_Error(const char *text, ...) -{ - va_list argptr; - static char msg[2048]; - - va_start (argptr,text); - QC_vsnprintf (msg,sizeof(msg)-1, text,argptr); - va_end (argptr); - - QCC_Error(ERR_INTERNAL, "%s", msg); -} - - -FILE *logfile; -int logprintf(const char *format, ...) -{ - va_list argptr; - static char string[1024]; - - va_start (argptr, format); -#ifdef _WIN32 - _vsnprintf (string,sizeof(string)-1, format,argptr); -#else - vsnprintf (string,sizeof(string), format,argptr); -#endif - va_end (argptr); - - printf("%s", string); - if (logfile) - fputs(string, logfile); - - return 0; -} - -int main (int argc, char **argv) -{ - int sucess; - progexterns_t ext; - progfuncs_t funcs; - progfuncs = &funcs; - memset(&funcs, 0, sizeof(funcs)); - funcs.parms = &ext; - memset(&ext, 0, sizeof(progexterns_t)); - funcs.parms->ReadFile = QCC_ReadFile; - funcs.parms->FileSize = QCC_FileSize; - funcs.parms->WriteFile = QCC_WriteFile; - funcs.parms->printf = logprintf; - funcs.parms->Sys_Error = Sys_Error; - logfile = fopen("fteqcc.log", "wt"); - sucess = CompileParams(&funcs, true, argc, argv); - qccClearHunk(); - if (logfile) - fclose(logfile); - -#ifdef _WIN32 -// fgetc(stdin); //wait for keypress -#endif - return !sucess; -} diff --git a/misc/source/fteqcc-src/qcd.h b/misc/source/fteqcc-src/qcd.h deleted file mode 100644 index fd7817eb..00000000 --- a/misc/source/fteqcc-src/qcd.h +++ /dev/null @@ -1,7 +0,0 @@ -pbool QC_decodeMethodSupported(int method); -char *QC_decode(progfuncs_t *progfuncs, int complen, int len, int method, char *info, char *buffer); -int QC_encode(progfuncs_t *progfuncs, int len, int method, char *in, int handle); - -char *filefromprogs(progfuncs_t *progfuncs, progsnum_t prnum, char *fname, int *size, char *buffer); -char *filefromnewprogs(progfuncs_t *progfuncs, char *prname, char *fname, int *size, char *buffer);//fixme - remove parm 1 - diff --git a/misc/source/fteqcc-src/qcd_main.c b/misc/source/fteqcc-src/qcd_main.c deleted file mode 100644 index 20c6f7ca..00000000 --- a/misc/source/fteqcc-src/qcd_main.c +++ /dev/null @@ -1,232 +0,0 @@ -#include "progsint.h" -//#include "qcc.h" - -//#define AVAIL_ZLIB - -#ifdef AVAIL_ZLIB -#ifdef _WIN32 -#define ZEXPORT VARGS -#include "../libs/zlib.h" - -#ifdef _WIN64 -# pragma comment (lib, "../libs/zlib64.lib") -#else -# pragma comment (lib, "../libs/zlib.lib") -#endif -#else -#include -#endif -#endif - -pbool QC_decodeMethodSupported(int method) -{ - if (method == 0) - return true; - if (method == 1) - return true; - if (method == 2) - { -#ifdef AVAIL_ZLIB - return false; -#endif - } - return false; -} - -char *QC_decode(progfuncs_t *progfuncs, int complen, int len, int method, char *info, char *buffer) -{ - int i; - if (method == 0) //copy - { - if (complen != len) Sys_Error("lengths do not match"); - memcpy(buffer, info, len); - } - else if (method == 1) //xor encryption - { - if (complen != len) Sys_Error("lengths do not match"); - for (i = 0; i < len; i++) - buffer[i] = info[i] ^ 0xA5; - } - else if (method == 2) //compression (ZLIB) - { -#ifdef AVAIL_ZLIB - z_stream strm = { - info, - complen, - 0, - - buffer, - len, - 0, - - NULL, - NULL, - - NULL, - NULL, - NULL, - - Z_BINARY, - 0, - 0 - }; - - inflateInit(&strm); - if (Z_STREAM_END != inflate(&strm, Z_FINISH)) //decompress it in one go. - Sys_Error("Failed block decompression\n"); - inflateEnd(&strm); -#endif - } - //add your decryption/decompression routine here. - else - Sys_Error("Bad file encryption routine\n"); - - - return buffer; -} - -#ifndef MINIMAL -void SafeWrite(int hand, void *buf, long count); -int SafeSeek(int hand, int ofs, int mode); -//we are allowed to trash our input here. -int QC_encode(progfuncs_t *progfuncs, int len, int method, char *in, int handle) -{ - int i; - if (method == 0) //copy - { - SafeWrite(handle, in, len); - return len; - } - else if (method == 1) //xor encryption - { - for (i = 0; i < len; i++) - in[i] = in[i] ^ 0xA5; - SafeWrite(handle, in, len); - return len; - } - else if (method == 2) //compression (ZLIB) - { -#ifdef AVAIL_ZLIB - char out[8192]; - - z_stream strm = { - in, - len, - 0, - - out, - sizeof(out), - 0, - - NULL, - NULL, - - NULL, - NULL, - NULL, - - Z_BINARY, - 0, - 0 - }; - i=0; - - deflateInit(&strm, Z_BEST_COMPRESSION); - while(deflate(&strm, Z_FINISH) == Z_OK) - { - SafeWrite(handle, out, sizeof(out) - strm.avail_out); //compress in chunks of 8192. Saves having to allocate a huge-mega-big buffer - i+=sizeof(out) - strm.avail_out; - strm.next_out = out; - strm.avail_out = sizeof(out); - } - SafeWrite(handle, out, sizeof(out) - strm.avail_out); - i+=sizeof(out) - strm.avail_out; - deflateEnd(&strm); - return i; -#endif - Sys_Error("ZLIB compression not supported in this build"); - return 0; - } - //add your compression/decryption routine here. - else - { - Sys_Error("Wierd method"); - return 0; - } -} -#endif - -char *filefromprogs(progfuncs_t *progfuncs, progsnum_t prnum, char *fname, int *size, char *buffer) -{ - int num; - includeddatafile_t *s; - if (!pr_progstate[prnum].progs) - return NULL; - if (pr_progstate[prnum].progs->version != PROG_EXTENDEDVERSION) - return NULL; - if (!pr_progstate[prnum].progs->secondaryversion != PROG_SECONDARYVERSION16 && - !pr_progstate[prnum].progs->secondaryversion != PROG_SECONDARYVERSION32) - return NULL; - - num = *(int*)((char *)pr_progstate[prnum].progs + pr_progstate[prnum].progs->ofsfiles); - s = (includeddatafile_t *)((char *)pr_progstate[prnum].progs + pr_progstate[prnum].progs->ofsfiles+4); - while(num>0) - { - if (!strcmp(s->filename, fname)) - { - if (size) - *size = s->size; - if (!buffer) - return (char *)0xffffffff; - return QC_decode(progfuncs, s->compsize, s->size, s->compmethod, (char *)pr_progstate[prnum].progs+s->ofs, buffer); - } - - s++; - num--; - } - - if (size) - *size = 0; - return NULL; -} - -/* -char *filefromnewprogs(progfuncs_t *progfuncs, char *prname, char *fname, int *size, char *buffer) -{ - int num; - includeddatafile_t *s; - progstate_t progs; - if (!PR_ReallyLoadProgs(progfuncs, prname, -1, &progs, false)) - { - if (size) - *size = 0; - return NULL; - } - - if (progs.progs->version < PROG_EXTENDEDVERSION) - return NULL; - if (!progs.progs->ofsfiles) - return NULL; - - num = *(int*)((char *)progs.progs + progs.progs->ofsfiles); - s = (includeddatafile_t *)((char *)progs.progs + progs.progs->ofsfiles+4); - while(num>0) - { - if (!strcmp(s->filename, fname)) - { - if (size) - *size = s->size; - if (!buffer) - return (char *)0xffffffff; - return QC_decode(progfuncs, s->compsize, s->size, s->compmethod, (char *)progs.progs+s->ofs, buffer); - } - - s++; - num--; - } - - if (size) - *size = 0; - return NULL; -} -*/ diff --git a/misc/source/fteqcc-src/qcdecomp.c b/misc/source/fteqcc-src/qcdecomp.c deleted file mode 100644 index 8e0b2788..00000000 --- a/misc/source/fteqcc-src/qcdecomp.c +++ /dev/null @@ -1,1004 +0,0 @@ -#ifndef MINIMAL - -#include "progsint.h" -#include "setjmp.h" - -#define MAX_PARMS 8 - -// I put the following here to resolve "undefined reference to `__imp__vsnprintf'" with MinGW64 ~ Moodles -#ifdef _WIN32 - #if (_MSC_VER >= 1400) - //with MSVC 8, use MS extensions - #define snprintf linuxlike_snprintf_vc8 - int VARGS linuxlike_snprintf_vc8(char *buffer, int size, const char *format, ...) LIKEPRINTF(3); - #define vsnprintf(a, b, c, d) vsnprintf_s(a, b, _TRUNCATE, c, d) - #else - //msvc crap - #define snprintf linuxlike_snprintf - int VARGS linuxlike_snprintf(char *buffer, int size, const char *format, ...) LIKEPRINTF(3); - #define vsnprintf linuxlike_vsnprintf - int VARGS linuxlike_vsnprintf(char *buffer, int size, const char *format, va_list argptr); - #endif -#endif - -typedef struct QCC_type_s -{ - etype_t type; - - struct QCC_type_s *next; -// function types are more complex - struct QCC_type_s *aux_type; // return type or field type - int num_parms; // -1 = variable args -// struct QCC_type_s *parm_types[MAX_PARMS]; // only [num_parms] allocated - - int ofs; //inside a structure. - int size; - char *name; - -} QCC_type_t; - - -extern QCC_type_t *qcc_typeinfo; -extern int numtypeinfos; -extern int maxtypeinfos; -extern QCC_type_t *type_void;// = {ev_void/*, &def_void*/}; -extern QCC_type_t *type_string;// = {ev_string/*, &def_string*/}; -extern QCC_type_t *type_float;// = {ev_float/*, &def_float*/}; -extern QCC_type_t *type_vector;// = {ev_vector/*, &def_vector*/}; -extern QCC_type_t *type_entity;// = {ev_entity/*, &def_entity*/}; -extern QCC_type_t *type_field;// = {ev_field/*, &def_field*/}; -extern QCC_type_t *type_function;// = {ev_function/*, &def_function*/,NULL,&type_void}; -// type_function is a void() function used for state defs -extern QCC_type_t *type_pointer;// = {ev_pointer/*, &def_pointer*/}; -extern QCC_type_t *type_integer;// = {ev_integer/*, &def_integer*/}; -extern QCC_type_t *type_floatpointer; -extern QCC_type_t *type_intpointer; - -extern QCC_type_t *type_floatfield;// = {ev_field/*, &def_field*/, NULL, &type_float}; -QCC_type_t *QCC_PR_NewType (char *name, int basictype, pbool typedefed); - - -jmp_buf decompilestatementfailure; - -#if 0 -pbool Decompile(progfuncs_t *progfuncs, char *fname) -{ - return false; -} -#else - -QCC_type_t **ofstype; -qbyte *ofsflags; - -int SafeOpenWrite (char *filename, int maxsize); -void SafeWrite(int hand, void *buf, long count); -int SafeSeek(int hand, int ofs, int mode); -void SafeClose(int hand); -void VARGS writes(int hand, char *msg, ...) -{ - va_list va; - char buf[4192]; - - va_start(va, msg); - Q_vsnprintf (buf,sizeof(buf)-1, msg, va); - va_end(va); - - SafeWrite(hand, buf, strlen(buf)); -}; - -char *PR_UglyValueString (etype_t type, eval_t *val); -ddef16_t *ED_GlobalAtOfs16 (progfuncs_t *progfuncs, int ofs); -char *VarAtOfs(progfuncs_t *progfuncs, int ofs) -{ - static char buf [4192]; - ddef16_t *def; - int typen; - - if (ofsflags[ofs]&8) - def = ED_GlobalAtOfs16(progfuncs, ofs); - else - def = NULL; - if (!def) - { - if (ofsflags[ofs]&3) - { - if (ofstype[ofs]) - sprintf(buf, "_v_%s_%i", ofstype[ofs]->name, ofs); - else - sprintf(buf, "_v_%i", ofs); - } - else - { - if (ofstype[ofs]) - { - typen = ofstype[ofs]->type; - goto evaluateimmediate; - } - else - sprintf(buf, "_c_%i", ofs); - } - return buf; - } - if (!def->s_name[progfuncs->stringtable] || !strcmp(progfuncs->stringtable+def->s_name, "IMMEDIATE")) - { - if (current_progstate->types) - typen = current_progstate->types[def->type & ~DEF_SHARED].type; - else - typen = def->type & ~(DEF_SHARED|DEF_SAVEGLOBAL); - -evaluateimmediate: -// return PR_UglyValueString(def->type, (eval_t *)¤t_progstate->globals[def->ofs]); - switch(typen) - { - case ev_float: - sprintf(buf, "%f", G_FLOAT(ofs)); - return buf; - case ev_vector: - sprintf(buf, "\'%f %f %f\'", G_FLOAT(ofs), G_FLOAT(ofs+1), G_FLOAT(ofs+2)); - return buf; - case ev_string: - { - char *s, *s2; - s = buf; - *s++ = '\"'; - s2 = pr_strings+G_INT(ofs); - - - if (s2) - while(*s2) - { - if (*s2 == '\n') - { - *s++ = '\\'; - *s++ = 'n'; - s2++; - } - else if (*s2 == '\"') - { - *s++ = '\\'; - *s++ = '\"'; - s2++; - } - else if (*s2 == '\t') - { - *s++ = '\\'; - *s++ = 't'; - s2++; - } - else - *s++=*s2++; - } - *s++ = '\"'; - *s++ = '\0'; - } - return buf; - case ev_pointer: - sprintf(buf, "_c_pointer_%i", ofs); - return buf; - default: - sprintf(buf, "_c_%i", ofs); - return buf; - } - } - return def->s_name+progfuncs->stringtable; -} - - -int file; - -int ImmediateReadLater(progfuncs_t *progfuncs, progstate_t *progs, unsigned int ofs, int firstst) -{ - dstatement16_t *st; - if (ofsflags[ofs] & 8) - return false; //this is a global/local/pramater, not a temp - if (!(ofsflags[ofs] & 3)) - return false; //this is a constant. - for (st = &((dstatement16_t*)progs->statements)[firstst]; ; st++,firstst++) - { //if written, return false, if read, return true. - if (st->op >= OP_CALL0 && st->op <= OP_CALL8) - { - if (ofs == OFS_RETURN) - return false; - if (ofs < OFS_PARM0 + 3*((unsigned int)st->op - OP_CALL0)) - return true; - } - else if (pr_opcodes[st->op].associative == ASSOC_RIGHT) - { - if (ofs == st->b) - return false; - if (ofs == st->a) - return true; - } - else - { - if (st->a == ofs) - return true; - if (st->b == ofs) - return true; - if (st->c == ofs) - return false; - } - - if (st->op == OP_DONE || st->op == OP_RETURN) //we missed our chance. (return/done ends any code coherancy). - return false; - } - return false; -} -int ProductReadLater(progfuncs_t *progfuncs, progstate_t *progs, int stnum) -{ - dstatement16_t *st; - st = &((dstatement16_t*)progs->statements)[stnum]; - if (pr_opcodes[st->op].priority == -1) - { - if (st->op >= OP_CALL0 && st->op <= OP_CALL7) - return ImmediateReadLater(progfuncs, progs, OFS_RETURN, stnum+1); - return false;//these don't have products... - } - - if (pr_opcodes[st->op].associative == ASSOC_RIGHT) - return ImmediateReadLater(progfuncs, progs, st->b, stnum+1); - else - return ImmediateReadLater(progfuncs, progs, st->c, stnum+1); -} - -void WriteStatementProducingOfs(progfuncs_t *progfuncs, progstate_t *progs, int lastnum, int firstpossible, int ofs) //recursive, works backwards -{ - int i; - dstatement16_t *st; - ddef16_t *def; - if (ofs == 0) - longjmp(decompilestatementfailure, 1); - for (; lastnum >= firstpossible; lastnum--) - { - st = &((dstatement16_t*)progs->statements)[lastnum]; - if (st->op >= OP_CALL0 && st->op < OP_CALL7) - { - if (ofs != OFS_RETURN) - continue; - WriteStatementProducingOfs(progfuncs, progs, lastnum-1, firstpossible, st->a); - writes(file, "("); - for (i = 0; i < st->op - OP_CALL0; i++) - { - WriteStatementProducingOfs(progfuncs, progs, lastnum-1, firstpossible, OFS_PARM0 + i*3); - if (i != st->op - OP_CALL0-1) - writes(file, ", "); - } - writes(file, ")"); - return; - } - else if (pr_opcodes[st->op].associative == ASSOC_RIGHT) - { - if (st->b != ofs) - continue; - if (!ImmediateReadLater(progfuncs, progs, st->b, lastnum+1)) - { - writes(file, "("); - WriteStatementProducingOfs(progfuncs, progs, lastnum-1, firstpossible, st->b); - writes(file, " "); - writes(file, pr_opcodes[st->op].name); - writes(file, " "); - WriteStatementProducingOfs(progfuncs, progs, lastnum-1, firstpossible, st->a); - writes(file, ")"); - return; - } - WriteStatementProducingOfs(progfuncs, progs, lastnum-1, firstpossible, st->a); - return; - } - else - { - if (st->c != ofs) - continue; - - if (!ImmediateReadLater(progfuncs, progs, st->c, lastnum+1)) - { - WriteStatementProducingOfs(progfuncs, progs, lastnum-1, firstpossible, st->c); - writes(file, " = "); - } - writes(file, "("); - WriteStatementProducingOfs(progfuncs, progs, lastnum-1, firstpossible, st->a); - - if (!strcmp(pr_opcodes[st->op].name, ".")) - writes(file, pr_opcodes[st->op].name); //extra spaces around .s are ugly. - else - { - writes(file, " "); - writes(file, pr_opcodes[st->op].name); - writes(file, " "); - } - WriteStatementProducingOfs(progfuncs, progs, lastnum-1, firstpossible, st->b); - writes(file, ")"); - return; - } - } - - def = ED_GlobalAtOfs16(progfuncs, ofs); - if (def) - { - if (!strcmp(def->s_name+progfuncs->stringtable, "IMMEDIATE")) - writes(file, "%s", VarAtOfs(progfuncs, ofs)); - else - writes(file, "%s", progfuncs->stringtable+def->s_name); - } - else - writes(file, "%s", VarAtOfs(progfuncs, ofs)); -// longjmp(decompilestatementfailure, 1); -} - -int WriteStatement(progfuncs_t *progfuncs, progstate_t *progs, int stnum, int firstpossible) -{ - int count, skip; - dstatement16_t *st; - st = &((dstatement16_t*)progs->statements)[stnum]; - switch(st->op) - { - case OP_IFNOT_I: - count = (signed short)st->b; - writes(file, "if ("); - WriteStatementProducingOfs(progfuncs, progs, stnum, firstpossible, st->a); - writes(file, ")\r\n"); - writes(file, "{\r\n"); - firstpossible = stnum+1; - count--; - stnum++; - while(count) - { - if (ProductReadLater(progfuncs, progs, stnum)) - { - count--; - stnum++; - continue; - } - skip = WriteStatement(progfuncs, progs, stnum, firstpossible); - count-=skip; - stnum+=skip; - } - writes(file, "}\r\n"); - st = &((dstatement16_t*)progs->statements)[stnum]; - if (st->op == OP_GOTO) - { - count = (signed short)st->b; - count--; - stnum++; - - writes(file, "else\r\n"); - writes(file, "{\r\n"); - while(count) - { - if (ProductReadLater(progfuncs, progs, stnum)) - { - count--; - stnum++; - continue; - } - skip = WriteStatement(progfuncs, progs, stnum, firstpossible); - count-=skip; - stnum+=skip; - } - writes(file, "}\r\n"); - } - break; - case OP_IF_I: - longjmp(decompilestatementfailure, 1); - break; - case OP_GOTO: - longjmp(decompilestatementfailure, 1); - break; - case OP_RETURN: - case OP_DONE: - if (st->a) - WriteStatementProducingOfs(progfuncs, progs, stnum-1, firstpossible, st->a); - break; - case OP_CALL0: - case OP_CALL1: - case OP_CALL2: - case OP_CALL3: - case OP_CALL4: - case OP_CALL5: - case OP_CALL6: - case OP_CALL7: - WriteStatementProducingOfs(progfuncs, progs, stnum, firstpossible, OFS_RETURN); - writes(file, ";\r\n"); - break; - default: - if (pr_opcodes[st->op].associative == ASSOC_RIGHT) - WriteStatementProducingOfs(progfuncs, progs, stnum, firstpossible, st->b); - else - WriteStatementProducingOfs(progfuncs, progs, stnum, firstpossible, st->c); - writes(file, ";\r\n"); - break; - } - - return 1; -} - -void WriteAsmStatements(progfuncs_t *progfuncs, progstate_t *progs, int num, int f, char *functionname) -{ - int stn = progs->functions[num].first_statement; - QCC_opcode_t *op; - dstatement16_t *st = NULL; - eval_t *v; - - ddef16_t *def; - int ofs,i; - - int fileofs; - - if (!functionname && stn<0) - { - //we wrote this one... - return; - } - - if (stn>=0) - { - for (stn = progs->functions[num].first_statement; stn < (signed int)pr_progs->numstatements; stn++) - { - st = &((dstatement16_t*)progs->statements)[stn]; - if (st->op == OP_DONE || st->op == OP_RETURN) - { - if (!st->a) - writes(f, "void("); - else if (ofstype[st->a]) - { - writes(f, "%s", ofstype[st->a]->name); - writes(f, "("); - } - else - writes(f, "function("); - break; - } - } - st=NULL; - stn = progs->functions[num].first_statement; - } - else - writes(f, "function("); - for (ofs = progs->functions[num].parm_start, i = 0; i < progs->functions[num].numparms; i++, ofs+=progs->functions[num].parm_size[i]) - { - ofsflags[ofs] |= 4; - - def = ED_GlobalAtOfs16(progfuncs, ofs); - if (def && stn>=0) - { - if (st) - writes(f, ", "); - st = (void *)0xffff; - - if (!def->s_name[progfuncs->stringtable]) - { - char mem[64]; - sprintf(mem, "_p_%i", def->ofs); - def->s_name = (char*)malloc(strlen(mem)+1)-progfuncs->stringtable; - strcpy(def->s_name+progfuncs->stringtable, mem); - } - - if (current_progstate->types) - writes(f, "%s %s", current_progstate->types[def->type&~(DEF_SHARED|DEF_SAVEGLOBAL)].name, def->s_name); - else - switch(def->type&~(DEF_SHARED|DEF_SAVEGLOBAL)) - { - case ev_string: - writes(f, "%s %s", "string", progfuncs->stringtable+def->s_name); - break; - case ev_float: - writes(f, "%s %s", "float", progfuncs->stringtable+def->s_name); - break; - case ev_entity: - writes(f, "%s %s", "entity", progfuncs->stringtable+def->s_name); - break; - case ev_vector: - writes(f, "%s %s", "vector", progfuncs->stringtable+def->s_name); - break; - default: - writes(f, "%s %s", "randomtype", progfuncs->stringtable+def->s_name); - break; - } - } - } - for (ofs = progs->functions[num].parm_start+progs->functions[num].numparms, i = progs->functions[num].numparms; i < progs->functions[num].locals; i++, ofs+=1) - ofsflags[ofs] |= 4; - - if (!progfuncs->stringtable[progs->functions[num].s_name]) - { - char mem[64]; - if (!functionname) - { - sprintf(mem, "_bi_%i", num); - progs->functions[num].s_name = (char*)malloc(strlen(mem)+1)-progfuncs->stringtable; - strcpy(progs->functions[num].s_name+progfuncs->stringtable, mem); - } - else - { - progs->functions[num].s_name = (char*)malloc(strlen(functionname)+1)-progfuncs->stringtable; - strcpy(progs->functions[num].s_name+progfuncs->stringtable, functionname); - } - } - - writes(f, ") %s", progfuncs->stringtable+progs->functions[num].s_name); - - if (stn < 0) - { - stn*=-1; - writes(f, " = #%i;\r\n", stn); -/* - for (ofs = progs->functions[num].parm_start, i = 0; i < progs->functions[num].numparms; i++, ofs+=progs->functions[num].parm_size[i]) - { - def = ED_GlobalAtOfs16(progfuncs, ofs); - if (def) - { - def->ofs = 0xffff; - - if (progs->types) - { - if (progs->types[def->type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].type == ev_vector) - { - def = ED_GlobalAtOfs16(progfuncs, ofs); - def->ofs = 0xffff; - def = ED_GlobalAtOfs16(progfuncs, ofs+1); - def->ofs = 0xffff; - def = ED_GlobalAtOfs16(progfuncs, ofs+2); - def->ofs = 0xffff; - } - } - else if ((def->type & (~(DEF_SHARED|DEF_SAVEGLOBAL))) == ev_vector) - { - def = ED_GlobalAtOfs16(progfuncs, ofs); - def->ofs = 0xffff; - def = ED_GlobalAtOfs16(progfuncs, ofs+1); - def->ofs = 0xffff; - def = ED_GlobalAtOfs16(progfuncs, ofs+2); - def->ofs = 0xffff; - } - } - } - */ - return; - } - - if (functionname) //parsing defs - { - writes(f, ";\r\n"); - return; - } - - fileofs = SafeSeek(f, 0, SEEK_CUR); - if (setjmp(decompilestatementfailure)) - { - writes(f, "*/\r\n"); - // SafeSeek(f, fileofs, SEEK_SET); - writes(f, " = asm {\r\n"); - - stn = progs->functions[num].first_statement; - for (ofs = progs->functions[num].parm_start+progs->functions[num].numparms, i = progs->functions[num].numparms; i < progs->functions[num].locals; i++, ofs+=1) - { - def = ED_GlobalAtOfs16(progfuncs, ofs); - if (def) - { - v = (eval_t *)&((int *)progs->globals)[def->ofs]; - if (current_progstate->types) - writes(f, "\tlocal %s %s;\r\n", current_progstate->types[def->type&~(DEF_SHARED|DEF_SAVEGLOBAL)].name, def->s_name); - else - { - if (!progfuncs->stringtable[def->s_name]) - { - char mem[64]; - sprintf(mem, "_l_%i", def->ofs); - def->s_name = (char*)malloc(strlen(mem)+1)-progfuncs->stringtable; - strcpy(def->s_name+progfuncs->stringtable, mem); - } - - switch(def->type&~(DEF_SHARED|DEF_SAVEGLOBAL)) - { - case ev_string: - writes(f, "\tlocal %s %s;\r\n", "string", progfuncs->stringtable+def->s_name); - break; - case ev_float: - writes(f, "\tlocal %s %s;\r\n", "float", progfuncs->stringtable+def->s_name); - break; - case ev_entity: - writes(f, "\tlocal %s %s;\r\n", "entity", progfuncs->stringtable+def->s_name); - break; - case ev_vector: - if (v->_vector[0] || v->_vector[1] || v->_vector[2]) - writes(f, "\tlocal vector %s = '%f %f %f';\r\n", progfuncs->stringtable+def->s_name, v->_vector[0], v->_vector[1], v->_vector[2]); - else - writes(f, "\tlocal %s %s;\r\n", "vector", progfuncs->stringtable+def->s_name); - ofs+=2; //skip floats; - break; - default: - writes(f, "\tlocal %s %s;\r\n", "randomtype", progfuncs->stringtable+def->s_name); - break; - } - } - } - } - - while(1) - { - st = &((dstatement16_t*)progs->statements)[stn]; - if (!st->op) //end of function statement! - break; - op = &pr_opcodes[st->op]; - writes(f, "\t%s", op->opname); - - if (op->priority==-1&&op->associative==ASSOC_RIGHT) //last param is a goto - { - if (op->type_b == &type_void) - { - if (st->a) - writes(f, " %i", (signed short)st->a); - } - else if (op->type_c == &type_void) - { - if (st->a) - writes(f, " %s", VarAtOfs(progfuncs, st->a)); - if (st->b) - writes(f, " %i", (signed short)st->b); - } - else - { - if (st->a) - writes(f, " %s", VarAtOfs(progfuncs, st->a)); - if (st->b) - writes(f, " %s", VarAtOfs(progfuncs, st->b)); - if (st->c) //rightness means it uses a as c - writes(f, " %i", (signed short)st->c); - } - } - else - { - if (st->a) - { - if (op->type_a == NULL) - writes(f, " %i", (signed short)st->a); - else - writes(f, " %s", VarAtOfs(progfuncs, st->a)); - } - if (st->b) - { - if (op->type_b == NULL) - writes(f, " %i", (signed short)st->b); - else - writes(f, " %s", VarAtOfs(progfuncs, st->b)); - } - if (st->c && op->associative != ASSOC_RIGHT) //rightness means it uses a as c - { - if (op->type_c == NULL) - writes(f, " %i", (signed short)st->c); - else - writes(f, " %s", VarAtOfs(progfuncs, st->c)); - } - } - - writes(f, ";\r\n"); - - stn++; - } - } - else - { - if (!strcmp(progfuncs->stringtable+progs->functions[num].s_name, "SUB_Remove")) - file = 0; - file = f; - - writes(f, "/*\r\n"); - - writes(f, " =\r\n{\r\n"); - - for (ofs = progs->functions[num].parm_start+progs->functions[num].numparms, i = progs->functions[num].numparms; i < progs->functions[num].locals; i++, ofs+=1) - { - def = ED_GlobalAtOfs16(progfuncs, ofs); - if (def) - { - v = (eval_t *)&((int *)progs->globals)[def->ofs]; - if (current_progstate->types) - writes(f, "\tlocal %s %s;\r\n", current_progstate->types[def->type&~(DEF_SHARED|DEF_SAVEGLOBAL)].name, def->s_name); - else - { - if (!def->s_name[progfuncs->stringtable]) - { - char mem[64]; - sprintf(mem, "_l_%i", def->ofs); - def->s_name = (char*)malloc(strlen(mem)+1)-progfuncs->stringtable; - strcpy(def->s_name+progfuncs->stringtable, mem); - } - - switch(def->type&~(DEF_SHARED|DEF_SAVEGLOBAL)) - { - case ev_string: - writes(f, "\tlocal %s %s;\r\n", "string", progfuncs->stringtable+def->s_name); - break; - case ev_float: - writes(f, "\tlocal %s %s;\r\n", "float", progfuncs->stringtable+def->s_name); - break; - case ev_entity: - writes(f, "\tlocal %s %s;\r\n", "entity", progfuncs->stringtable+def->s_name); - break; - case ev_vector: - if (v->_vector[0] || v->_vector[1] || v->_vector[2]) - writes(f, "\tlocal vector %s = '%f %f %f';\r\n", progfuncs->stringtable+def->s_name, v->_vector[0], v->_vector[1], v->_vector[2]); - else - writes(f, "\tlocal %s %s;\r\n", "vector",progfuncs->stringtable+def->s_name); - ofs+=2; //skip floats; - break; - default: - writes(f, "\tlocal %s %s;\r\n", "randomtype", progfuncs->stringtable+def->s_name); - break; - } - } - } - } - - - for (stn = progs->functions[num].first_statement; stn < (signed int)pr_progs->numstatements; stn++) - { - if (ProductReadLater(progfuncs, progs, stn)) - continue; - - st = &((dstatement16_t*)progs->statements)[stn]; - if (!st->op) - break; - WriteStatement(progfuncs, progs, stn, progs->functions[num].first_statement); - } - - longjmp(decompilestatementfailure, 1); - } - writes(f, "};\r\n"); -} - -void FigureOutTypes(progfuncs_t *progfuncs) -{ - ddef16_t *def; - QCC_opcode_t *op; - unsigned int i,p; - dstatement16_t *st; - - int parmofs[8]; - - ofstype = realloc(ofstype, sizeof(*ofstype)*65535); - ofsflags = realloc(ofsflags, sizeof(*ofsflags)*65535); - - maxtypeinfos=256; - qcc_typeinfo = (void *)realloc(qcc_typeinfo, sizeof(QCC_type_t)*maxtypeinfos); - numtypeinfos = 0; - - memset(ofstype, 0, sizeof(*ofstype)*65535); - memset(ofsflags, 0, sizeof(*ofsflags)*65535); - - type_void = QCC_PR_NewType("void", ev_void, true); - type_string = QCC_PR_NewType("string", ev_string, true); - type_float = QCC_PR_NewType("float", ev_float, true); - type_vector = QCC_PR_NewType("vector", ev_vector, true); - type_entity = QCC_PR_NewType("entity", ev_entity, true); - type_field = QCC_PR_NewType("field", ev_field, false); - type_function = QCC_PR_NewType("function", ev_function, false); - type_pointer = QCC_PR_NewType("pointer", ev_pointer, false); - type_integer = QCC_PR_NewType("integer", ev_integer, true); - -// type_variant = QCC_PR_NewType("__variant", ev_variant); - - type_floatfield = QCC_PR_NewType("fieldfloat", ev_field, false); - type_floatfield->aux_type = type_float; - type_pointer->aux_type = QCC_PR_NewType("pointeraux", ev_float, false); - - type_function->aux_type = type_void; - - for (i = 0,st = pr_statements16; i < pr_progs->numstatements; i++,st++) - { - op = &pr_opcodes[st->op]; - if (st->op >= OP_CALL1 && st->op <= OP_CALL8) - { - for (p = 0; p < (unsigned int)st->op-OP_CALL0; p++) - { - ofstype[parmofs[p]] = ofstype[OFS_PARM0+p*3]; - } - } - else if (op->associative == ASSOC_RIGHT) - { //assignment - ofsflags[st->b] |= 1; - if (st->b >= OFS_PARM0 && st->b < RESERVED_OFS) - parmofs[(st->b-OFS_PARM0)/3] = st->a; - -// if (st->op != OP_STORE_F || st->b>RESERVED_OFS) //optimising compilers fix the OP_STORE_V, it's the storef that becomes meaningless (this is the only time that we need this sort of info anyway) - { - if (op->type_c && op->type_c != &type_void) - ofstype[st->a] = *op->type_c; - if (op->type_b && op->type_b != &type_void) - ofstype[st->b] = *op->type_b; - } - } - else if (op->type_c) - { - ofsflags[st->c] |= 2; - - if (st->c >= OFS_PARM0 && st->b < RESERVED_OFS) //too complicated - parmofs[(st->b-OFS_PARM0)/3] = 0; - -// if (st->op != OP_STORE_F || st->b>RESERVED_OFS) //optimising compilers fix the OP_STORE_V, it's the storef that becomes meaningless (this is the only time that we need this sort of info anyway) - { - if (op->type_a && op->type_a != &type_void) - ofstype[st->a] = *op->type_a; - if (op->type_b && op->type_b != &type_void) - ofstype[st->b] = *op->type_b; - if (op->type_c && op->type_c != &type_void) - ofstype[st->c] = *op->type_c; - } - } - } - - - for (i=0 ; inumglobaldefs ; i++) - { - def = &pr_globaldefs16[i]; - ofsflags[def->ofs] |= 8; - switch(def->type) - { - case ev_float: - ofstype[def->ofs] = type_float; - break; - case ev_string: - ofstype[def->ofs] = type_string; - break; - case ev_vector: - ofstype[def->ofs] = type_vector; - break; - default: - break; - } - } -} - -pbool Decompile(progfuncs_t *progfuncs, char *fname) -{ - extern progfuncs_t *qccprogfuncs; - unsigned int i; - unsigned int fld=0; - eval_t *v; -// char *filename; - int f, type; - - progstate_t progs, *op; - - qccprogfuncs = progfuncs; - op=current_progstate; - - if (!PR_ReallyLoadProgs(progfuncs, fname, -1, &progs, false)) - { - return false; - } - - f=SafeOpenWrite("qcdtest/defs.qc", 1024*512); - - writes(f, "//Decompiled code can contain little type info.\r\n#define NOWARNINGS\r\n"); - - FigureOutTypes(progfuncs); - - for (i = 1; i < progs.progs->numglobaldefs; i++) - { - if (!strcmp(progfuncs->stringtable+pr_globaldefs16[i].s_name, "IMMEDIATE")) - continue; - - if (ofsflags[pr_globaldefs16[i].ofs] & 4) - continue; //this is a local. - - if (current_progstate->types) - type = progs.types[pr_globaldefs16[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].type; - else - type = pr_globaldefs16[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL); - v = (eval_t *)&((int *)progs.globals)[pr_globaldefs16[i].ofs]; - - if (!progfuncs->stringtable[pr_globaldefs16[i].s_name]) - { - char mem[64]; - if (ofsflags[pr_globaldefs16[i].ofs] & 3) - { - ofsflags[pr_globaldefs16[i].ofs] &= ~8; - continue; //this is a constant... - } - - sprintf(mem, "_g_%i", pr_globaldefs16[i].ofs); - pr_globaldefs16[i].s_name = (char*)malloc(strlen(mem)+1)-progfuncs->stringtable; - strcpy(pr_globaldefs16[i].s_name+progfuncs->stringtable, mem); - } - - switch(type) - { - case ev_void: - writes(f, "void %s;\r\n", progfuncs->stringtable+pr_globaldefs16[i].s_name); - break; - case ev_string: - if (v->string && *(pr_strings+v->_int)) - writes(f, "string %s = \"%s\";\r\n", progfuncs->stringtable+pr_globaldefs16[i].s_name, pr_strings+v->_int); - else - writes(f, "string %s;\r\n", progfuncs->stringtable+pr_globaldefs16[i].s_name); - break; - case ev_float: - if (v->_float) - writes(f, "float %s = %f;\r\n", progfuncs->stringtable+pr_globaldefs16[i].s_name, v->_float); - else - writes(f, "float %s;\r\n", progfuncs->stringtable+pr_globaldefs16[i].s_name); - break; - case ev_vector: - if (v->_vector[0] || v->_vector[1] || v->_vector[2]) - writes(f, "vector %s = '%f %f %f';\r\n", progfuncs->stringtable+pr_globaldefs16[i].s_name, v->_vector[0], v->_vector[1], v->_vector[2]); - else - writes(f, "vector %s;\r\n", progfuncs->stringtable+pr_globaldefs16[i].s_name); - i+=3;//skip the floats - break; - case ev_entity: - writes(f, "entity %s;\r\n", progfuncs->stringtable+pr_globaldefs16[i].s_name); - break; - case ev_field: -//wierd - fld++; - if (!v->_int) - writes(f, "var "); - switch(pr_fielddefs16[fld].type) - { - case ev_string: - writes(f, ".string %s;", progfuncs->stringtable+pr_globaldefs16[i].s_name); - break; - - case ev_float: - writes(f, ".float %s;", progfuncs->stringtable+pr_globaldefs16[i].s_name); - break; - - case ev_vector: - writes(f, ".float %s;", progfuncs->stringtable+pr_globaldefs16[i].s_name); - break; - - case ev_entity: - writes(f, ".float %s;", progfuncs->stringtable+pr_globaldefs16[i].s_name); - break; - - case ev_function: - writes(f, ".void() %s;", progfuncs->stringtable+pr_globaldefs16[i].s_name); - break; - - default: - writes(f, "field %s;", progfuncs->stringtable+pr_globaldefs16[i].s_name); - break; - } - if (v->_int) - writes(f, "/* %i */", v->_int); - writes(f, "\r\n"); - break; - - case ev_function: -//wierd - WriteAsmStatements(progfuncs, &progs, ((int *)progs.globals)[pr_globaldefs16[i].ofs], f, pr_globaldefs16[i].s_name+progfuncs->stringtable); - break; - - case ev_pointer: - writes(f, "pointer %s;\r\n", progfuncs->stringtable+pr_globaldefs16[i].s_name); - break; - case ev_integer: - writes(f, "integer %s;\r\n", progfuncs->stringtable+pr_globaldefs16[i].s_name); - break; - - case ev_union: - writes(f, "union %s;\r\n", progfuncs->stringtable+pr_globaldefs16[i].s_name); - break; - case ev_struct: - writes(f, "struct %s;\r\n", progfuncs->stringtable+pr_globaldefs16[i].s_name); - break; - default: - break; - - } - } - - for (i = 0; i < progs.progs->numfunctions; i++) - { - WriteAsmStatements(progfuncs, &progs, i, f, NULL); - } - - SafeClose(f); - - current_progstate=op; - - return true; -} -#endif - -#endif diff --git a/misc/source/fteqcc-src/readme.txt b/misc/source/fteqcc-src/readme.txt deleted file mode 100644 index a254729a..00000000 --- a/misc/source/fteqcc-src/readme.txt +++ /dev/null @@ -1,50 +0,0 @@ -Readme for the FTE QCLib - -This library is a library for running QuakeC gamecode. It does not provide any builtins itself. - -Features: - * Multiple library instances, enabling server qc, client qc, and menu qc. There is no maximum instance limit other than memory. - - * Addons, for running multiple progs in any individual instance. - - * Field reassignment, allowing a single engine to support multiple subtly different QC APIs. Also makes additional fields easier. - - * Step-by-step debugging. Requires a text editor of some form, however. A printout of the current line is also useful of course. - - * 64bit support. All strings, globals, and fields are allocated in a consecutive addressable section of memory. This also allows pointers and secure access (not implemented yet, but should be relativly easy bar builtins, which are your responsability). - - * Multiple 'threads'. The library allows a builtin to make a duplicate of the current execution state, or to wipe the current state. This allows sleep commands and fork commands. How handy. - - * Integrated QC compiler. FTEQCC comes as part of qclib. By setting up an interface with a specific value, you can cause it to always run, or run only if it detects a source change. - - * Support for different sorts of progs. Namly Hexen2's, kkqwsv's bigprogs, and FTE's extended format with extra opcodes and possibly fully 32bit offsets. The use of kkqwsv's progs is not recommended - this might be removed at some point. - - - - -Quirks: - * don't use multiple instances of fteqcc at the same time. Compilation will fail. - * 64bit support requires all strings to be allocated by qclib itself, achivable via a method call. Compatability requires a certain ammount of caution. - * a fair number of methods are obsolete. - * An overuse of pointers in the API. There are some macros which you can use to hide some of the dereferences. - * kkqwsv progs are not reliable. Do not try saving the game. Avoid letting your users know of support. - - * Builtin structures are different from original quake. You'll need to convert the arguments to qclib style. This change was required for both multiple instances as well as addon support. It should be straightforward enough. - * Entity fields are accessed via a pointer from the edict_t structure. This was required to place entity fields within the 64bit accessable section. Changing a . to a -> is not a major issue though. However, there are a lot. do a find and replace of ->v. to ->v-> - * FTE's entities are numbers not pointers. This fact is not made into a big feature as it's kinda incompatable with standard quake. Please do not use numbers directly to refer to ents but instead use the EDICT_TO_PROGS macro which will give protection. This is consistant with standard quake. - - -Basic usage: - * refer to test.c for a sample on how to set up the library. - * refer to progslib.h for the things that I've forgotten to mention. - * Call the InitProgs function to get a handle to the instance. It takes a parameter which should be set up with some fields. You'll require ReadFile, FileSize, Abort and printf for basic execution. - * Call the configure function to say how much memory to use, and how many progs/addons to support. - * Load your progs via LoadProgs. Use a crc of 0 to use any. Otherwise progs will be rejected if it doesn't match. Give it a list of progs-specific builtins too. :) - * Before calling the spawn builtin, call the InitEnts method. It's parameter stating how many maximum entities to spawn. Using a really large quantity is not much of an issue, as they are allocated as required. - * Before calling InitEnts, you can tell the VM which fields your engine uses (state all basic ones or none). This will place the entity fields in the same order as your engine expects for entvars_t. - * Obtain pointers to globals, or just use the globals structure directly. - * Call the ExecuteProgram method to start execution. - * Call the FindFunction method to find a function to run in the first place. - * Call the 'globals' method to retrieve a pointer to the globals (you should always use PR_CURRENT here). Set the parameters with the G_INT/G_FLOAT macros and friends. Use OFS_PARM0 - OFS_PARM7 to set params before calling or read inside a builtin. Use OFS_RETURN to read the return value. These macros are hard coded to use a 'pr_globals' symbol, so avoid renaming builtin parameter names. - * Ask me on IRC when it all starts keeling over. - * These are the C files that form qclib: pr_edict.c pr_exec.c pr_multi.c initlib.c qcc_pr_comp.c qcc_pr_lex.c qccmain.c qcc_cmdlib.c comprout.c hash.c qcd_main.c qcdecomp.c diff --git a/misc/source/fteqcc-src/test.c b/misc/source/fteqcc-src/test.c deleted file mode 100644 index 8875d2eb..00000000 --- a/misc/source/fteqcc-src/test.c +++ /dev/null @@ -1,221 +0,0 @@ -//This is basically a sample program. -//It deomnstrates the code required to get qclib up and running. -//This code does not demonstrate entities, however. -//It does demonstrate the built in qc compiler, and does demonstrate a globals-only progs interface. -//It also demonstrates basic builtin(s). - - - -#include "progtype.h" -#include "progslib.h" - -#include -#include -#include -#include - - - - -//builtins and builtin management. -void PF_prints (progfuncs_t *prinst, struct globalvars_s *gvars) -{ - char *s; - s = prinst->VarString(prinst, 0); - - printf("%s", s); -} - -void PF_printv (progfuncs_t *prinst, struct globalvars_s *pr_globals) -{ - printf("%f %f %f\n", G_FLOAT(OFS_PARM0+0), G_FLOAT(OFS_PARM0+1), G_FLOAT(OFS_PARM0+2)); -} - -void PF_printf (progfuncs_t *prinst, struct globalvars_s *pr_globals) -{ - printf("%f\n", G_FLOAT(OFS_PARM0)); -} - - -void PF_bad (progfuncs_t *prinst, struct globalvars_s *gvars) -{ - printf("bad builtin\n"); -} - -builtin_t builtins[] = { - PF_bad, - PF_prints, - PF_printv, - PF_printf -}; - - - - -//Called when the qc library has some sort of serious error. -void Sys_Abort(char *s, ...) -{ //quake handles this with a longjmp. - va_list ap; - va_start(ap, s); - vprintf(s, ap); - va_end(ap); - exit(1); -} -//Called when the library has something to say. -//Kinda required for the compiler... -//Not really that useful for the normal vm. -int Sys_Printf(char *s, ...) -{ //look up quake's va function to find out how to deal with variable arguments properly. - return printf("%s", s); -} - -#include -//copy file into buffer. note that the buffer will have been sized to fit the file (obtained via FileSize) -unsigned char *Sys_ReadFile (char *fname, void *buffer, int buflen) -{ - int len; - FILE *f; - if (!strncmp(fname, "src/", 4)) - fname+=4; //skip the src part - f = fopen(fname, "rb"); - if (!f) - return NULL; - fseek(f, 0, SEEK_END); - len = ftell(f); - if (buflen < len) - return NULL; - fseek(f, 0, SEEK_SET); - fread(buffer, 1, len, f); - fclose(f); - return buffer; -} -//Finds the size of a file. -int Sys_FileSize (char *fname) -{ - int len; - FILE *f; - if (!strncmp(fname, "src/", 4)) - fname+=4; //skip the src part - f = fopen(fname, "rb"); - if (!f) - return -1; - fseek(f, 0, SEEK_END); - len = ftell(f); - fclose(f); - return len; -} -//Writes a file. -pbool Sys_WriteFile (char *fname, void *data, int len) -{ - FILE *f; - f = fopen(fname, "wb"); - if (!f) - return 0; - fwrite(data, 1, len, f); - fclose(f); - return 1; -} - -void runtest(char *progsname) -{ - progfuncs_t *pf; - func_t func; - progsnum_t pn; - - progparms_t ext; - memset(&ext, 0, sizeof(ext)); - - ext.progsversion = PROGSTRUCT_VERSION; - ext.ReadFile = Sys_ReadFile; - ext.FileSize= Sys_FileSize; - ext.Abort = Sys_Abort; - ext.printf = printf; - - ext.numglobalbuiltins = sizeof(builtins)/sizeof(builtins[0]); - ext.globalbuiltins = builtins; - - pf = InitProgs(&ext); - pf->Configure(pf, 1024*1024, 1); //memory quantity of 1mb. Maximum progs loadable into the instance of 1 -//If you support multiple progs types, you should tell the VM the offsets here, via RegisterFieldVar - pn = pf->LoadProgs(pf, progsname, 0, NULL, 0); //load the progs, don't care about the crc, and use those builtins. - if (pn < 0) - printf("test: Failed to load progs \"%s\"\n", progsname); - else - { -//allocate qc-acessable strings here for 64bit cpus. (allocate via AddString, tempstringbase is a holding area not used by the actual vm) -//you can call functions before InitEnts if you want. it's not really advised for anything except naming additional progs. This sample only allows one max. - - pf->InitEnts(pf, 10); //Now we know how many fields required, we can say how many maximum ents we want to allow. 10 in this case. This can be huge without too many problems. - -//now it's safe to ED_Alloc. - - func = pf->FindFunction(pf, "main", PR_ANY); //find the function 'main' in the first progs that has it. - if (!func) - printf("Couldn't find function\n"); - else - pf->ExecuteProgram(pf, func); //call the function - } - CloseProgs(pf); -} - - -//Run a compiler and nothing else. -//Note that this could be done with an autocompile of PR_COMPILEALWAYS. -void compile(int argc, char **argv) -{ - progfuncs_t *pf; - - progparms_t ext; - - if (0) - { - char *testsrcfile = //newstyle progs.src must start with a #. - //it's newstyle to avoid using multiple source files. - "#pragma PROGS_DAT \"testprogs.dat\"\r\n" - "//INTERMEDIATE FILE - EDIT TEST.C INSTEAD\r\n" - "\r\n" - "void(...) print = #1;\r\n" - "void() main =\r\n" - "{\r\n" - " print(\"hello world\\n\");\r\n" - "};\r\n"; - - //so that the file exists. We could insert it via the callbacks instead - Sys_WriteFile("progs.src", testsrcfile, strlen(testsrcfile)); - } - - memset(&ext, 0, sizeof(ext)); - ext.progsversion = PROGSTRUCT_VERSION; - ext.ReadFile = Sys_ReadFile; - ext.FileSize= Sys_FileSize; - ext.WriteFile= Sys_WriteFile; - ext.Abort = Sys_Abort; - ext.printf = printf; - - pf = InitProgs(&ext); - if (pf->StartCompile) - { - if (pf->StartCompile(pf, argc, argv)) - { - while(pf->ContinueCompile(pf) == 1) - ; - } - } - else - printf("no compiler in this qcvm build\n"); - CloseProgs(pf); -} - -int main(int argc, char **argv) -{ - if (argc < 2) - { - printf("Invalid arguments!\nPlease run as, for example:\n%s testprogs.dat -srcfile progs.src\nThe first argument is the name of the progs.dat to run, the remaining arguments are the qcc args to use", argv[0]); - return 0; - } - - compile(argc-1, argv+1); - runtest(argv[1]); - - return 0; -} diff --git a/misc/source/fteqcc-src/vc6/qcc.dsp b/misc/source/fteqcc-src/vc6/qcc.dsp deleted file mode 100644 index 62c6bfe3..00000000 --- a/misc/source/fteqcc-src/vc6/qcc.dsp +++ /dev/null @@ -1,256 +0,0 @@ -# Microsoft Developer Studio Project File - Name="qcc" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=qcc - Win32 GUIDebug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "qcc.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "qcc.mak" CFG="qcc - Win32 GUIDebug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "qcc - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "qcc - Win32 Debug" (based on "Win32 (x86) Console Application") -!MESSAGE "qcc - Win32 GUIDebug" (based on "Win32 (x86) Console Application") -!MESSAGE "qcc - Win32 GUIRelease" (based on "Win32 (x86) Console Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "qcc - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "QCCONLY" /YX /FD /c -# ADD BASE RSC /l 0x809 /d "NDEBUG" -# ADD RSC /l 0x809 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 ../../libs/zlib.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"../../fteqcc.exe" - -!ELSEIF "$(CFG)" == "qcc - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "QCCONLY" /FR /YX /FD /GZ /c -# ADD BASE RSC /l 0x809 /d "_DEBUG" -# ADD RSC /l 0x809 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 ../../libs/zlib.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"../../fteqcc_dbg.exe" /pdbtype:sept - -!ELSEIF "$(CFG)" == "qcc - Win32 GUIDebug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "qcc___Win32_GUIDebug" -# PROP BASE Intermediate_Dir "qcc___Win32_GUIDebug" -# PROP BASE Ignore_Export_Lib 0 -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "qcc___Win32_GUIDebug" -# PROP Intermediate_Dir "qcc___Win32_GUIDebug" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "QCCONLY" /FR /YX /FD /GZ /c -# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "QCCONLY" /FR /YX /FD /GZ /c -# ADD BASE RSC /l 0x809 /d "_DEBUG" -# ADD RSC /l 0x809 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 ../../libs/zlib.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"../../../fteqcc.exe" /pdbtype:sept -# ADD LINK32 ../../libs/zlib.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib comctl32.lib comdlg32.lib /nologo /subsystem:windows /debug /machine:I386 /out:"..\..\fteqcc.exe" /pdbtype:sept -# SUBTRACT LINK32 /pdb:none - -!ELSEIF "$(CFG)" == "qcc - Win32 GUIRelease" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "qcc___Win32_GUIRelease0" -# PROP BASE Intermediate_Dir "qcc___Win32_GUIRelease0" -# PROP BASE Ignore_Export_Lib 0 -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "qcc___Win32_GUIRelease0" -# PROP Intermediate_Dir "qcc___Win32_GUIRelease0" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "QCCONLY" /YX /FD /c -# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "QCCONLY" /FR /YX /FD /c -# ADD BASE RSC /l 0x809 /d "NDEBUG" -# ADD RSC /l 0x809 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 ../../libs/zlib.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"../../../fteqcc.exe" -# ADD LINK32 ../../libs/zlib.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib comctl32.lib /nologo /subsystem:windows /machine:I386 /out:"../../fteqccgui.exe" -# SUBTRACT LINK32 /pdb:none - -!ENDIF - -# Begin Target - -# Name "qcc - Win32 Release" -# Name "qcc - Win32 Debug" -# Name "qcc - Win32 GUIDebug" -# Name "qcc - Win32 GUIRelease" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=..\Comprout.c -# End Source File -# Begin Source File - -SOURCE=..\hash.c -# End Source File -# Begin Source File - -SOURCE=..\qcc_cmdlib.c -# End Source File -# Begin Source File - -SOURCE=..\qcc_gtk.c -# PROP Exclude_From_Build 1 -# End Source File -# Begin Source File - -SOURCE=..\qcc_pr_comp.c -# End Source File -# Begin Source File - -SOURCE=..\qcc_pr_lex.c -# End Source File -# Begin Source File - -SOURCE=..\qccgui.c - -!IF "$(CFG)" == "qcc - Win32 Release" - -# PROP Exclude_From_Build 1 - -!ELSEIF "$(CFG)" == "qcc - Win32 Debug" - -# PROP Exclude_From_Build 1 - -!ELSEIF "$(CFG)" == "qcc - Win32 GUIDebug" - -!ELSEIF "$(CFG)" == "qcc - Win32 GUIRelease" - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\qccguistuff.c - -!IF "$(CFG)" == "qcc - Win32 Release" - -# PROP Exclude_From_Build 1 - -!ELSEIF "$(CFG)" == "qcc - Win32 Debug" - -# PROP Exclude_From_Build 1 - -!ELSEIF "$(CFG)" == "qcc - Win32 GUIDebug" - -!ELSEIF "$(CFG)" == "qcc - Win32 GUIRelease" - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\QccMain.c -# End Source File -# Begin Source File - -SOURCE=..\qcctui.c - -!IF "$(CFG)" == "qcc - Win32 Release" - -!ELSEIF "$(CFG)" == "qcc - Win32 Debug" - -!ELSEIF "$(CFG)" == "qcc - Win32 GUIDebug" - -# PROP Exclude_From_Build 1 - -!ELSEIF "$(CFG)" == "qcc - Win32 GUIRelease" - -# PROP Exclude_From_Build 1 - -!ENDIF - -# End Source File -# Begin Source File - -SOURCE=..\qcd_main.c -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=..\gui.h -# End Source File -# Begin Source File - -SOURCE=..\pr_comp.h -# End Source File -# Begin Source File - -SOURCE=..\qcc.h -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project diff --git a/misc/source/fteqcc-src/vc6/qcc.dsw b/misc/source/fteqcc-src/vc6/qcc.dsw deleted file mode 100644 index 4b65a4d2..00000000 --- a/misc/source/fteqcc-src/vc6/qcc.dsw +++ /dev/null @@ -1,29 +0,0 @@ -Microsoft Developer Studio Workspace File, Format Version 6.00 -# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! - -############################################################################### - -Project: "qcc"=".\qcc.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Global: - -Package=<5> -{{{ -}}} - -Package=<3> -{{{ -}}} - -############################################################################### - diff --git a/misc/source/gmqcc-src/.travis.yml b/misc/source/gmqcc-src/.travis.yml new file mode 100644 index 00000000..fdadff81 --- /dev/null +++ b/misc/source/gmqcc-src/.travis.yml @@ -0,0 +1,14 @@ +language: c +compiler: + - gcc + - clang +# Change this to your needs +script: make && make check +notifications: + irc: + channels: + - "irc.freenode.org#kf-engine" + template: + - "[%{commit} : %{author}] %{message}" + - "%{build_url}" + skip_join: true diff --git a/misc/source/gmqcc-src/AUTHORS b/misc/source/gmqcc-src/AUTHORS new file mode 100644 index 00000000..16255d2b --- /dev/null +++ b/misc/source/gmqcc-src/AUTHORS @@ -0,0 +1,9 @@ +Authors: + Dale `graphitemaster` Weiler - Charismatic Visionary / Programmer + Wolfgang `Blub\w` Bumiller - Main Programmer + +Thanks to: + Forest `LordHavoc` Hale - Technical support and assistance + Rudolf `divVerent` Polzer - Technical support and assistance + Matthias `matthiaskrgr` Krüger - Miscellaneous assistance + Samual `Samual` Lenks - Preprocessor assistance diff --git a/misc/source/gmqcc-src/CHANGES b/misc/source/gmqcc-src/CHANGES new file mode 100644 index 00000000..b7499a1a --- /dev/null +++ b/misc/source/gmqcc-src/CHANGES @@ -0,0 +1,113 @@ +Release v0.3.0 + * Language: + - Return assignments, the ability to assign to the return keyword + as if it were a local variable. + * Compilation: + - Optimized memory usage (now uses on average %3 less memory for + compilation). + - Fixed dotranslate (translatable strings) + * QCVM: + - Escape strings for -printdefs + * Commandline: + - Added statistic dumps, gives information about the number of used + hashtables, vectors, and number of unique sizes of vectors and + hashtables. The amount of memory used for vectors. As well as the + number of strdups used in total for compilation. + * Testsuite: + - Fixed a floating point exception rasied by modulo operation in + -memchk. + * Build: + - Added gentoo ebuilds. + - Added win32 Makefile for building win32 packages. + - Added slackware pkg build files + +2012-04-27 v0.2.9 + * Preprocessor: + - __VA_ARGS__ support + _ __VA_ARGS__ indexing + - Predefined macros like __DATE__, __TIME__, ... + (check the manpage for a full list) + - Signed numbers as single token in the + - Fixes some issues with #if operations on macros. + - Speed improvements + * Language: + - Untyped `nil` keyword. + - Removed the `noreturn` keyword. + - Added generic attribute syntax and reintroduced `noreturn` + as [[noreturn]]. + - Added [[deprecated]] and [[deprecated("message")]]. + - Support for `static` variables in functions. + - Support for labeled loops. + - UTF-8 Support + - enum support: without enum-types + (ie no `typedef enum { } foo;`) + - Accessing vector components via the dot operator on all + expressions. Eg: (3 * v).y + - Type restricted variadict parameters: + ie: void print(string...); + - Accessing varargs from QC via: ...(index, type) + - New operators: ** (exponentiation), % (modulo), etc + - Enumeration attributes: flag, reverse + * Compilation: + - Various optimizations and progs-size reductions. + - A new spell-checking algorithm tries to hint you at existing + variables on error. + - Some problems with VM related vector-instructions issues + have been solved in both DP and our own executor. A new + compatbility option (enabled by default) has been added for + now: -flegacy-vector-maths + - Compiler intrinsics: __builtin_floor, __builtin_mod, + __builtin_exp, __builtin_isnan + - Improved memory tracing + - Speed improvements + * QCVM: + - Improved commandline argument handling. + - More builtins: sqrt(), normalize(), floor() + * Commandline: + - Nicer memory dumps + - Support for making individual warnings an error + - via -Werror- + - added --add-info + * Testsuite: + - Support for QCFLAGS to run tests with several additional + flags. + - Added support for preprocessor tests + - Added preprocessor tests + - Added defs.qh (auto included) for qcvm definitions + * Syntax Highlighting: + - Added various syntax highlighting description files for + various text editors / integrated development envirorments, + including support for: geany, kate, kwrite, kdevelop, QtCreator, + gtksourceview, gedit, sany, nano, jedit + * Build: + - Build scripts for building debian, archlinux and archbsd + packages for x86, and x86_64. + - Makefile targets for gource visualization, and render of + gource visualization. + + +2012-12-27 Hotfix v0.2.2 + * Liferanges + * Crashes + +2012-12-23 Hotfix v0.2.1 + * General bugfixes + +2012-12-23 Release 0.2 + * Preprocessor: + - Added xonotic compatible preprocessor. + * Language + - Basic xonotic compatibility + - Array support + - Added fteqcc's string escape sequences. + - Support for `noref`. + - Support for `goto` with labels like in fteqcc. + - `break` and `continue`. + - Short circuit logic. + - Support for translatable strings via _("str") like in + fteqcc. + * Compilation + - Warnings about uninitialized values + +2012-11-17 Release 0.1 + * Compiles id1 code diff --git a/misc/source/gmqcc-src/INSTALL b/misc/source/gmqcc-src/INSTALL new file mode 100644 index 00000000..14afea4d --- /dev/null +++ b/misc/source/gmqcc-src/INSTALL @@ -0,0 +1,44 @@ + Installing gmqcc + +1. Prerequisites + - A C-Compiler such as gcc or clang + - GNU Make. This document will assume GNU-Make to be executed via + `make'. On BSD systems you probably have to use `gmake' instead. + +2. Compilation + Run the GNU make program `make' or `gmake'. + + make + + If no error appears, the following binary files will have been + created: + - gmqcc + - qcvm + +3. Installation + The `install' target will install the 2 binaries to /usr/local/bin + by default. + The Makefile honors the following variables: + + - DESTDIR: The installation directory root. + - PREFIX: The installation prefix, default: /usr/local + - BINDIR: Directory for binary executables, + deafult: $PREFIX/bin + + To install to /usr/local run: + + make install + + To install to /usr run: + + make PREFIX=/usr install + + To install to a package-staging directory such as $pkgdir when + writing a build script file: + + make DESTDIR=$pkgdir install + + + + ArchLinux PKGBUILDs (release and git build) can be found in the + respective folders in ./distro/arch diff --git a/misc/source/gmqcc-src/LICENSE b/misc/source/gmqcc-src/LICENSE new file mode 100644 index 00000000..3fd91811 --- /dev/null +++ b/misc/source/gmqcc-src/LICENSE @@ -0,0 +1,21 @@ +Copyright (C) 2012, 2013 + Dale Weiler + Wolfgang Bumiller + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/misc/source/gmqcc-src/Makefile b/misc/source/gmqcc-src/Makefile new file mode 100644 index 00000000..2e5c1eea --- /dev/null +++ b/misc/source/gmqcc-src/Makefile @@ -0,0 +1,253 @@ +DESTDIR := +OPTIONAL:= +PREFIX := /usr/local +BINDIR := $(PREFIX)/bin +DATADIR := $(PREFIX)/share +MANDIR := $(DATADIR)/man + +UNAME ?= $(shell uname) +CYGWIN = $(findstring CYGWIN, $(UNAME)) +MINGW = $(findstring MINGW32, $(UNAME)) + +CC ?= clang +# linker flags and optional additional libraries if required +LDFLAGS += +LIBS += -lm + +CFLAGS += -Wall -Wextra -Werror -fno-strict-aliasing $(OPTIONAL) +ifneq ($(shell git describe --always 2>/dev/null),) + CFLAGS += -DGMQCC_GITINFO="\"$(shell git describe --always)\"" +endif +#turn on tons of warnings if clang is present +# but also turn off the STUPID ONES +ifeq ($(CC), clang) + CFLAGS += \ + -Weverything \ + -Wno-padded \ + -Wno-format-nonliteral \ + -Wno-disabled-macro-expansion \ + -Wno-conversion \ + -Wno-missing-prototypes \ + -Wno-float-equal \ + -Wno-unknown-warning-option \ + -Wstrict-prototypes +else + #Tiny C Compiler doesn't know what -pedantic-errors is + # and instead of ignoring .. just errors. + ifneq ($(CC), tcc) + CFLAGS += -Wstrict-prototypes -pedantic-errors + else + CFLAGS += -Wno-pointer-sign -fno-common + endif +endif + +ifeq ($(track), no) + CFLAGS += -DNOTRACK +endif + +OBJ_D = util.o code.o ast.o ir.o conout.o ftepp.o opts.o fs.o utf8.o correct.o stat.o +OBJ_P = util.o fs.o conout.o opts.o pak.o stat.o +OBJ_T = test.o util.o conout.o fs.o stat.o +OBJ_C = main.o lexer.o parser.o fs.o stat.o +OBJ_X = exec-standalone.o util.o conout.o fs.o stat.o + +#we have duplicate object files when dealing with creating a simple list +#for dependinces. To combat this we use some clever recrusive-make to +#filter the list and remove duplicates which we use for make depend +RMDUP = $(if $1,$(firstword $1) $(call RMDUP,$(filter-out $(firstword $1),$1))) +DEPS := $(call RMDUP, $(OBJ_D) $(OBJ_P) $(OBJ_T) $(OBJ_C) $(OBJ_X)) + +ifneq ("$(CYGWIN)", "") + #nullify the common variables that + #most *nix systems have (for windows) + PREFIX := + BINDIR := + DATADIR := + MANDIR := + QCVM = qcvm.exe + GMQCC = gmqcc.exe + TESTSUITE = testsuite.exe + PAK = pak.exe +else +ifneq ("$(MINGW)", "") + #nullify the common variables that + #most *nix systems have (for windows) + PREFIX := + BINDIR := + DATADIR := + MANDIR := + QCVM = qcvm.exe + GMQCC = gmqcc.exe + TESTSUITE = testsuite.exe + PAK = gmqpak.exe +else + QCVM = qcvm + GMQCC = gmqcc + TESTSUITE = testsuite + PAK = gmqpak +endif +endif + +#gource flags +GOURCEFLAGS= \ + --date-format "%d %B, %Y" \ + --seconds-per-day 0.01 \ + --auto-skip-seconds 1 \ + --title "GMQCC" \ + --key \ + --camera-mode overview \ + --highlight-all-users \ + --file-idle-time 0 \ + --hide progress,mouse \ + --stop-at-end \ + --max-files 99999999999 \ + --max-file-lag 0.000001 \ + --bloom-multiplier 1.3 \ + --logo doc/html/gmqcc.png \ + -1280x720 + +#ffmpeg flags for gource +FFMPEGFLAGS= \ + -y \ + -r 60 \ + -f image2pipe \ + -vcodec ppm \ + -i - \ + -vcodec libx264 \ + -preset ultrafast \ + -crf 1 \ + -threads 0 \ + -bf 0 + +#splint flags +SPLINTFLAGS = \ + -redef \ + -noeffect \ + -nullderef \ + -usedef \ + -type \ + -mustfreeonly \ + -nullstate \ + -varuse \ + -mustfreefresh \ + -compdestroy \ + -compmempass \ + -nullpass \ + -onlytrans \ + -predboolint \ + -boolops \ + -incondefs \ + -macroredef \ + -retvalint \ + -nullret \ + -predboolothers \ + -globstate \ + -dependenttrans \ + -branchstate \ + -compdef \ + -temptrans \ + -usereleased \ + -warnposix \ + +charindex \ + -kepttrans \ + -unqualifiedtrans \ + +matchanyintegral \ + +voidabstract \ + -nullassign \ + -unrecog \ + -casebreak \ + -retvalbool \ + -retvalother \ + -mayaliasunique \ + -realcompare \ + -observertrans \ + -abstract \ + -statictrans \ + -castfcnptr + +#standard rules +default: all +%.o: %.c + $(CC) -c $< -o $@ $(CPPFLAGS) $(CFLAGS) + +exec-standalone.o: exec.c + $(CC) -c $< -o $@ $(CPPFLAGS) $(CFLAGS) -DQCVM_EXECUTOR=1 + +$(QCVM): $(OBJ_X) + $(CC) -o $@ $^ $(LDFLAGS) $(LIBS) + +$(GMQCC): $(OBJ_C) $(OBJ_D) + $(CC) -o $@ $^ $(LDFLAGS) $(LIBS) + +$(TESTSUITE): $(OBJ_T) + $(CC) -o $@ $^ $(LDFLAGS) $(LIBS) + +$(PAK): $(OBJ_P) + $(CC) -o $@ $^ $(LDFLAGS) + +all: $(GMQCC) $(QCVM) $(TESTSUITE) $(PAK) + +check: all + @ ./$(TESTSUITE) +test: all + @ ./$(TESTSUITE) + +clean: + rm -f *.o $(GMQCC) $(QCVM) $(TESTSUITE) $(PAK) *.dat gource.mp4 *.exe + +splint: + @ splint $(SPLINTFLAGS) *.c *.h + +gource: + @ gource $(GOURCEFLAGS) + +gource-record: + @ gource $(GOURCEFLAGS) -o - | ffmpeg $(FFMPEGFLAGS) gource.mp4 + +depend: + @makedepend -Y -w 65536 2> /dev/null \ + $(subst .o,.c,$(DEPS)) + +#install rules +install: install-gmqcc install-qcvm install-gmqpak install-doc +install-gmqcc: $(GMQCC) + install -d -m755 $(DESTDIR)$(BINDIR) + install -m755 $(GMQCC) $(DESTDIR)$(BINDIR)/$(GMQCC) +install-qcvm: $(QCVM) + install -d -m755 $(DESTDIR)$(BINDIR) + install -m755 $(QCVM) $(DESTDIR)$(BINDIR)/$(QCVM) +install-gmqpak: $(PAK) + install -d -m755 $(DESTDIR)$(BINDIR) + install -m755 $(PAK) $(DESTDIR)$(BINDIR)/$(PAK) +install-doc: + install -d -m755 $(DESTDIR)$(MANDIR)/man1 + install -m644 doc/gmqcc.1 $(DESTDIR)$(MANDIR)/man1/ + install -m644 doc/qcvm.1 $(DESTDIR)$(MANDIR)/man1/ + install -m644 doc/gmqpak.1 $(DESTDIR)$(MANDIR)/man1/ + +uninstall: + rm -f $(DESTDIR)$(BINDIR)/gmqcc + rm -f $(DESTDIR)$(BINDIR)/qcvm + rm -f $(DESTDIR)$(BINDIR)/gmqpak + rm -f $(DESTDIR)$(MANDIR)/man1/doc/gmqcc.1 + rm -f $(DESTDIR)$(MANDIR)/man1/doc/qcvm.1 + rm -f $(DESTDIR)$(MANDIR)/man1/doc/gmqpak.1 + +# DO NOT DELETE + +util.o: gmqcc.h opts.def +code.o: gmqcc.h opts.def +ast.o: gmqcc.h opts.def ast.h ir.h +ir.o: gmqcc.h opts.def ir.h +conout.o: gmqcc.h opts.def +ftepp.o: gmqcc.h opts.def lexer.h +opts.o: gmqcc.h opts.def +fs.o: gmqcc.h opts.def +utf8.o: gmqcc.h opts.def +correct.o: gmqcc.h opts.def +stat.o: gmqcc.h opts.def +pak.o: gmqcc.h opts.def +test.o: gmqcc.h opts.def +main.o: gmqcc.h opts.def lexer.h +lexer.o: gmqcc.h opts.def lexer.h +parser.o: gmqcc.h opts.def lexer.h ast.h ir.h intrin.h diff --git a/misc/source/gmqcc-src/README b/misc/source/gmqcc-src/README new file mode 100644 index 00000000..bd71be04 --- /dev/null +++ b/misc/source/gmqcc-src/README @@ -0,0 +1,14 @@ +GMQCC: An improved Quake C compiler + +For licensing: see the LICENSE file. +For installation notes: see the INSTALL file. +For a list of authors: see the AUTHORS file. +For a list of changes: see the CHANGES file. + +For documentation: + See the manpages, or visit the documentation online at + http://graphitemaster.github.com/gmqcc/doc.html + +For syntax highlighting description files, or information +regarding how to install them: + See the README in syntax directory diff --git a/misc/source/gmqcc-src/TODO b/misc/source/gmqcc-src/TODO new file mode 100644 index 00000000..94163afe --- /dev/null +++ b/misc/source/gmqcc-src/TODO @@ -0,0 +1,157 @@ +GMQCC is quite feature complete. But that doesn't address the fact that +it can be improved. This is a list of things that we'd like to support +in the distant future. When the time comes, we can just select a topic +from here and open a ticket for it on the issue tracker. But for the +meantime, this is sort of a cultivating flat file database. + +Optimizations: + The following are optimizations that can be implemented after the + transformation into static-single assignment (SSA). + + Global Value Numbering: + Eliminate redundancy by constructing a value graph of the source + then determining which values are computed by equivalent expressions. + Similar to Common Subexpression Elimination (CSE), however expressions + are determined via underlying equivalence, opposed to lexically identical + expressions (CSE). + + Spare Conditional Constant Propagation: + Simultaneously remove dead code and propagates constants. This is + not the same as individual dead code elimination and constant propagation + passes. This is multipass. + + The following are optimizations that can be implemented before the + transformation into a binary (code generator). + + Code factoring: + The process of finding sequences of code that are identical, + or can be parameterized or reordered to be identical. + Which can be replaced with calls to a shared subroutine. To + reduce duplicated code. (Size optimization) + + The following are optimizations that can be implemented anywhere, ideally + these are functional language optimizations. + + Removing Recursion: + Tail recursive algorithms can be converted to iteration, which + does not have to have call overhead. + + +Language Features: + The following are language features that we'd like to see implemented in the + future. + + Enumerations: + Like C + + AST Macros: + Macros with sanity. Not textual substiution. + + Classes: + Like C++, but minus the stupidity: + - No type operator overloads + - Keep operator overloading for basic operators though. + - No inheritance + - No virtuals / pure virtuals + - Essentially "C structs but with operators" :) + + Arrays: + They're currently implemented, but support in the engine + plus implicit bounds checks (and ability to turn the bounds + checking off) + + Exceptions: + I feel like all languages suck at implementing this. This would + require support from the engine, but it would help catch bugs. We + could make it fast using a neat method of "frame pointers". + + Overloaded Functions: + Ability to make individual functions with the same name, but take + different amount of arguments or type of arguments. + + Default Argument Substiution: + Ability to specify default values for arguments in functions. + void foo(string bar, string baz="default"); + Supplying just one argument will expand the second argument to + become "default", otherwise if two arguments are specified then + the "default" string is overrode with what ever the user passes. + + Character Type: + A char type would be nice to have. Essentially implemented as a + string, we can both "get" and "set" indices inside strings with + the help of builtin functions. + + { + string foo = "test"; + foo[0] = 'r'; + + print("it's time to ", foo); + } + + Array Accessor With C-Semantics: + Also the ability to use them as array accessors: + + { + float hugearray['Z']; + + hugearray['a'] = 100.0f; + } + + Keep existing "pointer-like" semantics as well. In C arrays + simple work as pointers, a[1] -> *(a+1), or 1[a] -> *(1+a) + so we should allow both forms of syntax. As well as operand + reversal. + + { + float h['Z']; + *(h+'a') = 100; + *('a'+h) = 'a'[h]; + } + + FTEQCC Inline Assembly: + This is still up for debate, mainly because a) it's syntax is + just utter crap. b) If we do an assembler, it should be nice. + we could provide a -std=fteqcc for the assembler itself :P + just like the compiler; although I think that's just insane. + + Please see Assembler below. + + Namespaces: + There is already a ticket open on this. They'd work just like C++ + identically even. + +Standalone QCVM: + The following are QCVM additions: + + Proper ASM disassembly: + Proper disassembly of compiled .dat files. Annotated if possible + when -g (is used during compilation) + + Debugging: + A step-through debugger -d (with separate compilation as well) + Called -> qcdb Optionally alias to qcvm -d :) + + We should be able to see the assembly and source it matches to + and the state of OFS_* and calls. + +Testsuite: + The following are things we'd like to see added to the testsuite + in the distant future: + + Multithreading: + Chances are when we start adding more and more tests, executing + them individually will be midly slow (even if that's a whole minute) + It would be nice to add a -j paramater to allow multiple threads to + be used and so we can execute many tests in parallel. + + Interface: + Ability to select individual tests, or set parameters manually + opposed to using the static task-template files. (A method to + override them rather). + + +Assembler: + Possibly support for a future assembler for QCASM. But we're not + entirely sure if it makes sense. + + diff --git a/misc/source/gmqcc-src/ast.c b/misc/source/gmqcc-src/ast.c new file mode 100644 index 00000000..d845eb24 --- /dev/null +++ b/misc/source/gmqcc-src/ast.c @@ -0,0 +1,3150 @@ +/* + * Copyright (C) 2012, 2013 + * Wolfgang Bumiller + * Dale Weiler + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include +#include + +#include "gmqcc.h" +#include "ast.h" + +#define ast_instantiate(T, ctx, destroyfn) \ + T* self = (T*)mem_a(sizeof(T)); \ + if (!self) { \ + return NULL; \ + } \ + ast_node_init((ast_node*)self, ctx, TYPE_##T); \ + ( (ast_node*)self )->destroy = (ast_node_delete*)destroyfn + +/* + * forward declarations, these need not be in ast.h for obvious + * static reasons. + */ +static bool ast_member_codegen(ast_member*, ast_function*, bool lvalue, ir_value**); +static void ast_array_index_delete(ast_array_index*); +static bool ast_array_index_codegen(ast_array_index*, ast_function*, bool lvalue, ir_value**); +static void ast_store_delete(ast_store*); +static bool ast_store_codegen(ast_store*, ast_function*, bool lvalue, ir_value**); +static void ast_ifthen_delete(ast_ifthen*); +static bool ast_ifthen_codegen(ast_ifthen*, ast_function*, bool lvalue, ir_value**); +static void ast_ternary_delete(ast_ternary*); +static bool ast_ternary_codegen(ast_ternary*, ast_function*, bool lvalue, ir_value**); +static void ast_loop_delete(ast_loop*); +static bool ast_loop_codegen(ast_loop*, ast_function*, bool lvalue, ir_value**); +static void ast_breakcont_delete(ast_breakcont*); +static bool ast_breakcont_codegen(ast_breakcont*, ast_function*, bool lvalue, ir_value**); +static void ast_switch_delete(ast_switch*); +static bool ast_switch_codegen(ast_switch*, ast_function*, bool lvalue, ir_value**); +static void ast_label_delete(ast_label*); +static void ast_label_register_goto(ast_label*, ast_goto*); +static bool ast_label_codegen(ast_label*, ast_function*, bool lvalue, ir_value**); +static bool ast_goto_codegen(ast_goto*, ast_function*, bool lvalue, ir_value**); +static void ast_goto_delete(ast_goto*); +static void ast_call_delete(ast_call*); +static bool ast_call_codegen(ast_call*, ast_function*, bool lvalue, ir_value**); +static bool ast_block_codegen(ast_block*, ast_function*, bool lvalue, ir_value**); +static void ast_unary_delete(ast_unary*); +static bool ast_unary_codegen(ast_unary*, ast_function*, bool lvalue, ir_value**); +static void ast_entfield_delete(ast_entfield*); +static bool ast_entfield_codegen(ast_entfield*, ast_function*, bool lvalue, ir_value**); +static void ast_return_delete(ast_return*); +static bool ast_return_codegen(ast_return*, ast_function*, bool lvalue, ir_value**); +static void ast_binstore_delete(ast_binstore*); +static bool ast_binstore_codegen(ast_binstore*, ast_function*, bool lvalue, ir_value**); +static void ast_binary_delete(ast_binary*); +static bool ast_binary_codegen(ast_binary*, ast_function*, bool lvalue, ir_value**); + +/* It must not be possible to get here. */ +static GMQCC_NORETURN void _ast_node_destroy(ast_node *self) +{ + (void)self; + con_err("ast node missing destroy()\n"); + exit(EXIT_FAILURE); +} + +/* Initialize main ast node aprts */ +static void ast_node_init(ast_node *self, lex_ctx ctx, int nodetype) +{ + self->context = ctx; + self->destroy = &_ast_node_destroy; + self->keep = false; + self->nodetype = nodetype; + self->side_effects = false; +} + +/* weight and side effects */ +static void _ast_propagate_effects(ast_node *self, ast_node *other) +{ + if (ast_side_effects(other)) + ast_side_effects(self) = true; +} +#define ast_propagate_effects(s,o) _ast_propagate_effects(((ast_node*)(s)), ((ast_node*)(o))) + +/* General expression initialization */ +static void ast_expression_init(ast_expression *self, + ast_expression_codegen *codegen) +{ + self->codegen = codegen; + self->vtype = TYPE_VOID; + self->next = NULL; + self->outl = NULL; + self->outr = NULL; + self->params = NULL; + self->count = 0; + self->flags = 0; + self->varparam = NULL; +} + +static void ast_expression_delete(ast_expression *self) +{ + size_t i; + if (self->next) + ast_delete(self->next); + for (i = 0; i < vec_size(self->params); ++i) { + ast_delete(self->params[i]); + } + vec_free(self->params); + if (self->varparam) + ast_delete(self->varparam); +} + +static void ast_expression_delete_full(ast_expression *self) +{ + ast_expression_delete(self); + mem_d(self); +} + +ast_value* ast_value_copy(const ast_value *self) +{ + size_t i; + const ast_expression *fromex; + ast_expression *selfex; + ast_value *cp = ast_value_new(self->expression.node.context, self->name, self->expression.vtype); + if (self->expression.next) { + cp->expression.next = ast_type_copy(self->expression.node.context, self->expression.next); + } + fromex = &self->expression; + selfex = &cp->expression; + selfex->count = fromex->count; + selfex->flags = fromex->flags; + for (i = 0; i < vec_size(fromex->params); ++i) { + ast_value *v = ast_value_copy(fromex->params[i]); + vec_push(selfex->params, v); + } + return cp; +} + +void ast_type_adopt_impl(ast_expression *self, const ast_expression *other) +{ + size_t i; + const ast_expression *fromex; + ast_expression *selfex; + self->vtype = other->vtype; + if (other->next) { + self->next = (ast_expression*)ast_type_copy(ast_ctx(self), other->next); + } + fromex = other; + selfex = self; + selfex->count = fromex->count; + selfex->flags = fromex->flags; + for (i = 0; i < vec_size(fromex->params); ++i) { + ast_value *v = ast_value_copy(fromex->params[i]); + vec_push(selfex->params, v); + } +} + +static ast_expression* ast_shallow_type(lex_ctx ctx, int vtype) +{ + ast_instantiate(ast_expression, ctx, ast_expression_delete_full); + ast_expression_init(self, NULL); + self->codegen = NULL; + self->next = NULL; + self->vtype = vtype; + return self; +} + +ast_expression* ast_type_copy(lex_ctx ctx, const ast_expression *ex) +{ + size_t i; + const ast_expression *fromex; + ast_expression *selfex; + + if (!ex) + return NULL; + else + { + ast_instantiate(ast_expression, ctx, ast_expression_delete_full); + ast_expression_init(self, NULL); + + fromex = ex; + selfex = self; + + /* This may never be codegen()d */ + selfex->codegen = NULL; + + selfex->vtype = fromex->vtype; + if (fromex->next) + selfex->next = ast_type_copy(ctx, fromex->next); + else + selfex->next = NULL; + + selfex->count = fromex->count; + selfex->flags = fromex->flags; + for (i = 0; i < vec_size(fromex->params); ++i) { + ast_value *v = ast_value_copy(fromex->params[i]); + vec_push(selfex->params, v); + } + + return self; + } +} + +bool ast_compare_type(ast_expression *a, ast_expression *b) +{ + if (a->vtype == TYPE_NIL || + b->vtype == TYPE_NIL) + return true; + if (a->vtype != b->vtype) + return false; + if (!a->next != !b->next) + return false; + if (vec_size(a->params) != vec_size(b->params)) + return false; + if ((a->flags & AST_FLAG_TYPE_MASK) != + (b->flags & AST_FLAG_TYPE_MASK) ) + { + return false; + } + if (vec_size(a->params)) { + size_t i; + for (i = 0; i < vec_size(a->params); ++i) { + if (!ast_compare_type((ast_expression*)a->params[i], + (ast_expression*)b->params[i])) + return false; + } + } + if (a->next) + return ast_compare_type(a->next, b->next); + return true; +} + +static size_t ast_type_to_string_impl(ast_expression *e, char *buf, size_t bufsize, size_t pos) +{ + const char *typestr; + size_t typelen; + size_t i; + + if (!e) { + if (pos + 6 >= bufsize) + goto full; + util_strncpy(buf + pos, "(null)", 6); + return pos + 6; + } + + if (pos + 1 >= bufsize) + goto full; + + switch (e->vtype) { + case TYPE_VARIANT: + util_strncpy(buf + pos, "(variant)", 9); + return pos + 9; + + case TYPE_FIELD: + buf[pos++] = '.'; + return ast_type_to_string_impl(e->next, buf, bufsize, pos); + + case TYPE_POINTER: + if (pos + 3 >= bufsize) + goto full; + buf[pos++] = '*'; + buf[pos++] = '('; + pos = ast_type_to_string_impl(e->next, buf, bufsize, pos); + if (pos + 1 >= bufsize) + goto full; + buf[pos++] = ')'; + return pos; + + case TYPE_FUNCTION: + pos = ast_type_to_string_impl(e->next, buf, bufsize, pos); + if (pos + 2 >= bufsize) + goto full; + if (!vec_size(e->params)) { + buf[pos++] = '('; + buf[pos++] = ')'; + return pos; + } + buf[pos++] = '('; + pos = ast_type_to_string_impl((ast_expression*)(e->params[0]), buf, bufsize, pos); + for (i = 1; i < vec_size(e->params); ++i) { + if (pos + 2 >= bufsize) + goto full; + buf[pos++] = ','; + buf[pos++] = ' '; + pos = ast_type_to_string_impl((ast_expression*)(e->params[i]), buf, bufsize, pos); + } + if (pos + 1 >= bufsize) + goto full; + buf[pos++] = ')'; + return pos; + + case TYPE_ARRAY: + pos = ast_type_to_string_impl(e->next, buf, bufsize, pos); + if (pos + 1 >= bufsize) + goto full; + buf[pos++] = '['; + pos += util_snprintf(buf + pos, bufsize - pos - 1, "%i", (int)e->count); + if (pos + 1 >= bufsize) + goto full; + buf[pos++] = ']'; + return pos; + + default: + typestr = type_name[e->vtype]; + typelen = strlen(typestr); + if (pos + typelen >= bufsize) + goto full; + util_strncpy(buf + pos, typestr, typelen); + return pos + typelen; + } + +full: + buf[bufsize-3] = '.'; + buf[bufsize-2] = '.'; + buf[bufsize-1] = '.'; + return bufsize; +} + +void ast_type_to_string(ast_expression *e, char *buf, size_t bufsize) +{ + size_t pos = ast_type_to_string_impl(e, buf, bufsize-1, 0); + buf[pos] = 0; +} + +static bool ast_value_codegen(ast_value *self, ast_function *func, bool lvalue, ir_value **out); +ast_value* ast_value_new(lex_ctx ctx, const char *name, int t) +{ + ast_instantiate(ast_value, ctx, ast_value_delete); + ast_expression_init((ast_expression*)self, + (ast_expression_codegen*)&ast_value_codegen); + self->expression.node.keep = true; /* keep */ + + self->name = name ? util_strdup(name) : NULL; + self->expression.vtype = t; + self->expression.next = NULL; + self->isfield = false; + self->cvq = CV_NONE; + self->hasvalue = false; + self->isimm = false; + self->uses = 0; + memset(&self->constval, 0, sizeof(self->constval)); + self->initlist = NULL; + + self->ir_v = NULL; + self->ir_values = NULL; + self->ir_value_count = 0; + + self->setter = NULL; + self->getter = NULL; + self->desc = NULL; + + self->argcounter = NULL; + + return self; +} + +void ast_value_delete(ast_value* self) +{ + if (self->name) + mem_d((void*)self->name); + if (self->argcounter) + mem_d((void*)self->argcounter); + if (self->hasvalue) { + switch (self->expression.vtype) + { + case TYPE_STRING: + mem_d((void*)self->constval.vstring); + break; + case TYPE_FUNCTION: + /* unlink us from the function node */ + self->constval.vfunc->vtype = NULL; + break; + /* NOTE: delete function? currently collected in + * the parser structure + */ + default: + break; + } + } + if (self->ir_values) + mem_d(self->ir_values); + + if (self->desc) + mem_d(self->desc); + + if (self->initlist) { + if (self->expression.next->vtype == TYPE_STRING) { + /* strings are allocated, free them */ + size_t i, len = vec_size(self->initlist); + /* in theory, len should be expression.count + * but let's not take any chances */ + for (i = 0; i < len; ++i) { + if (self->initlist[i].vstring) + mem_d(self->initlist[i].vstring); + } + } + vec_free(self->initlist); + } + + ast_expression_delete((ast_expression*)self); + mem_d(self); +} + +void ast_value_params_add(ast_value *self, ast_value *p) +{ + vec_push(self->expression.params, p); +} + +bool ast_value_set_name(ast_value *self, const char *name) +{ + if (self->name) + mem_d((void*)self->name); + self->name = util_strdup(name); + return !!self->name; +} + +ast_binary* ast_binary_new(lex_ctx ctx, int op, + ast_expression* left, ast_expression* right) +{ + ast_instantiate(ast_binary, ctx, ast_binary_delete); + ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_binary_codegen); + + self->op = op; + self->left = left; + self->right = right; + + ast_propagate_effects(self, left); + ast_propagate_effects(self, right); + + if (op >= INSTR_EQ_F && op <= INSTR_GT) + self->expression.vtype = TYPE_FLOAT; + else if (op == INSTR_AND || op == INSTR_OR) { + if (OPTS_FLAG(PERL_LOGIC)) + ast_type_adopt(self, right); + else + self->expression.vtype = TYPE_FLOAT; + } + else if (op == INSTR_BITAND || op == INSTR_BITOR) + self->expression.vtype = TYPE_FLOAT; + else if (op == INSTR_MUL_VF || op == INSTR_MUL_FV) + self->expression.vtype = TYPE_VECTOR; + else if (op == INSTR_MUL_V) + self->expression.vtype = TYPE_FLOAT; + else + self->expression.vtype = left->vtype; + + /* references all */ + self->refs = AST_REF_ALL; + + return self; +} + +void ast_binary_delete(ast_binary *self) +{ + if (self->refs & AST_REF_LEFT) ast_unref(self->left); + if (self->refs & AST_REF_RIGHT) ast_unref(self->right); + + ast_expression_delete((ast_expression*)self); + mem_d(self); +} + +ast_binstore* ast_binstore_new(lex_ctx ctx, int storop, int op, + ast_expression* left, ast_expression* right) +{ + ast_instantiate(ast_binstore, ctx, ast_binstore_delete); + ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_binstore_codegen); + + ast_side_effects(self) = true; + + self->opstore = storop; + self->opbin = op; + self->dest = left; + self->source = right; + + self->keep_dest = false; + + ast_type_adopt(self, left); + return self; +} + +void ast_binstore_delete(ast_binstore *self) +{ + if (!self->keep_dest) + ast_unref(self->dest); + ast_unref(self->source); + ast_expression_delete((ast_expression*)self); + mem_d(self); +} + +ast_unary* ast_unary_new(lex_ctx ctx, int op, + ast_expression *expr) +{ + ast_instantiate(ast_unary, ctx, ast_unary_delete); + ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_unary_codegen); + + self->op = op; + self->operand = expr; + + ast_propagate_effects(self, expr); + + if (op >= INSTR_NOT_F && op <= INSTR_NOT_FNC) { + self->expression.vtype = TYPE_FLOAT; + } else + compile_error(ctx, "cannot determine type of unary operation %s", asm_instr[op].m); + + return self; +} + +void ast_unary_delete(ast_unary *self) +{ + if (self->operand) ast_unref(self->operand); + ast_expression_delete((ast_expression*)self); + mem_d(self); +} + +ast_return* ast_return_new(lex_ctx ctx, ast_expression *expr) +{ + ast_instantiate(ast_return, ctx, ast_return_delete); + ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_return_codegen); + + self->operand = expr; + + if (expr) + ast_propagate_effects(self, expr); + + return self; +} + +void ast_return_delete(ast_return *self) +{ + if (self->operand) + ast_unref(self->operand); + ast_expression_delete((ast_expression*)self); + mem_d(self); +} + +ast_entfield* ast_entfield_new(lex_ctx ctx, ast_expression *entity, ast_expression *field) +{ + if (field->vtype != TYPE_FIELD) { + compile_error(ctx, "ast_entfield_new with expression not of type field"); + return NULL; + } + return ast_entfield_new_force(ctx, entity, field, field->next); +} + +ast_entfield* ast_entfield_new_force(lex_ctx ctx, ast_expression *entity, ast_expression *field, const ast_expression *outtype) +{ + ast_instantiate(ast_entfield, ctx, ast_entfield_delete); + + if (!outtype) { + mem_d(self); + /* Error: field has no type... */ + return NULL; + } + + ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_entfield_codegen); + + self->entity = entity; + self->field = field; + ast_propagate_effects(self, entity); + ast_propagate_effects(self, field); + + ast_type_adopt(self, outtype); + return self; +} + +void ast_entfield_delete(ast_entfield *self) +{ + ast_unref(self->entity); + ast_unref(self->field); + ast_expression_delete((ast_expression*)self); + mem_d(self); +} + +ast_member* ast_member_new(lex_ctx ctx, ast_expression *owner, unsigned int field, const char *name) +{ + ast_instantiate(ast_member, ctx, ast_member_delete); + if (field >= 3) { + mem_d(self); + return NULL; + } + + if (owner->vtype != TYPE_VECTOR && + owner->vtype != TYPE_FIELD) { + compile_error(ctx, "member-access on an invalid owner of type %s", type_name[owner->vtype]); + mem_d(self); + return NULL; + } + + ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_member_codegen); + self->expression.node.keep = true; /* keep */ + + if (owner->vtype == TYPE_VECTOR) { + self->expression.vtype = TYPE_FLOAT; + self->expression.next = NULL; + } else { + self->expression.vtype = TYPE_FIELD; + self->expression.next = ast_shallow_type(ctx, TYPE_FLOAT); + } + + self->rvalue = false; + self->owner = owner; + ast_propagate_effects(self, owner); + + self->field = field; + if (name) + self->name = util_strdup(name); + else + self->name = NULL; + + return self; +} + +void ast_member_delete(ast_member *self) +{ + /* The owner is always an ast_value, which has .keep=true, + * also: ast_members are usually deleted after the owner, thus + * this will cause invalid access + ast_unref(self->owner); + * once we allow (expression).x to access a vector-member, we need + * to change this: preferably by creating an alternate ast node for this + * purpose that is not garbage-collected. + */ + ast_expression_delete((ast_expression*)self); + mem_d(self->name); + mem_d(self); +} + +bool ast_member_set_name(ast_member *self, const char *name) +{ + if (self->name) + mem_d((void*)self->name); + self->name = util_strdup(name); + return !!self->name; +} + +ast_array_index* ast_array_index_new(lex_ctx ctx, ast_expression *array, ast_expression *index) +{ + ast_expression *outtype; + ast_instantiate(ast_array_index, ctx, ast_array_index_delete); + + outtype = array->next; + if (!outtype) { + mem_d(self); + /* Error: field has no type... */ + return NULL; + } + + ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_array_index_codegen); + + self->array = array; + self->index = index; + ast_propagate_effects(self, array); + ast_propagate_effects(self, index); + + ast_type_adopt(self, outtype); + if (array->vtype == TYPE_FIELD && outtype->vtype == TYPE_ARRAY) { + if (self->expression.vtype != TYPE_ARRAY) { + compile_error(ast_ctx(self), "array_index node on type"); + ast_array_index_delete(self); + return NULL; + } + self->array = outtype; + self->expression.vtype = TYPE_FIELD; + } + + return self; +} + +void ast_array_index_delete(ast_array_index *self) +{ + if (self->array) + ast_unref(self->array); + if (self->index) + ast_unref(self->index); + ast_expression_delete((ast_expression*)self); + mem_d(self); +} + +ast_ifthen* ast_ifthen_new(lex_ctx ctx, ast_expression *cond, ast_expression *ontrue, ast_expression *onfalse) +{ + ast_instantiate(ast_ifthen, ctx, ast_ifthen_delete); + if (!ontrue && !onfalse) { + /* because it is invalid */ + mem_d(self); + return NULL; + } + ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_ifthen_codegen); + + self->cond = cond; + self->on_true = ontrue; + self->on_false = onfalse; + ast_propagate_effects(self, cond); + if (ontrue) + ast_propagate_effects(self, ontrue); + if (onfalse) + ast_propagate_effects(self, onfalse); + + return self; +} + +void ast_ifthen_delete(ast_ifthen *self) +{ + ast_unref(self->cond); + if (self->on_true) + ast_unref(self->on_true); + if (self->on_false) + ast_unref(self->on_false); + ast_expression_delete((ast_expression*)self); + mem_d(self); +} + +ast_ternary* ast_ternary_new(lex_ctx ctx, ast_expression *cond, ast_expression *ontrue, ast_expression *onfalse) +{ + ast_expression *exprtype = ontrue; + ast_instantiate(ast_ternary, ctx, ast_ternary_delete); + /* This time NEITHER must be NULL */ + if (!ontrue || !onfalse) { + mem_d(self); + return NULL; + } + ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_ternary_codegen); + + self->cond = cond; + self->on_true = ontrue; + self->on_false = onfalse; + ast_propagate_effects(self, cond); + ast_propagate_effects(self, ontrue); + ast_propagate_effects(self, onfalse); + + if (ontrue->vtype == TYPE_NIL) + exprtype = onfalse; + ast_type_adopt(self, exprtype); + + return self; +} + +void ast_ternary_delete(ast_ternary *self) +{ + /* the if()s are only there because computed-gotos can set them + * to NULL + */ + if (self->cond) ast_unref(self->cond); + if (self->on_true) ast_unref(self->on_true); + if (self->on_false) ast_unref(self->on_false); + ast_expression_delete((ast_expression*)self); + mem_d(self); +} + +ast_loop* ast_loop_new(lex_ctx ctx, + ast_expression *initexpr, + ast_expression *precond, bool pre_not, + ast_expression *postcond, bool post_not, + ast_expression *increment, + ast_expression *body) +{ + ast_instantiate(ast_loop, ctx, ast_loop_delete); + ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_loop_codegen); + + self->initexpr = initexpr; + self->precond = precond; + self->postcond = postcond; + self->increment = increment; + self->body = body; + + self->pre_not = pre_not; + self->post_not = post_not; + + if (initexpr) + ast_propagate_effects(self, initexpr); + if (precond) + ast_propagate_effects(self, precond); + if (postcond) + ast_propagate_effects(self, postcond); + if (increment) + ast_propagate_effects(self, increment); + if (body) + ast_propagate_effects(self, body); + + return self; +} + +void ast_loop_delete(ast_loop *self) +{ + if (self->initexpr) + ast_unref(self->initexpr); + if (self->precond) + ast_unref(self->precond); + if (self->postcond) + ast_unref(self->postcond); + if (self->increment) + ast_unref(self->increment); + if (self->body) + ast_unref(self->body); + ast_expression_delete((ast_expression*)self); + mem_d(self); +} + +ast_breakcont* ast_breakcont_new(lex_ctx ctx, bool iscont, unsigned int levels) +{ + ast_instantiate(ast_breakcont, ctx, ast_breakcont_delete); + ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_breakcont_codegen); + + self->is_continue = iscont; + self->levels = levels; + + return self; +} + +void ast_breakcont_delete(ast_breakcont *self) +{ + ast_expression_delete((ast_expression*)self); + mem_d(self); +} + +ast_switch* ast_switch_new(lex_ctx ctx, ast_expression *op) +{ + ast_instantiate(ast_switch, ctx, ast_switch_delete); + ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_switch_codegen); + + self->operand = op; + self->cases = NULL; + + ast_propagate_effects(self, op); + + return self; +} + +void ast_switch_delete(ast_switch *self) +{ + size_t i; + ast_unref(self->operand); + + for (i = 0; i < vec_size(self->cases); ++i) { + if (self->cases[i].value) + ast_unref(self->cases[i].value); + ast_unref(self->cases[i].code); + } + vec_free(self->cases); + + ast_expression_delete((ast_expression*)self); + mem_d(self); +} + +ast_label* ast_label_new(lex_ctx ctx, const char *name, bool undefined) +{ + ast_instantiate(ast_label, ctx, ast_label_delete); + ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_label_codegen); + + self->expression.vtype = TYPE_NOEXPR; + + self->name = util_strdup(name); + self->irblock = NULL; + self->gotos = NULL; + self->undefined = undefined; + + return self; +} + +void ast_label_delete(ast_label *self) +{ + mem_d((void*)self->name); + vec_free(self->gotos); + ast_expression_delete((ast_expression*)self); + mem_d(self); +} + +static void ast_label_register_goto(ast_label *self, ast_goto *g) +{ + vec_push(self->gotos, g); +} + +ast_goto* ast_goto_new(lex_ctx ctx, const char *name) +{ + ast_instantiate(ast_goto, ctx, ast_goto_delete); + ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_goto_codegen); + + self->name = util_strdup(name); + self->target = NULL; + self->irblock_from = NULL; + + return self; +} + +void ast_goto_delete(ast_goto *self) +{ + mem_d((void*)self->name); + ast_expression_delete((ast_expression*)self); + mem_d(self); +} + +void ast_goto_set_label(ast_goto *self, ast_label *label) +{ + self->target = label; +} + +ast_call* ast_call_new(lex_ctx ctx, + ast_expression *funcexpr) +{ + ast_instantiate(ast_call, ctx, ast_call_delete); + if (!funcexpr->next) { + compile_error(ctx, "not a function"); + mem_d(self); + return NULL; + } + ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_call_codegen); + + ast_side_effects(self) = true; + + self->params = NULL; + self->func = funcexpr; + self->va_count = NULL; + + ast_type_adopt(self, funcexpr->next); + + return self; +} + +void ast_call_delete(ast_call *self) +{ + size_t i; + for (i = 0; i < vec_size(self->params); ++i) + ast_unref(self->params[i]); + vec_free(self->params); + + if (self->func) + ast_unref(self->func); + + if (self->va_count) + ast_unref(self->va_count); + + ast_expression_delete((ast_expression*)self); + mem_d(self); +} + +bool ast_call_check_types(ast_call *self) +{ + char texp[1024]; + char tgot[1024]; + size_t i; + bool retval = true; + const ast_expression *func = self->func; + size_t count = vec_size(self->params); + if (count > vec_size(func->params)) + count = vec_size(func->params); + + for (i = 0; i < count; ++i) { + if (!ast_compare_type(self->params[i], (ast_expression*)(func->params[i]))) + { + ast_type_to_string(self->params[i], tgot, sizeof(tgot)); + ast_type_to_string((ast_expression*)func->params[i], texp, sizeof(texp)); + compile_error(ast_ctx(self), "invalid type for parameter %u in function call: expected %s, got %s", + (unsigned int)(i+1), texp, tgot); + /* we don't immediately return */ + retval = false; + } + } + count = vec_size(self->params); + if (count > vec_size(func->params) && func->varparam) { + for (; i < count; ++i) { + if (!ast_compare_type(self->params[i], func->varparam)) + { + ast_type_to_string(self->params[i], tgot, sizeof(tgot)); + ast_type_to_string(func->varparam, texp, sizeof(texp)); + compile_error(ast_ctx(self), "invalid type for parameter %u in function call: expected %s, got %s", + (unsigned int)(i+1), texp, tgot); + /* we don't immediately return */ + retval = false; + } + } + } + return retval; +} + +ast_store* ast_store_new(lex_ctx ctx, int op, + ast_expression *dest, ast_expression *source) +{ + ast_instantiate(ast_store, ctx, ast_store_delete); + ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_store_codegen); + + ast_side_effects(self) = true; + + self->op = op; + self->dest = dest; + self->source = source; + + ast_type_adopt(self, dest); + + return self; +} + +void ast_store_delete(ast_store *self) +{ + ast_unref(self->dest); + ast_unref(self->source); + ast_expression_delete((ast_expression*)self); + mem_d(self); +} + +ast_block* ast_block_new(lex_ctx ctx) +{ + ast_instantiate(ast_block, ctx, ast_block_delete); + ast_expression_init((ast_expression*)self, + (ast_expression_codegen*)&ast_block_codegen); + + self->locals = NULL; + self->exprs = NULL; + self->collect = NULL; + + return self; +} + +bool ast_block_add_expr(ast_block *self, ast_expression *e) +{ + ast_propagate_effects(self, e); + vec_push(self->exprs, e); + if (self->expression.next) { + ast_delete(self->expression.next); + self->expression.next = NULL; + } + ast_type_adopt(self, e); + return true; +} + +void ast_block_collect(ast_block *self, ast_expression *expr) +{ + vec_push(self->collect, expr); + expr->node.keep = true; +} + +void ast_block_delete(ast_block *self) +{ + size_t i; + for (i = 0; i < vec_size(self->exprs); ++i) + ast_unref(self->exprs[i]); + vec_free(self->exprs); + for (i = 0; i < vec_size(self->locals); ++i) + ast_delete(self->locals[i]); + vec_free(self->locals); + for (i = 0; i < vec_size(self->collect); ++i) + ast_delete(self->collect[i]); + vec_free(self->collect); + ast_expression_delete((ast_expression*)self); + mem_d(self); +} + +void ast_block_set_type(ast_block *self, ast_expression *from) +{ + if (self->expression.next) + ast_delete(self->expression.next); + ast_type_adopt(self, from); +} + +ast_function* ast_function_new(lex_ctx ctx, const char *name, ast_value *vtype) +{ + ast_instantiate(ast_function, ctx, ast_function_delete); + + if (!vtype || + vtype->hasvalue || + vtype->expression.vtype != TYPE_FUNCTION) + { + compile_error(ast_ctx(self), "internal error: ast_function_new condition %i %i type=%i (probably 2 bodies?)", + (int)!vtype, + (int)vtype->hasvalue, + vtype->expression.vtype); + mem_d(self); + return NULL; + } + + self->vtype = vtype; + self->name = name ? util_strdup(name) : NULL; + self->blocks = NULL; + + self->labelcount = 0; + self->builtin = 0; + + self->ir_func = NULL; + self->curblock = NULL; + + self->breakblocks = NULL; + self->continueblocks = NULL; + + vtype->hasvalue = true; + vtype->constval.vfunc = self; + + self->varargs = NULL; + self->argc = NULL; + self->fixedparams = NULL; + self->return_value = NULL; + + return self; +} + +void ast_function_delete(ast_function *self) +{ + size_t i; + if (self->name) + mem_d((void*)self->name); + if (self->vtype) { + /* ast_value_delete(self->vtype); */ + self->vtype->hasvalue = false; + self->vtype->constval.vfunc = NULL; + /* We use unref - if it was stored in a global table it is supposed + * to be deleted from *there* + */ + ast_unref(self->vtype); + } + for (i = 0; i < vec_size(self->blocks); ++i) + ast_delete(self->blocks[i]); + vec_free(self->blocks); + vec_free(self->breakblocks); + vec_free(self->continueblocks); + if (self->varargs) + ast_delete(self->varargs); + if (self->argc) + ast_delete(self->argc); + if (self->fixedparams) + ast_unref(self->fixedparams); + if (self->return_value) + ast_unref(self->return_value); + mem_d(self); +} + +static const char* ast_function_label(ast_function *self, const char *prefix) +{ + size_t id; + size_t len; + char *from; + + if (!OPTS_OPTION_BOOL(OPTION_DUMP) && + !OPTS_OPTION_BOOL(OPTION_DUMPFIN) && + !OPTS_OPTION_BOOL(OPTION_DEBUG)) + { + return NULL; + } + + id = (self->labelcount++); + len = strlen(prefix); + + from = self->labelbuf + sizeof(self->labelbuf)-1; + *from-- = 0; + do { + *from-- = (id%10) + '0'; + id /= 10; + } while (id); + ++from; + memcpy(from - len, prefix, len); + return from - len; +} + +/*********************************************************************/ +/* AST codegen part + * by convention you must never pass NULL to the 'ir_value **out' + * parameter. If you really don't care about the output, pass a dummy. + * But I can't imagine a pituation where the output is truly unnecessary. + */ + +static void _ast_codegen_output_type(ast_expression *self, ir_value *out) +{ + if (out->vtype == TYPE_FIELD) + out->fieldtype = self->next->vtype; + if (out->vtype == TYPE_FUNCTION) + out->outtype = self->next->vtype; +} + +#define codegen_output_type(a,o) (_ast_codegen_output_type(&((a)->expression),(o))) + +bool ast_value_codegen(ast_value *self, ast_function *func, bool lvalue, ir_value **out) +{ + (void)func; + (void)lvalue; + if (self->expression.vtype == TYPE_NIL) { + *out = func->ir_func->owner->nil; + return true; + } + /* NOTE: This is the codegen for a variable used in an expression. + * It is not the codegen to generate the value. For this purpose, + * ast_local_codegen and ast_global_codegen are to be used before this + * is executed. ast_function_codegen should take care of its locals, + * and the ast-user should take care of ast_global_codegen to be used + * on all the globals. + */ + if (!self->ir_v) { + char tname[1024]; /* typename is reserved in C++ */ + ast_type_to_string((ast_expression*)self, tname, sizeof(tname)); + compile_error(ast_ctx(self), "ast_value used before generated %s %s", tname, self->name); + return false; + } + *out = self->ir_v; + return true; +} + +bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield) +{ + ir_value *v = NULL; + + if (self->expression.vtype == TYPE_NIL) { + compile_error(ast_ctx(self), "internal error: trying to generate a variable of TYPE_NIL"); + return false; + } + + if (self->hasvalue && self->expression.vtype == TYPE_FUNCTION) + { + ir_function *func = ir_builder_create_function(ir, self->name, self->expression.next->vtype); + if (!func) + return false; + func->context = ast_ctx(self); + func->value->context = ast_ctx(self); + + self->constval.vfunc->ir_func = func; + self->ir_v = func->value; + if (self->expression.flags & AST_FLAG_INCLUDE_DEF) + self->ir_v->flags |= IR_FLAG_INCLUDE_DEF; + /* The function is filled later on ast_function_codegen... */ + return true; + } + + if (isfield && self->expression.vtype == TYPE_FIELD) { + ast_expression *fieldtype = self->expression.next; + + if (self->hasvalue) { + compile_error(ast_ctx(self), "TODO: constant field pointers with value"); + goto error; + } + + if (fieldtype->vtype == TYPE_ARRAY) { + size_t ai; + char *name; + size_t namelen; + + ast_expression *elemtype; + int vtype; + ast_value *array = (ast_value*)fieldtype; + + if (!ast_istype(fieldtype, ast_value)) { + compile_error(ast_ctx(self), "internal error: ast_value required"); + return false; + } + + /* we are lame now - considering the way QC works we won't tolerate arrays > 1024 elements */ + if (!array->expression.count || array->expression.count > OPTS_OPTION_U32(OPTION_MAX_ARRAY_SIZE)) + compile_error(ast_ctx(self), "Invalid array of size %lu", (unsigned long)array->expression.count); + + elemtype = array->expression.next; + vtype = elemtype->vtype; + + v = ir_builder_create_field(ir, self->name, vtype); + if (!v) { + compile_error(ast_ctx(self), "ir_builder_create_global failed on `%s`", self->name); + return false; + } + v->context = ast_ctx(self); + v->unique_life = true; + v->locked = true; + array->ir_v = self->ir_v = v; + if (self->expression.flags & AST_FLAG_INCLUDE_DEF) + self->ir_v->flags |= IR_FLAG_INCLUDE_DEF; + + namelen = strlen(self->name); + name = (char*)mem_a(namelen + 16); + util_strncpy(name, self->name, namelen); + + array->ir_values = (ir_value**)mem_a(sizeof(array->ir_values[0]) * array->expression.count); + array->ir_values[0] = v; + for (ai = 1; ai < array->expression.count; ++ai) { + util_snprintf(name + namelen, 16, "[%u]", (unsigned int)ai); + array->ir_values[ai] = ir_builder_create_field(ir, name, vtype); + if (!array->ir_values[ai]) { + mem_d(name); + compile_error(ast_ctx(self), "ir_builder_create_global failed on `%s`", name); + return false; + } + array->ir_values[ai]->context = ast_ctx(self); + array->ir_values[ai]->unique_life = true; + array->ir_values[ai]->locked = true; + if (self->expression.flags & AST_FLAG_INCLUDE_DEF) + self->ir_values[ai]->flags |= IR_FLAG_INCLUDE_DEF; + } + mem_d(name); + } + else + { + v = ir_builder_create_field(ir, self->name, self->expression.next->vtype); + if (!v) + return false; + v->context = ast_ctx(self); + self->ir_v = v; + if (self->expression.flags & AST_FLAG_INCLUDE_DEF) + self->ir_v->flags |= IR_FLAG_INCLUDE_DEF; + } + return true; + } + + if (self->expression.vtype == TYPE_ARRAY) { + size_t ai; + char *name; + size_t namelen; + + ast_expression *elemtype = self->expression.next; + int vtype = elemtype->vtype; + + /* same as with field arrays */ + if (!self->expression.count || self->expression.count > OPTS_OPTION_U32(OPTION_MAX_ARRAY_SIZE)) + compile_error(ast_ctx(self), "Invalid array of size %lu", (unsigned long)self->expression.count); + + v = ir_builder_create_global(ir, self->name, vtype); + if (!v) { + compile_error(ast_ctx(self), "ir_builder_create_global failed `%s`", self->name); + return false; + } + v->context = ast_ctx(self); + v->unique_life = true; + v->locked = true; + if (self->expression.flags & AST_FLAG_INCLUDE_DEF) + v->flags |= IR_FLAG_INCLUDE_DEF; + + namelen = strlen(self->name); + name = (char*)mem_a(namelen + 16); + util_strncpy(name, self->name, namelen); + + self->ir_values = (ir_value**)mem_a(sizeof(self->ir_values[0]) * self->expression.count); + self->ir_values[0] = v; + for (ai = 1; ai < self->expression.count; ++ai) { + util_snprintf(name + namelen, 16, "[%u]", (unsigned int)ai); + self->ir_values[ai] = ir_builder_create_global(ir, name, vtype); + if (!self->ir_values[ai]) { + mem_d(name); + compile_error(ast_ctx(self), "ir_builder_create_global failed `%s`", name); + return false; + } + self->ir_values[ai]->context = ast_ctx(self); + self->ir_values[ai]->unique_life = true; + self->ir_values[ai]->locked = true; + if (self->expression.flags & AST_FLAG_INCLUDE_DEF) + self->ir_values[ai]->flags |= IR_FLAG_INCLUDE_DEF; + } + mem_d(name); + } + else + { + /* Arrays don't do this since there's no "array" value which spans across the + * whole thing. + */ + v = ir_builder_create_global(ir, self->name, self->expression.vtype); + if (!v) { + compile_error(ast_ctx(self), "ir_builder_create_global failed on `%s`", self->name); + return false; + } + codegen_output_type(self, v); + v->context = ast_ctx(self); + } + + if (self->hasvalue) { + switch (self->expression.vtype) + { + case TYPE_FLOAT: + if (!ir_value_set_float(v, self->constval.vfloat)) + goto error; + break; + case TYPE_VECTOR: + if (!ir_value_set_vector(v, self->constval.vvec)) + goto error; + break; + case TYPE_STRING: + if (!ir_value_set_string(v, self->constval.vstring)) + goto error; + break; + case TYPE_ARRAY: + compile_error(ast_ctx(self), "TODO: global constant array"); + break; + case TYPE_FUNCTION: + compile_error(ast_ctx(self), "global of type function not properly generated"); + goto error; + /* Cannot generate an IR value for a function, + * need a pointer pointing to a function rather. + */ + case TYPE_FIELD: + if (!self->constval.vfield) { + compile_error(ast_ctx(self), "field constant without vfield set"); + goto error; + } + if (!self->constval.vfield->ir_v) { + compile_error(ast_ctx(self), "field constant generated before its field"); + goto error; + } + if (!ir_value_set_field(v, self->constval.vfield->ir_v)) + goto error; + break; + default: + compile_error(ast_ctx(self), "TODO: global constant type %i", self->expression.vtype); + break; + } + } + + /* link us to the ir_value */ + v->cvq = self->cvq; + self->ir_v = v; + if (self->expression.flags & AST_FLAG_INCLUDE_DEF) + self->ir_v->flags |= IR_FLAG_INCLUDE_DEF; + return true; + +error: /* clean up */ + ir_value_delete(v); + return false; +} + +static bool ast_local_codegen(ast_value *self, ir_function *func, bool param) +{ + ir_value *v = NULL; + + if (self->expression.vtype == TYPE_NIL) { + compile_error(ast_ctx(self), "internal error: trying to generate a variable of TYPE_NIL"); + return false; + } + + if (self->hasvalue && self->expression.vtype == TYPE_FUNCTION) + { + /* Do we allow local functions? I think not... + * this is NOT a function pointer atm. + */ + return false; + } + + if (self->expression.vtype == TYPE_ARRAY) { + size_t ai; + char *name; + size_t namelen; + + ast_expression *elemtype = self->expression.next; + int vtype = elemtype->vtype; + + func->flags |= IR_FLAG_HAS_ARRAYS; + + if (param && !(self->expression.flags & AST_FLAG_IS_VARARG)) { + compile_error(ast_ctx(self), "array-parameters are not supported"); + return false; + } + + /* we are lame now - considering the way QC works we won't tolerate arrays > 1024 elements */ + if (!self->expression.count || self->expression.count > OPTS_OPTION_U32(OPTION_MAX_ARRAY_SIZE)) { + compile_error(ast_ctx(self), "Invalid array of size %lu", (unsigned long)self->expression.count); + } + + self->ir_values = (ir_value**)mem_a(sizeof(self->ir_values[0]) * self->expression.count); + if (!self->ir_values) { + compile_error(ast_ctx(self), "failed to allocate array values"); + return false; + } + + v = ir_function_create_local(func, self->name, vtype, param); + if (!v) { + compile_error(ast_ctx(self), "internal error: ir_function_create_local failed"); + return false; + } + v->context = ast_ctx(self); + v->unique_life = true; + v->locked = true; + + namelen = strlen(self->name); + name = (char*)mem_a(namelen + 16); + util_strncpy(name, self->name, namelen); + + self->ir_values[0] = v; + for (ai = 1; ai < self->expression.count; ++ai) { + util_snprintf(name + namelen, 16, "[%u]", (unsigned int)ai); + self->ir_values[ai] = ir_function_create_local(func, name, vtype, param); + if (!self->ir_values[ai]) { + compile_error(ast_ctx(self), "internal_error: ir_builder_create_global failed on `%s`", name); + return false; + } + self->ir_values[ai]->context = ast_ctx(self); + self->ir_values[ai]->unique_life = true; + self->ir_values[ai]->locked = true; + } + mem_d(name); + } + else + { + v = ir_function_create_local(func, self->name, self->expression.vtype, param); + if (!v) + return false; + codegen_output_type(self, v); + v->context = ast_ctx(self); + } + + /* A constant local... hmmm... + * I suppose the IR will have to deal with this + */ + if (self->hasvalue) { + switch (self->expression.vtype) + { + case TYPE_FLOAT: + if (!ir_value_set_float(v, self->constval.vfloat)) + goto error; + break; + case TYPE_VECTOR: + if (!ir_value_set_vector(v, self->constval.vvec)) + goto error; + break; + case TYPE_STRING: + if (!ir_value_set_string(v, self->constval.vstring)) + goto error; + break; + default: + compile_error(ast_ctx(self), "TODO: global constant type %i", self->expression.vtype); + break; + } + } + + /* link us to the ir_value */ + v->cvq = self->cvq; + self->ir_v = v; + + if (!ast_generate_accessors(self, func->owner)) + return false; + return true; + +error: /* clean up */ + ir_value_delete(v); + return false; +} + +bool ast_generate_accessors(ast_value *self, ir_builder *ir) +{ + size_t i; + bool warn = OPTS_WARN(WARN_USED_UNINITIALIZED); + if (!self->setter || !self->getter) + return true; + for (i = 0; i < self->expression.count; ++i) { + if (!self->ir_values) { + compile_error(ast_ctx(self), "internal error: no array values generated for `%s`", self->name); + return false; + } + if (!self->ir_values[i]) { + compile_error(ast_ctx(self), "internal error: not all array values have been generated for `%s`", self->name); + return false; + } + if (self->ir_values[i]->life) { + compile_error(ast_ctx(self), "internal error: function containing `%s` already generated", self->name); + return false; + } + } + + opts_set(opts.warn, WARN_USED_UNINITIALIZED, false); + if (self->setter) { + if (!ast_global_codegen (self->setter, ir, false) || + !ast_function_codegen(self->setter->constval.vfunc, ir) || + !ir_function_finalize(self->setter->constval.vfunc->ir_func)) + { + compile_error(ast_ctx(self), "internal error: failed to generate setter for `%s`", self->name); + opts_set(opts.warn, WARN_USED_UNINITIALIZED, warn); + return false; + } + } + if (self->getter) { + if (!ast_global_codegen (self->getter, ir, false) || + !ast_function_codegen(self->getter->constval.vfunc, ir) || + !ir_function_finalize(self->getter->constval.vfunc->ir_func)) + { + compile_error(ast_ctx(self), "internal error: failed to generate getter for `%s`", self->name); + opts_set(opts.warn, WARN_USED_UNINITIALIZED, warn); + return false; + } + } + for (i = 0; i < self->expression.count; ++i) { + vec_free(self->ir_values[i]->life); + } + opts_set(opts.warn, WARN_USED_UNINITIALIZED, warn); + return true; +} + +bool ast_function_codegen(ast_function *self, ir_builder *ir) +{ + ir_function *irf; + ir_value *dummy; + ast_expression *ec; + ast_expression_codegen *cgen; + size_t i; + + (void)ir; + + irf = self->ir_func; + if (!irf) { + compile_error(ast_ctx(self), "internal error: ast_function's related ast_value was not generated yet"); + return false; + } + + /* fill the parameter list */ + ec = &self->vtype->expression; + for (i = 0; i < vec_size(ec->params); ++i) + { + if (ec->params[i]->expression.vtype == TYPE_FIELD) + vec_push(irf->params, ec->params[i]->expression.next->vtype); + else + vec_push(irf->params, ec->params[i]->expression.vtype); + if (!self->builtin) { + if (!ast_local_codegen(ec->params[i], self->ir_func, true)) + return false; + } + } + + if (self->varargs) { + if (!ast_local_codegen(self->varargs, self->ir_func, true)) + return false; + irf->max_varargs = self->varargs->expression.count; + } + + if (self->builtin) { + irf->builtin = self->builtin; + return true; + } + + /* have a local return value variable? */ + if (self->return_value) { + if (!ast_local_codegen(self->return_value, self->ir_func, false)) + return false; + } + + if (!vec_size(self->blocks)) { + compile_error(ast_ctx(self), "function `%s` has no body", self->name); + return false; + } + + irf->first = self->curblock = ir_function_create_block(ast_ctx(self), irf, "entry"); + if (!self->curblock) { + compile_error(ast_ctx(self), "failed to allocate entry block for `%s`", self->name); + return false; + } + + if (self->argc) { + ir_value *va_count; + ir_value *fixed; + ir_value *sub; + if (!ast_local_codegen(self->argc, self->ir_func, true)) + return false; + cgen = self->argc->expression.codegen; + if (!(*cgen)((ast_expression*)(self->argc), self, false, &va_count)) + return false; + cgen = self->fixedparams->expression.codegen; + if (!(*cgen)((ast_expression*)(self->fixedparams), self, false, &fixed)) + return false; + sub = ir_block_create_binop(self->curblock, ast_ctx(self), + ast_function_label(self, "va_count"), INSTR_SUB_F, + ir_builder_get_va_count(ir), fixed); + if (!sub) + return false; + if (!ir_block_create_store_op(self->curblock, ast_ctx(self), INSTR_STORE_F, + va_count, sub)) + { + return false; + } + } + + for (i = 0; i < vec_size(self->blocks); ++i) { + cgen = self->blocks[i]->expression.codegen; + if (!(*cgen)((ast_expression*)self->blocks[i], self, false, &dummy)) + return false; + } + + /* TODO: check return types */ + if (!self->curblock->final) + { + if (!self->vtype->expression.next || + self->vtype->expression.next->vtype == TYPE_VOID) + { + return ir_block_create_return(self->curblock, ast_ctx(self), NULL); + } + else if (vec_size(self->curblock->entries) || self->curblock == irf->first) + { + if (self->return_value) { + cgen = self->return_value->expression.codegen; + if (!(*cgen)((ast_expression*)(self->return_value), self, false, &dummy)) + return false; + return ir_block_create_return(self->curblock, ast_ctx(self), dummy); + } + else if (compile_warning(ast_ctx(self), WARN_MISSING_RETURN_VALUES, + "control reaches end of non-void function (`%s`) via %s", + self->name, self->curblock->label)) + { + return false; + } + return ir_block_create_return(self->curblock, ast_ctx(self), NULL); + } + } + return true; +} + +/* Note, you will not see ast_block_codegen generate ir_blocks. + * To the AST and the IR, blocks are 2 different things. + * In the AST it represents a block of code, usually enclosed in + * curly braces {...}. + * While in the IR it represents a block in terms of control-flow. + */ +bool ast_block_codegen(ast_block *self, ast_function *func, bool lvalue, ir_value **out) +{ + size_t i; + + /* We don't use this + * Note: an ast-representation using the comma-operator + * of the form: (a, b, c) = x should not assign to c... + */ + if (lvalue) { + compile_error(ast_ctx(self), "not an l-value (code-block)"); + return false; + } + + if (self->expression.outr) { + *out = self->expression.outr; + return true; + } + + /* output is NULL at first, we'll have each expression + * assign to out output, thus, a comma-operator represention + * using an ast_block will return the last generated value, + * so: (b, c) + a executed both b and c, and returns c, + * which is then added to a. + */ + *out = NULL; + + /* generate locals */ + for (i = 0; i < vec_size(self->locals); ++i) + { + if (!ast_local_codegen(self->locals[i], func->ir_func, false)) { + if (OPTS_OPTION_BOOL(OPTION_DEBUG)) + compile_error(ast_ctx(self), "failed to generate local `%s`", self->locals[i]->name); + return false; + } + } + + for (i = 0; i < vec_size(self->exprs); ++i) + { + ast_expression_codegen *gen; + if (func->curblock->final && !ast_istype(self->exprs[i], ast_label)) { + if (compile_warning(ast_ctx(self->exprs[i]), WARN_UNREACHABLE_CODE, "unreachable statement")) + return false; + continue; + } + gen = self->exprs[i]->codegen; + if (!(*gen)(self->exprs[i], func, false, out)) + return false; + } + + self->expression.outr = *out; + + return true; +} + +bool ast_store_codegen(ast_store *self, ast_function *func, bool lvalue, ir_value **out) +{ + ast_expression_codegen *cgen; + ir_value *left = NULL; + ir_value *right = NULL; + + ast_value *arr; + ast_value *idx = 0; + ast_array_index *ai = NULL; + + if (lvalue && self->expression.outl) { + *out = self->expression.outl; + return true; + } + + if (!lvalue && self->expression.outr) { + *out = self->expression.outr; + return true; + } + + if (ast_istype(self->dest, ast_array_index)) + { + + ai = (ast_array_index*)self->dest; + idx = (ast_value*)ai->index; + + if (ast_istype(ai->index, ast_value) && idx->hasvalue && idx->cvq == CV_CONST) + ai = NULL; + } + + if (ai) { + /* we need to call the setter */ + ir_value *iridx, *funval; + ir_instr *call; + + if (lvalue) { + compile_error(ast_ctx(self), "array-subscript assignment cannot produce lvalues"); + return false; + } + + arr = (ast_value*)ai->array; + if (!ast_istype(ai->array, ast_value) || !arr->setter) { + compile_error(ast_ctx(self), "value has no setter (%s)", arr->name); + return false; + } + + cgen = idx->expression.codegen; + if (!(*cgen)((ast_expression*)(idx), func, false, &iridx)) + return false; + + cgen = arr->setter->expression.codegen; + if (!(*cgen)((ast_expression*)(arr->setter), func, true, &funval)) + return false; + + cgen = self->source->codegen; + if (!(*cgen)((ast_expression*)(self->source), func, false, &right)) + return false; + + call = ir_block_create_call(func->curblock, ast_ctx(self), ast_function_label(func, "store"), funval, false); + if (!call) + return false; + ir_call_param(call, iridx); + ir_call_param(call, right); + self->expression.outr = right; + } + else + { + /* regular code */ + + cgen = self->dest->codegen; + /* lvalue! */ + if (!(*cgen)((ast_expression*)(self->dest), func, true, &left)) + return false; + self->expression.outl = left; + + cgen = self->source->codegen; + /* rvalue! */ + if (!(*cgen)((ast_expression*)(self->source), func, false, &right)) + return false; + + if (!ir_block_create_store_op(func->curblock, ast_ctx(self), self->op, left, right)) + return false; + self->expression.outr = right; + } + + /* Theoretically, an assinment returns its left side as an + * lvalue, if we don't need an lvalue though, we return + * the right side as an rvalue, otherwise we have to + * somehow know whether or not we need to dereference the pointer + * on the left side - that is: OP_LOAD if it was an address. + * Also: in original QC we cannot OP_LOADP *anyway*. + */ + *out = (lvalue ? left : right); + + return true; +} + +bool ast_binary_codegen(ast_binary *self, ast_function *func, bool lvalue, ir_value **out) +{ + ast_expression_codegen *cgen; + ir_value *left, *right; + + /* A binary operation cannot yield an l-value */ + if (lvalue) { + compile_error(ast_ctx(self), "not an l-value (binop)"); + return false; + } + + if (self->expression.outr) { + *out = self->expression.outr; + return true; + } + + if ((OPTS_FLAG(SHORT_LOGIC) || OPTS_FLAG(PERL_LOGIC)) && + (self->op == INSTR_AND || self->op == INSTR_OR)) + { + /* short circuit evaluation */ + ir_block *other, *merge; + ir_block *from_left, *from_right; + ir_instr *phi; + size_t merge_id; + + /* prepare end-block */ + merge_id = vec_size(func->ir_func->blocks); + merge = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "sce_merge")); + + /* generate the left expression */ + cgen = self->left->codegen; + if (!(*cgen)((ast_expression*)(self->left), func, false, &left)) + return false; + /* remember the block */ + from_left = func->curblock; + + /* create a new block for the right expression */ + other = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "sce_other")); + if (self->op == INSTR_AND) { + /* on AND: left==true -> other */ + if (!ir_block_create_if(func->curblock, ast_ctx(self), left, other, merge)) + return false; + } else { + /* on OR: left==false -> other */ + if (!ir_block_create_if(func->curblock, ast_ctx(self), left, merge, other)) + return false; + } + /* use the likely flag */ + vec_last(func->curblock->instr)->likely = true; + + /* enter the right-expression's block */ + func->curblock = other; + /* generate */ + cgen = self->right->codegen; + if (!(*cgen)((ast_expression*)(self->right), func, false, &right)) + return false; + /* remember block */ + from_right = func->curblock; + + /* jump to the merge block */ + if (!ir_block_create_jump(func->curblock, ast_ctx(self), merge)) + return false; + + vec_remove(func->ir_func->blocks, merge_id, 1); + vec_push(func->ir_func->blocks, merge); + + func->curblock = merge; + phi = ir_block_create_phi(func->curblock, ast_ctx(self), + ast_function_label(func, "sce_value"), + self->expression.vtype); + ir_phi_add(phi, from_left, left); + ir_phi_add(phi, from_right, right); + *out = ir_phi_value(phi); + if (!*out) + return false; + + if (!OPTS_FLAG(PERL_LOGIC)) { + /* cast-to-bool */ + if (OPTS_FLAG(CORRECT_LOGIC) && (*out)->vtype == TYPE_VECTOR) { + *out = ir_block_create_unary(func->curblock, ast_ctx(self), + ast_function_label(func, "sce_bool_v"), + INSTR_NOT_V, *out); + if (!*out) + return false; + *out = ir_block_create_unary(func->curblock, ast_ctx(self), + ast_function_label(func, "sce_bool"), + INSTR_NOT_F, *out); + if (!*out) + return false; + } + else if (OPTS_FLAG(FALSE_EMPTY_STRINGS) && (*out)->vtype == TYPE_STRING) { + *out = ir_block_create_unary(func->curblock, ast_ctx(self), + ast_function_label(func, "sce_bool_s"), + INSTR_NOT_S, *out); + if (!*out) + return false; + *out = ir_block_create_unary(func->curblock, ast_ctx(self), + ast_function_label(func, "sce_bool"), + INSTR_NOT_F, *out); + if (!*out) + return false; + } + else { + *out = ir_block_create_binop(func->curblock, ast_ctx(self), + ast_function_label(func, "sce_bool"), + INSTR_AND, *out, *out); + if (!*out) + return false; + } + } + + self->expression.outr = *out; + codegen_output_type(self, *out); + return true; + } + + cgen = self->left->codegen; + if (!(*cgen)((ast_expression*)(self->left), func, false, &left)) + return false; + + cgen = self->right->codegen; + if (!(*cgen)((ast_expression*)(self->right), func, false, &right)) + return false; + + *out = ir_block_create_binop(func->curblock, ast_ctx(self), ast_function_label(func, "bin"), + self->op, left, right); + if (!*out) + return false; + self->expression.outr = *out; + codegen_output_type(self, *out); + + return true; +} + +bool ast_binstore_codegen(ast_binstore *self, ast_function *func, bool lvalue, ir_value **out) +{ + ast_expression_codegen *cgen; + ir_value *leftl = NULL, *leftr, *right, *bin; + + ast_value *arr; + ast_value *idx = 0; + ast_array_index *ai = NULL; + ir_value *iridx = NULL; + + if (lvalue && self->expression.outl) { + *out = self->expression.outl; + return true; + } + + if (!lvalue && self->expression.outr) { + *out = self->expression.outr; + return true; + } + + if (ast_istype(self->dest, ast_array_index)) + { + + ai = (ast_array_index*)self->dest; + idx = (ast_value*)ai->index; + + if (ast_istype(ai->index, ast_value) && idx->hasvalue && idx->cvq == CV_CONST) + ai = NULL; + } + + /* for a binstore we need both an lvalue and an rvalue for the left side */ + /* rvalue of destination! */ + if (ai) { + cgen = idx->expression.codegen; + if (!(*cgen)((ast_expression*)(idx), func, false, &iridx)) + return false; + } + cgen = self->dest->codegen; + if (!(*cgen)((ast_expression*)(self->dest), func, false, &leftr)) + return false; + + /* source as rvalue only */ + cgen = self->source->codegen; + if (!(*cgen)((ast_expression*)(self->source), func, false, &right)) + return false; + + /* now the binary */ + bin = ir_block_create_binop(func->curblock, ast_ctx(self), ast_function_label(func, "binst"), + self->opbin, leftr, right); + self->expression.outr = bin; + + + if (ai) { + /* we need to call the setter */ + ir_value *funval; + ir_instr *call; + + if (lvalue) { + compile_error(ast_ctx(self), "array-subscript assignment cannot produce lvalues"); + return false; + } + + arr = (ast_value*)ai->array; + if (!ast_istype(ai->array, ast_value) || !arr->setter) { + compile_error(ast_ctx(self), "value has no setter (%s)", arr->name); + return false; + } + + cgen = arr->setter->expression.codegen; + if (!(*cgen)((ast_expression*)(arr->setter), func, true, &funval)) + return false; + + call = ir_block_create_call(func->curblock, ast_ctx(self), ast_function_label(func, "store"), funval, false); + if (!call) + return false; + ir_call_param(call, iridx); + ir_call_param(call, bin); + self->expression.outr = bin; + } else { + /* now store them */ + cgen = self->dest->codegen; + /* lvalue of destination */ + if (!(*cgen)((ast_expression*)(self->dest), func, true, &leftl)) + return false; + self->expression.outl = leftl; + + if (!ir_block_create_store_op(func->curblock, ast_ctx(self), self->opstore, leftl, bin)) + return false; + self->expression.outr = bin; + } + + /* Theoretically, an assinment returns its left side as an + * lvalue, if we don't need an lvalue though, we return + * the right side as an rvalue, otherwise we have to + * somehow know whether or not we need to dereference the pointer + * on the left side - that is: OP_LOAD if it was an address. + * Also: in original QC we cannot OP_LOADP *anyway*. + */ + *out = (lvalue ? leftl : bin); + + return true; +} + +bool ast_unary_codegen(ast_unary *self, ast_function *func, bool lvalue, ir_value **out) +{ + ast_expression_codegen *cgen; + ir_value *operand; + + /* An unary operation cannot yield an l-value */ + if (lvalue) { + compile_error(ast_ctx(self), "not an l-value (binop)"); + return false; + } + + if (self->expression.outr) { + *out = self->expression.outr; + return true; + } + + cgen = self->operand->codegen; + /* lvalue! */ + if (!(*cgen)((ast_expression*)(self->operand), func, false, &operand)) + return false; + + *out = ir_block_create_unary(func->curblock, ast_ctx(self), ast_function_label(func, "unary"), + self->op, operand); + if (!*out) + return false; + self->expression.outr = *out; + + return true; +} + +bool ast_return_codegen(ast_return *self, ast_function *func, bool lvalue, ir_value **out) +{ + ast_expression_codegen *cgen; + ir_value *operand; + + *out = NULL; + + /* In the context of a return operation, we don't actually return + * anything... + */ + if (lvalue) { + compile_error(ast_ctx(self), "return-expression is not an l-value"); + return false; + } + + if (self->expression.outr) { + compile_error(ast_ctx(self), "internal error: ast_return cannot be reused, it bears no result!"); + return false; + } + self->expression.outr = (ir_value*)1; + + if (self->operand) { + cgen = self->operand->codegen; + /* lvalue! */ + if (!(*cgen)((ast_expression*)(self->operand), func, false, &operand)) + return false; + + if (!ir_block_create_return(func->curblock, ast_ctx(self), operand)) + return false; + } else { + if (!ir_block_create_return(func->curblock, ast_ctx(self), NULL)) + return false; + } + + return true; +} + +bool ast_entfield_codegen(ast_entfield *self, ast_function *func, bool lvalue, ir_value **out) +{ + ast_expression_codegen *cgen; + ir_value *ent, *field; + + /* This function needs to take the 'lvalue' flag into account! + * As lvalue we provide a field-pointer, as rvalue we provide the + * value in a temp. + */ + + if (lvalue && self->expression.outl) { + *out = self->expression.outl; + return true; + } + + if (!lvalue && self->expression.outr) { + *out = self->expression.outr; + return true; + } + + cgen = self->entity->codegen; + if (!(*cgen)((ast_expression*)(self->entity), func, false, &ent)) + return false; + + cgen = self->field->codegen; + if (!(*cgen)((ast_expression*)(self->field), func, false, &field)) + return false; + + if (lvalue) { + /* address! */ + *out = ir_block_create_fieldaddress(func->curblock, ast_ctx(self), ast_function_label(func, "efa"), + ent, field); + } else { + *out = ir_block_create_load_from_ent(func->curblock, ast_ctx(self), ast_function_label(func, "efv"), + ent, field, self->expression.vtype); + /* Done AFTER error checking: + codegen_output_type(self, *out); + */ + } + if (!*out) { + compile_error(ast_ctx(self), "failed to create %s instruction (output type %s)", + (lvalue ? "ADDRESS" : "FIELD"), + type_name[self->expression.vtype]); + return false; + } + if (!lvalue) + codegen_output_type(self, *out); + + if (lvalue) + self->expression.outl = *out; + else + self->expression.outr = *out; + + /* Hm that should be it... */ + return true; +} + +bool ast_member_codegen(ast_member *self, ast_function *func, bool lvalue, ir_value **out) +{ + ast_expression_codegen *cgen; + ir_value *vec; + + /* in QC this is always an lvalue */ + if (lvalue && self->rvalue) { + compile_error(ast_ctx(self), "not an l-value (member access)"); + return false; + } + if (self->expression.outl) { + *out = self->expression.outl; + return true; + } + + cgen = self->owner->codegen; + if (!(*cgen)((ast_expression*)(self->owner), func, false, &vec)) + return false; + + if (vec->vtype != TYPE_VECTOR && + !(vec->vtype == TYPE_FIELD && self->owner->next->vtype == TYPE_VECTOR)) + { + return false; + } + + *out = ir_value_vector_member(vec, self->field); + self->expression.outl = *out; + + return (*out != NULL); +} + +bool ast_array_index_codegen(ast_array_index *self, ast_function *func, bool lvalue, ir_value **out) +{ + ast_value *arr; + ast_value *idx; + + if (!lvalue && self->expression.outr) { + *out = self->expression.outr; + return true; + } + if (lvalue && self->expression.outl) { + *out = self->expression.outl; + return true; + } + + if (!ast_istype(self->array, ast_value)) { + compile_error(ast_ctx(self), "array indexing this way is not supported"); + /* note this would actually be pointer indexing because the left side is + * not an actual array but (hopefully) an indexable expression. + * Once we get integer arithmetic, and GADDRESS/GSTORE/GLOAD instruction + * support this path will be filled. + */ + return false; + } + + arr = (ast_value*)self->array; + idx = (ast_value*)self->index; + + if (!ast_istype(self->index, ast_value) || !idx->hasvalue || idx->cvq != CV_CONST) { + /* Time to use accessor functions */ + ast_expression_codegen *cgen; + ir_value *iridx, *funval; + ir_instr *call; + + if (lvalue) { + compile_error(ast_ctx(self), "(.2) array indexing here needs a compile-time constant"); + return false; + } + + if (!arr->getter) { + compile_error(ast_ctx(self), "value has no getter, don't know how to index it"); + return false; + } + + cgen = self->index->codegen; + if (!(*cgen)((ast_expression*)(self->index), func, false, &iridx)) + return false; + + cgen = arr->getter->expression.codegen; + if (!(*cgen)((ast_expression*)(arr->getter), func, true, &funval)) + return false; + + call = ir_block_create_call(func->curblock, ast_ctx(self), ast_function_label(func, "fetch"), funval, false); + if (!call) + return false; + ir_call_param(call, iridx); + + *out = ir_call_value(call); + self->expression.outr = *out; + (*out)->vtype = self->expression.vtype; + codegen_output_type(self, *out); + return true; + } + + if (idx->expression.vtype == TYPE_FLOAT) { + unsigned int arridx = idx->constval.vfloat; + if (arridx >= self->array->count) + { + compile_error(ast_ctx(self), "array index out of bounds: %i", arridx); + return false; + } + *out = arr->ir_values[arridx]; + } + else if (idx->expression.vtype == TYPE_INTEGER) { + unsigned int arridx = idx->constval.vint; + if (arridx >= self->array->count) + { + compile_error(ast_ctx(self), "array index out of bounds: %i", arridx); + return false; + } + *out = arr->ir_values[arridx]; + } + else { + compile_error(ast_ctx(self), "array indexing here needs an integer constant"); + return false; + } + (*out)->vtype = self->expression.vtype; + codegen_output_type(self, *out); + return true; +} + +bool ast_ifthen_codegen(ast_ifthen *self, ast_function *func, bool lvalue, ir_value **out) +{ + ast_expression_codegen *cgen; + + ir_value *condval; + ir_value *dummy; + + ir_block *cond; + ir_block *ontrue; + ir_block *onfalse; + ir_block *ontrue_endblock = NULL; + ir_block *onfalse_endblock = NULL; + ir_block *merge = NULL; + + /* We don't output any value, thus also don't care about r/lvalue */ + (void)out; + (void)lvalue; + + if (self->expression.outr) { + compile_error(ast_ctx(self), "internal error: ast_ifthen cannot be reused, it bears no result!"); + return false; + } + self->expression.outr = (ir_value*)1; + + /* generate the condition */ + cgen = self->cond->codegen; + if (!(*cgen)((ast_expression*)(self->cond), func, false, &condval)) + return false; + /* update the block which will get the jump - because short-logic or ternaries may have changed this */ + cond = func->curblock; + + /* on-true path */ + + if (self->on_true) { + /* create on-true block */ + ontrue = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "ontrue")); + if (!ontrue) + return false; + + /* enter the block */ + func->curblock = ontrue; + + /* generate */ + cgen = self->on_true->codegen; + if (!(*cgen)((ast_expression*)(self->on_true), func, false, &dummy)) + return false; + + /* we now need to work from the current endpoint */ + ontrue_endblock = func->curblock; + } else + ontrue = NULL; + + /* on-false path */ + if (self->on_false) { + /* create on-false block */ + onfalse = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "onfalse")); + if (!onfalse) + return false; + + /* enter the block */ + func->curblock = onfalse; + + /* generate */ + cgen = self->on_false->codegen; + if (!(*cgen)((ast_expression*)(self->on_false), func, false, &dummy)) + return false; + + /* we now need to work from the current endpoint */ + onfalse_endblock = func->curblock; + } else + onfalse = NULL; + + /* Merge block were they all merge in to */ + if (!ontrue || !onfalse || !ontrue_endblock->final || !onfalse_endblock->final) + { + merge = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "endif")); + if (!merge) + return false; + /* add jumps ot the merge block */ + if (ontrue && !ontrue_endblock->final && !ir_block_create_jump(ontrue_endblock, ast_ctx(self), merge)) + return false; + if (onfalse && !onfalse_endblock->final && !ir_block_create_jump(onfalse_endblock, ast_ctx(self), merge)) + return false; + + /* Now enter the merge block */ + func->curblock = merge; + } + + /* we create the if here, that way all blocks are ordered :) + */ + if (!ir_block_create_if(cond, ast_ctx(self), condval, + (ontrue ? ontrue : merge), + (onfalse ? onfalse : merge))) + { + return false; + } + + return true; +} + +bool ast_ternary_codegen(ast_ternary *self, ast_function *func, bool lvalue, ir_value **out) +{ + ast_expression_codegen *cgen; + + ir_value *condval; + ir_value *trueval, *falseval; + ir_instr *phi; + + ir_block *cond = func->curblock; + ir_block *cond_out = NULL; + ir_block *ontrue, *ontrue_out = NULL; + ir_block *onfalse, *onfalse_out = NULL; + ir_block *merge; + + /* Ternary can never create an lvalue... */ + if (lvalue) + return false; + + /* In theory it shouldn't be possible to pass through a node twice, but + * in case we add any kind of optimization pass for the AST itself, it + * may still happen, thus we remember a created ir_value and simply return one + * if it already exists. + */ + if (self->expression.outr) { + *out = self->expression.outr; + return true; + } + + /* In the following, contraty to ast_ifthen, we assume both paths exist. */ + + /* generate the condition */ + func->curblock = cond; + cgen = self->cond->codegen; + if (!(*cgen)((ast_expression*)(self->cond), func, false, &condval)) + return false; + cond_out = func->curblock; + + /* create on-true block */ + ontrue = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "tern_T")); + if (!ontrue) + return false; + else + { + /* enter the block */ + func->curblock = ontrue; + + /* generate */ + cgen = self->on_true->codegen; + if (!(*cgen)((ast_expression*)(self->on_true), func, false, &trueval)) + return false; + + ontrue_out = func->curblock; + } + + /* create on-false block */ + onfalse = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "tern_F")); + if (!onfalse) + return false; + else + { + /* enter the block */ + func->curblock = onfalse; + + /* generate */ + cgen = self->on_false->codegen; + if (!(*cgen)((ast_expression*)(self->on_false), func, false, &falseval)) + return false; + + onfalse_out = func->curblock; + } + + /* create merge block */ + merge = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "tern_out")); + if (!merge) + return false; + /* jump to merge block */ + if (!ir_block_create_jump(ontrue_out, ast_ctx(self), merge)) + return false; + if (!ir_block_create_jump(onfalse_out, ast_ctx(self), merge)) + return false; + + /* create if instruction */ + if (!ir_block_create_if(cond_out, ast_ctx(self), condval, ontrue, onfalse)) + return false; + + /* Now enter the merge block */ + func->curblock = merge; + + /* Here, now, we need a PHI node + * but first some sanity checking... + */ + if (trueval->vtype != falseval->vtype && trueval->vtype != TYPE_NIL && falseval->vtype != TYPE_NIL) { + /* error("ternary with different types on the two sides"); */ + compile_error(ast_ctx(self), "internal error: ternary operand types invalid"); + return false; + } + + /* create PHI */ + phi = ir_block_create_phi(merge, ast_ctx(self), ast_function_label(func, "phi"), self->expression.vtype); + if (!phi) { + compile_error(ast_ctx(self), "internal error: failed to generate phi node"); + return false; + } + ir_phi_add(phi, ontrue_out, trueval); + ir_phi_add(phi, onfalse_out, falseval); + + self->expression.outr = ir_phi_value(phi); + *out = self->expression.outr; + + codegen_output_type(self, *out); + + return true; +} + +bool ast_loop_codegen(ast_loop *self, ast_function *func, bool lvalue, ir_value **out) +{ + ast_expression_codegen *cgen; + + ir_value *dummy = NULL; + ir_value *precond = NULL; + ir_value *postcond = NULL; + + /* Since we insert some jumps "late" so we have blocks + * ordered "nicely", we need to keep track of the actual end-blocks + * of expressions to add the jumps to. + */ + ir_block *bbody = NULL, *end_bbody = NULL; + ir_block *bprecond = NULL, *end_bprecond = NULL; + ir_block *bpostcond = NULL, *end_bpostcond = NULL; + ir_block *bincrement = NULL, *end_bincrement = NULL; + ir_block *bout = NULL, *bin = NULL; + + /* let's at least move the outgoing block to the end */ + size_t bout_id; + + /* 'break' and 'continue' need to be able to find the right blocks */ + ir_block *bcontinue = NULL; + ir_block *bbreak = NULL; + + ir_block *tmpblock = NULL; + + (void)lvalue; + (void)out; + + if (self->expression.outr) { + compile_error(ast_ctx(self), "internal error: ast_loop cannot be reused, it bears no result!"); + return false; + } + self->expression.outr = (ir_value*)1; + + /* NOTE: + * Should we ever need some kind of block ordering, better make this function + * move blocks around than write a block ordering algorithm later... after all + * the ast and ir should work together, not against each other. + */ + + /* initexpr doesn't get its own block, it's pointless, it could create more blocks + * anyway if for example it contains a ternary. + */ + if (self->initexpr) + { + cgen = self->initexpr->codegen; + if (!(*cgen)((ast_expression*)(self->initexpr), func, false, &dummy)) + return false; + } + + /* Store the block from which we enter this chaos */ + bin = func->curblock; + + /* The pre-loop condition needs its own block since we + * need to be able to jump to the start of that expression. + */ + if (self->precond) + { + bprecond = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "pre_loop_cond")); + if (!bprecond) + return false; + + /* the pre-loop-condition the least important place to 'continue' at */ + bcontinue = bprecond; + + /* enter */ + func->curblock = bprecond; + + /* generate */ + cgen = self->precond->codegen; + if (!(*cgen)((ast_expression*)(self->precond), func, false, &precond)) + return false; + + end_bprecond = func->curblock; + } else { + bprecond = end_bprecond = NULL; + } + + /* Now the next blocks won't be ordered nicely, but we need to + * generate them this early for 'break' and 'continue'. + */ + if (self->increment) { + bincrement = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "loop_increment")); + if (!bincrement) + return false; + bcontinue = bincrement; /* increment comes before the pre-loop-condition */ + } else { + bincrement = end_bincrement = NULL; + } + + if (self->postcond) { + bpostcond = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "post_loop_cond")); + if (!bpostcond) + return false; + bcontinue = bpostcond; /* postcond comes before the increment */ + } else { + bpostcond = end_bpostcond = NULL; + } + + bout_id = vec_size(func->ir_func->blocks); + bout = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "after_loop")); + if (!bout) + return false; + bbreak = bout; + + /* The loop body... */ + /* if (self->body) */ + { + bbody = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "loop_body")); + if (!bbody) + return false; + + /* enter */ + func->curblock = bbody; + + vec_push(func->breakblocks, bbreak); + if (bcontinue) + vec_push(func->continueblocks, bcontinue); + else + vec_push(func->continueblocks, bbody); + + /* generate */ + if (self->body) { + cgen = self->body->codegen; + if (!(*cgen)((ast_expression*)(self->body), func, false, &dummy)) + return false; + } + + end_bbody = func->curblock; + vec_pop(func->breakblocks); + vec_pop(func->continueblocks); + } + + /* post-loop-condition */ + if (self->postcond) + { + /* enter */ + func->curblock = bpostcond; + + /* generate */ + cgen = self->postcond->codegen; + if (!(*cgen)((ast_expression*)(self->postcond), func, false, &postcond)) + return false; + + end_bpostcond = func->curblock; + } + + /* The incrementor */ + if (self->increment) + { + /* enter */ + func->curblock = bincrement; + + /* generate */ + cgen = self->increment->codegen; + if (!(*cgen)((ast_expression*)(self->increment), func, false, &dummy)) + return false; + + end_bincrement = func->curblock; + } + + /* In any case now, we continue from the outgoing block */ + func->curblock = bout; + + /* Now all blocks are in place */ + /* From 'bin' we jump to whatever comes first */ + if (bprecond) tmpblock = bprecond; + else if (bbody) tmpblock = bbody; + else if (bpostcond) tmpblock = bpostcond; + else tmpblock = bout; + if (!ir_block_create_jump(bin, ast_ctx(self), tmpblock)) + return false; + + /* From precond */ + if (bprecond) + { + ir_block *ontrue, *onfalse; + if (bbody) ontrue = bbody; + else if (bincrement) ontrue = bincrement; + else if (bpostcond) ontrue = bpostcond; + else ontrue = bprecond; + onfalse = bout; + if (self->pre_not) { + tmpblock = ontrue; + ontrue = onfalse; + onfalse = tmpblock; + } + if (!ir_block_create_if(end_bprecond, ast_ctx(self), precond, ontrue, onfalse)) + return false; + } + + /* from body */ + if (bbody) + { + if (bincrement) tmpblock = bincrement; + else if (bpostcond) tmpblock = bpostcond; + else if (bprecond) tmpblock = bprecond; + else tmpblock = bbody; + if (!end_bbody->final && !ir_block_create_jump(end_bbody, ast_ctx(self), tmpblock)) + return false; + } + + /* from increment */ + if (bincrement) + { + if (bpostcond) tmpblock = bpostcond; + else if (bprecond) tmpblock = bprecond; + else if (bbody) tmpblock = bbody; + else tmpblock = bout; + if (!ir_block_create_jump(end_bincrement, ast_ctx(self), tmpblock)) + return false; + } + + /* from postcond */ + if (bpostcond) + { + ir_block *ontrue, *onfalse; + if (bprecond) ontrue = bprecond; + else if (bbody) ontrue = bbody; + else if (bincrement) ontrue = bincrement; + else ontrue = bpostcond; + onfalse = bout; + if (self->post_not) { + tmpblock = ontrue; + ontrue = onfalse; + onfalse = tmpblock; + } + if (!ir_block_create_if(end_bpostcond, ast_ctx(self), postcond, ontrue, onfalse)) + return false; + } + + /* Move 'bout' to the end */ + vec_remove(func->ir_func->blocks, bout_id, 1); + vec_push(func->ir_func->blocks, bout); + + return true; +} + +bool ast_breakcont_codegen(ast_breakcont *self, ast_function *func, bool lvalue, ir_value **out) +{ + ir_block *target; + + *out = NULL; + + if (lvalue) { + compile_error(ast_ctx(self), "break/continue expression is not an l-value"); + return false; + } + + if (self->expression.outr) { + compile_error(ast_ctx(self), "internal error: ast_breakcont cannot be reused!"); + return false; + } + self->expression.outr = (ir_value*)1; + + if (self->is_continue) + target = func->continueblocks[vec_size(func->continueblocks)-1-self->levels]; + else + target = func->breakblocks[vec_size(func->breakblocks)-1-self->levels]; + + if (!target) { + compile_error(ast_ctx(self), "%s is lacking a target block", (self->is_continue ? "continue" : "break")); + return false; + } + + if (!ir_block_create_jump(func->curblock, ast_ctx(self), target)) + return false; + return true; +} + +bool ast_switch_codegen(ast_switch *self, ast_function *func, bool lvalue, ir_value **out) +{ + ast_expression_codegen *cgen; + + ast_switch_case *def_case = NULL; + ir_block *def_bfall = NULL; + ir_block *def_bfall_to = NULL; + bool set_def_bfall_to = false; + + ir_value *dummy = NULL; + ir_value *irop = NULL; + ir_block *bout = NULL; + ir_block *bfall = NULL; + size_t bout_id; + size_t c; + + char typestr[1024]; + uint16_t cmpinstr; + + if (lvalue) { + compile_error(ast_ctx(self), "switch expression is not an l-value"); + return false; + } + + if (self->expression.outr) { + compile_error(ast_ctx(self), "internal error: ast_switch cannot be reused!"); + return false; + } + self->expression.outr = (ir_value*)1; + + (void)lvalue; + (void)out; + + cgen = self->operand->codegen; + if (!(*cgen)((ast_expression*)(self->operand), func, false, &irop)) + return false; + + if (!vec_size(self->cases)) + return true; + + cmpinstr = type_eq_instr[irop->vtype]; + if (cmpinstr >= VINSTR_END) { + ast_type_to_string(self->operand, typestr, sizeof(typestr)); + compile_error(ast_ctx(self), "invalid type to perform a switch on: %s", typestr); + return false; + } + + bout_id = vec_size(func->ir_func->blocks); + bout = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "after_switch")); + if (!bout) + return false; + + /* setup the break block */ + vec_push(func->breakblocks, bout); + + /* Now create all cases */ + for (c = 0; c < vec_size(self->cases); ++c) { + ir_value *cond, *val; + ir_block *bcase, *bnot; + size_t bnot_id; + + ast_switch_case *swcase = &self->cases[c]; + + if (swcase->value) { + /* A regular case */ + /* generate the condition operand */ + cgen = swcase->value->codegen; + if (!(*cgen)((ast_expression*)(swcase->value), func, false, &val)) + return false; + /* generate the condition */ + cond = ir_block_create_binop(func->curblock, ast_ctx(self), ast_function_label(func, "switch_eq"), cmpinstr, irop, val); + if (!cond) + return false; + + bcase = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "case")); + bnot_id = vec_size(func->ir_func->blocks); + bnot = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "not_case")); + if (!bcase || !bnot) + return false; + if (set_def_bfall_to) { + set_def_bfall_to = false; + def_bfall_to = bcase; + } + if (!ir_block_create_if(func->curblock, ast_ctx(self), cond, bcase, bnot)) + return false; + + /* Make the previous case-end fall through */ + if (bfall && !bfall->final) { + if (!ir_block_create_jump(bfall, ast_ctx(self), bcase)) + return false; + } + + /* enter the case */ + func->curblock = bcase; + cgen = swcase->code->codegen; + if (!(*cgen)((ast_expression*)swcase->code, func, false, &dummy)) + return false; + + /* remember this block to fall through from */ + bfall = func->curblock; + + /* enter the else and move it down */ + func->curblock = bnot; + vec_remove(func->ir_func->blocks, bnot_id, 1); + vec_push(func->ir_func->blocks, bnot); + } else { + /* The default case */ + /* Remember where to fall through from: */ + def_bfall = bfall; + bfall = NULL; + /* remember which case it was */ + def_case = swcase; + /* And the next case will be remembered */ + set_def_bfall_to = true; + } + } + + /* Jump from the last bnot to bout */ + if (bfall && !bfall->final && !ir_block_create_jump(bfall, ast_ctx(self), bout)) { + /* + astwarning(ast_ctx(bfall), WARN_???, "missing break after last case"); + */ + return false; + } + + /* If there was a default case, put it down here */ + if (def_case) { + ir_block *bcase; + + /* No need to create an extra block */ + bcase = func->curblock; + + /* Insert the fallthrough jump */ + if (def_bfall && !def_bfall->final) { + if (!ir_block_create_jump(def_bfall, ast_ctx(self), bcase)) + return false; + } + + /* Now generate the default code */ + cgen = def_case->code->codegen; + if (!(*cgen)((ast_expression*)def_case->code, func, false, &dummy)) + return false; + + /* see if we need to fall through */ + if (def_bfall_to && !func->curblock->final) + { + if (!ir_block_create_jump(func->curblock, ast_ctx(self), def_bfall_to)) + return false; + } + } + + /* Jump from the last bnot to bout */ + if (!func->curblock->final && !ir_block_create_jump(func->curblock, ast_ctx(self), bout)) + return false; + /* enter the outgoing block */ + func->curblock = bout; + + /* restore the break block */ + vec_pop(func->breakblocks); + + /* Move 'bout' to the end, it's nicer */ + vec_remove(func->ir_func->blocks, bout_id, 1); + vec_push(func->ir_func->blocks, bout); + + return true; +} + +bool ast_label_codegen(ast_label *self, ast_function *func, bool lvalue, ir_value **out) +{ + size_t i; + ir_value *dummy; + + if (self->undefined) { + compile_error(ast_ctx(self), "internal error: ast_label never defined"); + return false; + } + + *out = NULL; + if (lvalue) { + compile_error(ast_ctx(self), "internal error: ast_label cannot be an lvalue"); + return false; + } + + /* simply create a new block and jump to it */ + self->irblock = ir_function_create_block(ast_ctx(self), func->ir_func, self->name); + if (!self->irblock) { + compile_error(ast_ctx(self), "failed to allocate label block `%s`", self->name); + return false; + } + if (!func->curblock->final) { + if (!ir_block_create_jump(func->curblock, ast_ctx(self), self->irblock)) + return false; + } + + /* enter the new block */ + func->curblock = self->irblock; + + /* Generate all the leftover gotos */ + for (i = 0; i < vec_size(self->gotos); ++i) { + if (!ast_goto_codegen(self->gotos[i], func, false, &dummy)) + return false; + } + + return true; +} + +bool ast_goto_codegen(ast_goto *self, ast_function *func, bool lvalue, ir_value **out) +{ + *out = NULL; + if (lvalue) { + compile_error(ast_ctx(self), "internal error: ast_goto cannot be an lvalue"); + return false; + } + + if (self->target->irblock) { + if (self->irblock_from) { + /* we already tried once, this is the callback */ + self->irblock_from->final = false; + if (!ir_block_create_goto(self->irblock_from, ast_ctx(self), self->target->irblock)) { + compile_error(ast_ctx(self), "failed to generate goto to `%s`", self->name); + return false; + } + } + else + { + if (!ir_block_create_goto(func->curblock, ast_ctx(self), self->target->irblock)) { + compile_error(ast_ctx(self), "failed to generate goto to `%s`", self->name); + return false; + } + } + } + else + { + /* the target has not yet been created... + * close this block in a sneaky way: + */ + func->curblock->final = true; + self->irblock_from = func->curblock; + ast_label_register_goto(self->target, self); + } + + return true; +} + +bool ast_call_codegen(ast_call *self, ast_function *func, bool lvalue, ir_value **out) +{ + ast_expression_codegen *cgen; + ir_value **params; + ir_instr *callinstr; + size_t i; + + ir_value *funval = NULL; + + /* return values are never lvalues */ + if (lvalue) { + compile_error(ast_ctx(self), "not an l-value (function call)"); + return false; + } + + if (self->expression.outr) { + *out = self->expression.outr; + return true; + } + + cgen = self->func->codegen; + if (!(*cgen)((ast_expression*)(self->func), func, false, &funval)) + return false; + if (!funval) + return false; + + params = NULL; + + /* parameters */ + for (i = 0; i < vec_size(self->params); ++i) + { + ir_value *param; + ast_expression *expr = self->params[i]; + + cgen = expr->codegen; + if (!(*cgen)(expr, func, false, ¶m)) + goto error; + if (!param) + goto error; + vec_push(params, param); + } + + /* varargs counter */ + if (self->va_count) { + ir_value *va_count; + ir_builder *builder = func->curblock->owner->owner; + cgen = self->va_count->codegen; + if (!(*cgen)((ast_expression*)(self->va_count), func, false, &va_count)) + return false; + if (!ir_block_create_store_op(func->curblock, ast_ctx(self), INSTR_STORE_F, + ir_builder_get_va_count(builder), va_count)) + { + return false; + } + } + + callinstr = ir_block_create_call(func->curblock, ast_ctx(self), + ast_function_label(func, "call"), + funval, !!(self->func->flags & AST_FLAG_NORETURN)); + if (!callinstr) + goto error; + + for (i = 0; i < vec_size(params); ++i) { + ir_call_param(callinstr, params[i]); + } + + *out = ir_call_value(callinstr); + self->expression.outr = *out; + + codegen_output_type(self, *out); + + vec_free(params); + return true; +error: + vec_free(params); + return false; +} diff --git a/misc/source/gmqcc-src/ast.h b/misc/source/gmqcc-src/ast.h new file mode 100644 index 00000000..a0fa14de --- /dev/null +++ b/misc/source/gmqcc-src/ast.h @@ -0,0 +1,638 @@ +/* + * Copyright (C) 2012, 2013 + * Wolfgang Bumiller + * Dale Weiler + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef GMQCC_AST_HDR +#define GMQCC_AST_HDR +#include "ir.h" + +/* Note: I will not be using a _t suffix for the + * "main" ast node types for now. + */ + +typedef struct ast_node_common ast_node; +typedef struct ast_expression_common ast_expression; + +typedef struct ast_value_s ast_value; +typedef struct ast_function_s ast_function; +typedef struct ast_block_s ast_block; +typedef struct ast_binary_s ast_binary; +typedef struct ast_store_s ast_store; +typedef struct ast_binstore_s ast_binstore; +typedef struct ast_entfield_s ast_entfield; +typedef struct ast_ifthen_s ast_ifthen; +typedef struct ast_ternary_s ast_ternary; +typedef struct ast_loop_s ast_loop; +typedef struct ast_call_s ast_call; +typedef struct ast_unary_s ast_unary; +typedef struct ast_return_s ast_return; +typedef struct ast_member_s ast_member; +typedef struct ast_array_index_s ast_array_index; +typedef struct ast_breakcont_s ast_breakcont; +typedef struct ast_switch_s ast_switch; +typedef struct ast_label_s ast_label; +typedef struct ast_goto_s ast_goto; + +enum { + TYPE_ast_node, /* 0 */ + TYPE_ast_expression, /* 1 */ + TYPE_ast_value, /* 2 */ + TYPE_ast_function, /* 3 */ + TYPE_ast_block, /* 4 */ + TYPE_ast_binary, /* 5 */ + TYPE_ast_store, /* 6 */ + TYPE_ast_binstore, /* 7 */ + TYPE_ast_entfield, /* 8 */ + TYPE_ast_ifthen, /* 9 */ + TYPE_ast_ternary, /* 10 */ + TYPE_ast_loop, /* 11 */ + TYPE_ast_call, /* 12 */ + TYPE_ast_unary, /* 13 */ + TYPE_ast_return, /* 14 */ + TYPE_ast_member, /* 15 */ + TYPE_ast_array_index, /* 16 */ + TYPE_ast_breakcont, /* 17 */ + TYPE_ast_switch, /* 18 */ + TYPE_ast_label, /* 19 */ + TYPE_ast_goto /* 20 */ +}; + +#define ast_istype(x, t) ( ((ast_node*)x)->nodetype == (TYPE_##t) ) +#define ast_ctx(node) (((ast_node*)(node))->context) +#define ast_side_effects(node) (((ast_node*)(node))->side_effects) + +/* Node interface with common components + */ +typedef void ast_node_delete(ast_node*); +struct ast_node_common +{ + lex_ctx context; + /* I don't feel comfortable using keywords like 'delete' as names... */ + ast_node_delete *destroy; + int nodetype; + /* keep: if a node contains this node, 'keep' + * prevents its dtor from destroying this node as well. + */ + bool keep; + bool side_effects; +}; + +#define ast_delete(x) (*( ((ast_node*)(x))->destroy ))((ast_node*)(x)) +#define ast_unref(x) do \ +{ \ + if (! (((ast_node*)(x))->keep) ) { \ + ast_delete(x); \ + } \ +} while(0) + +/* Expression interface + * + * Any expression or block returns an ir_value, and needs + * to know the current function. + */ +typedef bool ast_expression_codegen(ast_expression*, + ast_function*, + bool lvalue, + ir_value**); +/* TODO: the codegen function should take an output-type parameter + * indicating whether a variable, type, label etc. is expected, and + * an environment! + * Then later an ast_ident could have a codegen using this to figure + * out what to look for. + * eg. in code which uses a not-yet defined variable, the expression + * would take an ast_ident, and the codegen would be called with + * type `expression`, so the ast_ident's codegen would search for + * variables through the environment (or functions, constants...). + */ +struct ast_expression_common +{ + ast_node node; + ast_expression_codegen *codegen; + int vtype; + ast_expression *next; + /* arrays get a member-count */ + size_t count; + ast_value* *params; + uint32_t flags; + /* void foo(string...) gets varparam set as a restriction + * for variadic parameters + */ + ast_expression *varparam; + /* The codegen functions should store their output values + * so we can call it multiple times without re-evaluating. + * Store lvalue and rvalue seperately though. So that + * ast_entfield for example can generate both if required. + */ + ir_value *outl; + ir_value *outr; +}; +#define AST_FLAG_VARIADIC (1<<0) +#define AST_FLAG_NORETURN (1<<1) +#define AST_FLAG_INLINE (1<<2) +#define AST_FLAG_INITIALIZED (1<<3) +#define AST_FLAG_DEPRECATED (1<<4) +#define AST_FLAG_INCLUDE_DEF (1<<5) +#define AST_FLAG_IS_VARARG (1<<6) +#define AST_FLAG_ALIAS (1<<7) +#define AST_FLAG_TYPE_MASK (AST_FLAG_VARIADIC | AST_FLAG_NORETURN) + +/* Value + * + * Types are also values, both have a type and a name. + * especially considering possible constructs like typedefs. + * typedef float foo; + * is like creating a 'float foo', foo serving as the type's name. + */ +typedef union { + double vfloat; + int vint; + vector vvec; + const char *vstring; + int ventity; + ast_function *vfunc; + ast_value *vfield; +} basic_value_t; +struct ast_value_s +{ + ast_expression expression; + + const char *name; + const char *desc; + + const char *argcounter; + + /* + int vtype; + ast_value *next; + */ + + int cvq; /* const/var qualifier */ + bool isfield; /* this declares a field */ + bool isimm; /* an immediate, not just const */ + bool hasvalue; + basic_value_t constval; + /* for TYPE_ARRAY we have an optional vector + * of constants when an initializer list + * was provided. + */ + basic_value_t *initlist; + + /* usecount for the parser */ + size_t uses; + + ir_value *ir_v; + ir_value **ir_values; + size_t ir_value_count; + + /* ONLY for arrays in progs version up to 6 */ + ast_value *setter; + ast_value *getter; +}; + +ast_value* ast_value_new(lex_ctx ctx, const char *name, int qctype); +ast_value* ast_value_copy(const ast_value *self); +/* This will NOT delete an underlying ast_function */ +void ast_value_delete(ast_value*); + +bool ast_value_set_name(ast_value*, const char *name); + +/* +bool ast_value_codegen(ast_value*, ast_function*, bool lvalue, ir_value**); +bool ast_local_codegen(ast_value *self, ir_function *func, bool isparam); +*/ + +bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield); + +void ast_value_params_add(ast_value*, ast_value*); + +bool ast_compare_type(ast_expression *a, ast_expression *b); +ast_expression* ast_type_copy(lex_ctx ctx, const ast_expression *ex); +#define ast_type_adopt(a, b) ast_type_adopt_impl((ast_expression*)(a), (ast_expression*)(b)) +void ast_type_adopt_impl(ast_expression *self, const ast_expression *other); +void ast_type_to_string(ast_expression *e, char *buf, size_t bufsize); + +typedef enum ast_binary_ref_s { + AST_REF_LEFT = 1 << 1, + AST_REF_RIGHT = 1 << 2, + AST_REF_ALL = (AST_REF_LEFT | AST_REF_RIGHT) +} ast_binary_ref; + + +/* Binary + * + * A value-returning binary expression. + */ +struct ast_binary_s +{ + ast_expression expression; + + int op; + ast_expression *left; + ast_expression *right; + ast_binary_ref refs; + +}; +ast_binary* ast_binary_new(lex_ctx ctx, + int op, + ast_expression *left, + ast_expression *right); + +/* Binstore + * + * An assignment including a binary expression with the source as left operand. + * Eg. a += b; is a binstore { INSTR_STORE, INSTR_ADD, a, b } + */ +struct ast_binstore_s +{ + ast_expression expression; + + int opstore; + int opbin; + ast_expression *dest; + ast_expression *source; + /* for &~= which uses the destination in a binary in source we can use this */ + bool keep_dest; +}; +ast_binstore* ast_binstore_new(lex_ctx ctx, + int storeop, + int op, + ast_expression *left, + ast_expression *right); + +/* Unary + * + * Regular unary expressions: not,neg + */ +struct ast_unary_s +{ + ast_expression expression; + + int op; + ast_expression *operand; +}; +ast_unary* ast_unary_new(lex_ctx ctx, + int op, + ast_expression *expr); + +/* Return + * + * Make sure 'return' only happens at the end of a block, otherwise the IR + * will refuse to create further instructions. + * This should be honored by the parser. + */ +struct ast_return_s +{ + ast_expression expression; + ast_expression *operand; +}; +ast_return* ast_return_new(lex_ctx ctx, + ast_expression *expr); + +/* Entity-field + * + * This must do 2 things: + * -) Provide a way to fetch an entity field value. (Rvalue) + * -) Provide a pointer to an entity field. (Lvalue) + * The problem: + * In original QC, there's only a STORE via pointer, but + * no LOAD via pointer. + * So we must know beforehand if we are going to read or assign + * the field. + * For this we will have to extend the codegen() functions with + * a flag saying whether or not we need an L or an R-value. + */ +struct ast_entfield_s +{ + ast_expression expression; + /* The entity can come from an expression of course. */ + ast_expression *entity; + /* As can the field, it just must result in a value of TYPE_FIELD */ + ast_expression *field; +}; +ast_entfield* ast_entfield_new(lex_ctx ctx, ast_expression *entity, ast_expression *field); +ast_entfield* ast_entfield_new_force(lex_ctx ctx, ast_expression *entity, ast_expression *field, const ast_expression *outtype); + +/* Member access: + * + * For now used for vectors. If we get structs or unions + * we can have them handled here as well. + */ +struct ast_member_s +{ + ast_expression expression; + ast_expression *owner; + unsigned int field; + const char *name; + bool rvalue; +}; +ast_member* ast_member_new(lex_ctx ctx, ast_expression *owner, unsigned int field, const char *name); +void ast_member_delete(ast_member*); +bool ast_member_set_name(ast_member*, const char *name); + + +/* Array index access: + * + * QC forces us to take special action on arrays: + * an ast_store on an ast_array_index must not codegen the index, + * but call its setter - unless we have an instruction set which supports + * what we need. + * Any other array index access will be codegened to a call to the getter. + * In any case, accessing an element via a compiletime-constant index will + * result in quick access to that variable. + */ +struct ast_array_index_s +{ + ast_expression expression; + ast_expression *array; + ast_expression *index; +}; +ast_array_index* ast_array_index_new(lex_ctx ctx, ast_expression *array, ast_expression *index); + +/* Store + * + * Stores left<-right and returns left. + * Specialized binary expression node + */ +struct ast_store_s +{ + ast_expression expression; + int op; + ast_expression *dest; + ast_expression *source; +}; +ast_store* ast_store_new(lex_ctx ctx, int op, + ast_expression *d, ast_expression *s); + +/* If + * + * A general 'if then else' statement, either side can be NULL and will + * thus be omitted. It is an error for *both* cases to be NULL at once. + * + * During its 'codegen' it'll be changing the ast_function's block. + * + * An if is also an "expression". Its codegen will put NULL into the + * output field though. For ternary expressions an ast_ternary will be + * added. + */ +struct ast_ifthen_s +{ + ast_expression expression; + ast_expression *cond; + /* It's all just 'expressions', since an ast_block is one too. */ + ast_expression *on_true; + ast_expression *on_false; +}; +ast_ifthen* ast_ifthen_new(lex_ctx ctx, ast_expression *cond, ast_expression *ontrue, ast_expression *onfalse); + +/* Ternary expressions... + * + * Contrary to 'if-then-else' nodes, ternary expressions actually + * return a value, otherwise they behave the very same way. + * The difference in 'codegen' is that it'll return the value of + * a PHI node. + * + * The other difference is that in an ast_ternary, NEITHER side + * must be NULL, there's ALWAYS an else branch. + * + * This is the only ast_node beside ast_value which contains + * an ir_value. Theoretically we don't need to remember it though. + */ +struct ast_ternary_s +{ + ast_expression expression; + ast_expression *cond; + /* It's all just 'expressions', since an ast_block is one too. */ + ast_expression *on_true; + ast_expression *on_false; +}; +ast_ternary* ast_ternary_new(lex_ctx ctx, ast_expression *cond, ast_expression *ontrue, ast_expression *onfalse); + +/* A general loop node + * + * For convenience it contains 4 parts: + * -) (ini) = initializing expression + * -) (pre) = pre-loop condition + * -) (pst) = post-loop condition + * -) (inc) = "increment" expression + * The following is a psudo-representation of this loop + * note that '=>' bears the logical meaning of "implies". + * (a => b) equals (!a || b) + +{ini}; +while (has_pre => {pre}) +{ + {body}; + +continue: // a 'continue' will jump here + if (has_pst => {pst}) + break; + + {inc}; +} + */ +struct ast_loop_s +{ + ast_expression expression; + ast_expression *initexpr; + ast_expression *precond; + ast_expression *postcond; + ast_expression *increment; + ast_expression *body; + /* For now we allow a seperate flag on whether or not the condition + * is supposed to be true or false. + * That way, the parser can generate a 'while not(!x)' for `while(x)` + * if desired, which is useful for the new -f{true,false}-empty-strings + * flag. + */ + bool pre_not; + bool post_not; +}; +ast_loop* ast_loop_new(lex_ctx ctx, + ast_expression *initexpr, + ast_expression *precond, bool pre_not, + ast_expression *postcond, bool post_not, + ast_expression *increment, + ast_expression *body); + +/* Break/Continue + */ +struct ast_breakcont_s +{ + ast_expression expression; + bool is_continue; + unsigned int levels; +}; +ast_breakcont* ast_breakcont_new(lex_ctx ctx, bool iscont, unsigned int levels); + +/* Switch Statements + * + * A few notes about this: with the original QCVM, no real optimization + * is possible. The SWITCH instruction set isn't really helping a lot, since + * it only collapes the EQ and IF instructions into one. + * Note: Declaring local variables inside caseblocks is normal. + * Since we don't have to deal with a stack there's no unnatural behaviour to + * be expected from it. + * TODO: Ticket #20 + */ +typedef struct { + ast_expression *value; /* #20 will replace this */ + ast_expression *code; +} ast_switch_case; +struct ast_switch_s +{ + ast_expression expression; + + ast_expression *operand; + ast_switch_case *cases; +}; + +ast_switch* ast_switch_new(lex_ctx ctx, ast_expression *op); + +/* Label nodes + * + * Introduce a label which can be used together with 'goto' + */ +struct ast_label_s +{ + ast_expression expression; + const char *name; + ir_block *irblock; + ast_goto **gotos; + /* means it has not yet been defined */ + bool undefined; +}; + +ast_label* ast_label_new(lex_ctx ctx, const char *name, bool undefined); + +/* GOTO nodes + * + * Go to a label, the label node is filled in at a later point! + */ +struct ast_goto_s +{ + ast_expression expression; + const char *name; + ast_label *target; + ir_block *irblock_from; +}; + +ast_goto* ast_goto_new(lex_ctx ctx, const char *name); +void ast_goto_set_label(ast_goto*, ast_label*); + +/* CALL node + * + * Contains an ast_expression as target, rather than an ast_function/value. + * Since it's how QC works, every ast_function has an ast_value + * associated anyway - in other words, the VM contains function + * pointers for every function anyway. Thus, this node will call + * expression. + * Additionally it contains a list of ast_expressions as parameters. + * Since calls can return values, an ast_call is also an ast_expression. + */ +struct ast_call_s +{ + ast_expression expression; + ast_expression *func; + ast_expression* *params; + ast_expression *va_count; +}; +ast_call* ast_call_new(lex_ctx ctx, + ast_expression *funcexpr); +bool ast_call_check_types(ast_call*); + +/* Blocks + * + */ +struct ast_block_s +{ + ast_expression expression; + + ast_value* *locals; + ast_expression* *exprs; + ast_expression* *collect; +}; +ast_block* ast_block_new(lex_ctx ctx); +void ast_block_delete(ast_block*); +void ast_block_set_type(ast_block*, ast_expression *from); +void ast_block_collect(ast_block*, ast_expression*); + +bool GMQCC_WARN ast_block_add_expr(ast_block*, ast_expression*); + +/* Function + * + * Contains a list of blocks... at least in theory. + * Usually there's just the main block, other blocks are inside that. + * + * Technically, functions don't need to be an AST node, since we have + * neither functions inside functions, nor lambdas, and function + * pointers could just work with a name. However, this way could be + * more flexible, and adds no real complexity. + */ +struct ast_function_s +{ + ast_node node; + + ast_value *vtype; + const char *name; + + int builtin; + + ir_function *ir_func; + ir_block *curblock; + ir_block **breakblocks; + ir_block **continueblocks; + +#if 0 + /* In order for early-out logic not to go over + * excessive jumps, we remember their target + * blocks... + */ + ir_block *iftrue; + ir_block *iffalse; +#endif + + size_t labelcount; + /* in order for thread safety - for the optional + * channel abesed multithreading... keeping a buffer + * here to use in ast_function_label. + */ + char labelbuf[64]; + + ast_block* *blocks; + + ast_value *varargs; + ast_value *argc; + ast_value *fixedparams; + ast_value *return_value; +}; +ast_function* ast_function_new(lex_ctx ctx, const char *name, ast_value *vtype); +/* This will NOT delete the underlying ast_value */ +void ast_function_delete(ast_function*); +/* For "optimized" builds this can just keep returning "foo"... + * or whatever... + */ +/*const char* ast_function_label(ast_function*, const char *prefix);*/ + +bool ast_function_codegen(ast_function *self, ir_builder *builder); +bool ast_generate_accessors(ast_value *asvalue, ir_builder *ir); + +#endif diff --git a/misc/source/gmqcc-src/code.c b/misc/source/gmqcc-src/code.c new file mode 100644 index 00000000..c2838efa --- /dev/null +++ b/misc/source/gmqcc-src/code.c @@ -0,0 +1,387 @@ +/* + * Copyright (C) 2012, 2013 + * Dale Weiler + * Wolfgang Bumiller + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include +#include "gmqcc.h" + +/* + * We could use the old method of casting to uintptr_t then to void* + * or qcint; however, it's incredibly unsafe for two reasons. + * 1) The compilers aliasing optimization can legally make it unstable + * (it's undefined behaviour). + * + * 2) The cast itself depends on fresh storage (newly allocated in which + * ever function is using the cast macros), the contents of which are + * transferred in a way that the obligation to release storage is not + * propagated. + */ +typedef union { + void *enter; + qcint leave; +} code_hash_entry_t; + +/* Some sanity macros */ +#define CODE_HASH_ENTER(ENTRY) ((ENTRY).enter) +#define CODE_HASH_LEAVE(ENTRY) ((ENTRY).leave) + +void code_push_statement(code_t *code, prog_section_statement *stmt, int linenum) +{ + vec_push(code->statements, *stmt); + vec_push(code->linenums, linenum); +} + +void code_pop_statement(code_t *code) +{ + vec_pop(code->statements); + vec_pop(code->linenums); +} + +code_t *code_init() { + static prog_section_function empty_function = {0,0,0,0,0,0,0,{0,0,0,0,0,0,0,0}}; + static prog_section_statement empty_statement = {0,{0},{0},{0}}; + static prog_section_def empty_def = {0, 0, 0}; + + code_t *code = (code_t*)mem_a(sizeof(code_t)); + int i = 0; + + memset(code, 0, sizeof(code_t)); + code->entfields = 0; + code->string_cache = util_htnew(OPTS_OPTIMIZATION(OPTIM_OVERLAP_STRINGS) ? 0x100 : 1024); + + /* + * The way progs.dat is suppose to work is odd, there needs to be + * some null (empty) statements, functions, and 28 globals + */ + for(; i < 28; i++) + vec_push(code->globals, 0); + + vec_push(code->chars, '\0'); + vec_push(code->functions, empty_function); + + code_push_statement(code, &empty_statement, 0); + + vec_push(code->defs, empty_def); + vec_push(code->fields, empty_def); + + return code; +} + +void *code_util_str_htgeth(hash_table_t *ht, const char *key, size_t bin); + +uint32_t code_genstring(code_t *code, const char *str) { + size_t hash; + code_hash_entry_t existing; + + if (!str) + return 0; + + if (!*str) { + if (!code->string_cached_empty) { + code->string_cached_empty = vec_size(code->chars); + vec_push(code->chars, 0); + } + return code->string_cached_empty; + } + + if (OPTS_OPTIMIZATION(OPTIM_OVERLAP_STRINGS)) { + hash = ((unsigned char*)str)[strlen(str)-1]; + CODE_HASH_ENTER(existing) = code_util_str_htgeth(code->string_cache, str, hash); + } else { + hash = util_hthash(code->string_cache, str); + CODE_HASH_ENTER(existing) = util_htgeth(code->string_cache, str, hash); + } + + if (CODE_HASH_ENTER(existing)) + return CODE_HASH_LEAVE(existing); + + CODE_HASH_LEAVE(existing) = vec_size(code->chars); + vec_upload(code->chars, str, strlen(str)+1); + + util_htseth(code->string_cache, str, hash, CODE_HASH_ENTER(existing)); + return CODE_HASH_LEAVE(existing); +} + +qcint code_alloc_field (code_t *code, size_t qcsize) +{ + qcint pos = (qcint)code->entfields; + code->entfields += qcsize; + return pos; +} + +static void code_create_header(code_t *code, prog_header *code_header) { + code_header->statements.offset = sizeof(prog_header); + code_header->statements.length = vec_size(code->statements); + code_header->defs.offset = code_header->statements.offset + (sizeof(prog_section_statement) * vec_size(code->statements)); + code_header->defs.length = vec_size(code->defs); + code_header->fields.offset = code_header->defs.offset + (sizeof(prog_section_def) * vec_size(code->defs)); + code_header->fields.length = vec_size(code->fields); + code_header->functions.offset = code_header->fields.offset + (sizeof(prog_section_field) * vec_size(code->fields)); + code_header->functions.length = vec_size(code->functions); + code_header->globals.offset = code_header->functions.offset + (sizeof(prog_section_function) * vec_size(code->functions)); + code_header->globals.length = vec_size(code->globals); + code_header->strings.offset = code_header->globals.offset + (sizeof(int32_t) * vec_size(code->globals)); + code_header->strings.length = vec_size(code->chars); + code_header->version = 6; + + if (OPTS_OPTION_BOOL(OPTION_FORCECRC)) + code_header->crc16 = OPTS_OPTION_U16(OPTION_FORCED_CRC); + else + code_header->crc16 = code->crc; + code_header->entfield = code->entfields; + + if (OPTS_FLAG(DARKPLACES_STRING_TABLE_BUG)) { + util_debug("GEN", "Patching stringtable for -fdarkplaces-stringtablebug\n"); + + /* >= + P */ + vec_push(code->chars, '\0'); /* > */ + vec_push(code->chars, '\0'); /* = */ + vec_push(code->chars, '\0'); /* P */ + } + + /* ensure all data is in LE format */ + util_endianswap(&code_header->version, 1, sizeof(code_header->version)); + util_endianswap(&code_header->crc16, 1, sizeof(code_header->crc16)); + util_endianswap(&code_header->statements, 2, sizeof(code_header->statements.offset)); + util_endianswap(&code_header->defs, 2, sizeof(code_header->statements.offset)); + util_endianswap(&code_header->fields, 2, sizeof(code_header->statements.offset)); + util_endianswap(&code_header->functions, 2, sizeof(code_header->statements.offset)); + util_endianswap(&code_header->strings, 2, sizeof(code_header->statements.offset)); + util_endianswap(&code_header->globals, 2, sizeof(code_header->statements.offset)); + util_endianswap(&code_header->entfield, 1, sizeof(code_header->entfield)); + + /* + * These are not part of the header but we ensure LE format here to save on duplicated + * code. + */ + util_endianswap(code->statements, vec_size(code->statements), sizeof(prog_section_statement)); + util_endianswap(code->defs, vec_size(code->defs), sizeof(prog_section_def)); + util_endianswap(code->fields, vec_size(code->fields), sizeof(prog_section_field)); + util_endianswap(code->functions, vec_size(code->functions), sizeof(prog_section_function)); + util_endianswap(code->globals, vec_size(code->globals), sizeof(int32_t)); +} + +/* + * Same principle except this one allocates memory and writes the lno(optional) and the dat file + * directly out to allocated memory. Which is actually very useful for the future library support + * we're going to add. + */ +bool code_write_memory(code_t *code, uint8_t **datmem, size_t *sizedat, uint8_t **lnomem, size_t *sizelno) { + prog_header code_header; + uint32_t offset = 0; + + if (!datmem) + return false; + + code_create_header(code, &code_header); + + #define WRITE_CHUNK(C,X,S) \ + do { \ + memcpy((void*)(&(*C)[offset]), (const void*)(X), (S)); \ + offset += (S); \ + } while (0) + + /* Calculate size required to store entire file out to memory */ + if (lnomem) { + uint32_t version = 1; + + *sizelno += 4; /* LNOF */ + *sizelno += sizeof(version); + *sizelno += sizeof(code_header.defs.length); + *sizelno += sizeof(code_header.globals.length); + *sizelno += sizeof(code_header.fields.length); + *sizelno += sizeof(code_header.statements.length); + *sizelno += sizeof(code->linenums[0]) * vec_size(code->linenums); + + *lnomem = (uint8_t*)mem_a(*sizelno); + + WRITE_CHUNK(lnomem, "LNOF", 4); + WRITE_CHUNK(lnomem, &version, sizeof(version)); + WRITE_CHUNK(lnomem, &code_header.defs.length, sizeof(code_header.defs.length)); + WRITE_CHUNK(lnomem, &code_header.globals.length, sizeof(code_header.globals.length)); + WRITE_CHUNK(lnomem, &code_header.fields.length, sizeof(code_header.fields.length)); + WRITE_CHUNK(lnomem, &code_header.statements.length, sizeof(code_header.statements.length)); + + /* something went terribly wrong */ + if (offset != *sizelno) { + mem_d(*lnomem); + *sizelno = 0; + return false; + } + offset = 0; + } + + /* Write out the dat */ + *sizedat += sizeof(prog_header); + *sizedat += sizeof(prog_section_statement) * vec_size(code->statements); + *sizedat += sizeof(prog_section_def) * vec_size(code->defs); + *sizedat += sizeof(prog_section_field) * vec_size(code->fields); + *sizedat += sizeof(prog_section_function) * vec_size(code->functions); + *sizedat += sizeof(int32_t) * vec_size(code->globals); + *sizedat += 1 * vec_size(code->chars); + + *datmem = (uint8_t*)mem_a(*sizedat); + + WRITE_CHUNK(datmem, &code_header, sizeof(prog_header)); + WRITE_CHUNK(datmem, code->statements, sizeof(prog_section_statement) * vec_size(code->statements)); + WRITE_CHUNK(datmem, code->defs, sizeof(prog_section_def) * vec_size(code->defs)); + WRITE_CHUNK(datmem, code->fields, sizeof(prog_section_field) * vec_size(code->fields)); + WRITE_CHUNK(datmem, code->functions, sizeof(prog_section_function) * vec_size(code->functions)); + WRITE_CHUNK(datmem, code->globals, sizeof(int32_t) * vec_size(code->globals)); + WRITE_CHUNK(datmem, code->chars, 1 * vec_size(code->chars)); + + #undef WRITE_CHUNK + + vec_free(code->statements); + vec_free(code->linenums); + vec_free(code->defs); + vec_free(code->fields); + vec_free(code->functions); + vec_free(code->globals); + vec_free(code->chars); + + util_htdel(code->string_cache); + mem_d(code); + + return true; +} + +bool code_write(code_t *code, const char *filename, const char *lnofile) { + prog_header code_header; + FILE *fp = NULL; + size_t it = 2; + + code_create_header(code, &code_header); + + if (lnofile) { + uint32_t version = 1; + + fp = fs_file_open(lnofile, "wb"); + if (!fp) + return false; + + util_endianswap(&version, 1, sizeof(version)); + util_endianswap(code->linenums, vec_size(code->linenums), sizeof(code->linenums[0])); + + + if (fs_file_write("LNOF", 4, 1, fp) != 1 || + fs_file_write(&version, sizeof(version), 1, fp) != 1 || + fs_file_write(&code_header.defs.length, sizeof(code_header.defs.length), 1, fp) != 1 || + fs_file_write(&code_header.globals.length, sizeof(code_header.globals.length), 1, fp) != 1 || + fs_file_write(&code_header.fields.length, sizeof(code_header.fields.length), 1, fp) != 1 || + fs_file_write(&code_header.statements.length, sizeof(code_header.statements.length), 1, fp) != 1 || + fs_file_write(code->linenums, sizeof(code->linenums[0]), vec_size(code->linenums), fp) != vec_size(code->linenums)) + { + con_err("failed to write lno file\n"); + } + + fs_file_close(fp); + fp = NULL; + } + + fp = fs_file_open(filename, "wb"); + if (!fp) + return false; + + if (1 != fs_file_write(&code_header, sizeof(prog_header) , 1 , fp) || + vec_size(code->statements) != fs_file_write(code->statements, sizeof(prog_section_statement), vec_size(code->statements), fp) || + vec_size(code->defs) != fs_file_write(code->defs, sizeof(prog_section_def) , vec_size(code->defs) , fp) || + vec_size(code->fields) != fs_file_write(code->fields, sizeof(prog_section_field) , vec_size(code->fields) , fp) || + vec_size(code->functions) != fs_file_write(code->functions, sizeof(prog_section_function) , vec_size(code->functions) , fp) || + vec_size(code->globals) != fs_file_write(code->globals, sizeof(int32_t) , vec_size(code->globals) , fp) || + vec_size(code->chars) != fs_file_write(code->chars, 1 , vec_size(code->chars) , fp)) + { + fs_file_close(fp); + return false; + } + + util_debug("GEN","HEADER:\n"); + util_debug("GEN"," version: = %d\n", code_header.version ); + util_debug("GEN"," crc16: = %d\n", code_header.crc16 ); + util_debug("GEN"," entfield: = %d\n", code_header.entfield); + util_debug("GEN"," statements = {.offset = % 8d, .length = % 8d}\n", code_header.statements.offset, code_header.statements.length); + util_debug("GEN"," defs = {.offset = % 8d, .length = % 8d}\n", code_header.defs .offset, code_header.defs .length); + util_debug("GEN"," fields = {.offset = % 8d, .length = % 8d}\n", code_header.fields .offset, code_header.fields .length); + util_debug("GEN"," functions = {.offset = % 8d, .length = % 8d}\n", code_header.functions .offset, code_header.functions .length); + util_debug("GEN"," globals = {.offset = % 8d, .length = % 8d}\n", code_header.globals .offset, code_header.globals .length); + util_debug("GEN"," strings = {.offset = % 8d, .length = % 8d}\n", code_header.strings .offset, code_header.strings .length); + + /* FUNCTIONS */ + util_debug("GEN", "FUNCTIONS:\n"); + for (; it < vec_size(code->functions); it++) { + size_t j = code->functions[it].entry; + util_debug("GEN", " {.entry =% 5d, .firstlocal =% 5d, .locals =% 5d, .profile =% 5d, .name =% 5d, .file =% 5d, .nargs =% 5d, .argsize ={%d,%d,%d,%d,%d,%d,%d,%d} }\n", + code->functions[it].entry, + code->functions[it].firstlocal, + code->functions[it].locals, + code->functions[it].profile, + code->functions[it].name, + code->functions[it].file, + code->functions[it].nargs, + code->functions[it].argsize[0], + code->functions[it].argsize[1], + code->functions[it].argsize[2], + code->functions[it].argsize[3], + code->functions[it].argsize[4], + code->functions[it].argsize[5], + code->functions[it].argsize[6], + code->functions[it].argsize[7] + + ); + util_debug("GEN", " NAME: %s\n", &code->chars[code->functions[it].name]); + /* Internal functions have no code */ + if (code->functions[it].entry >= 0) { + util_debug("GEN", " CODE:\n"); + for (;;) { + if (code->statements[j].opcode != INSTR_DONE) + util_debug("GEN", " %-12s {% 5i,% 5i,% 5i}\n", + asm_instr[code->statements[j].opcode].m, + code->statements[j].o1.s1, + code->statements[j].o2.s1, + code->statements[j].o3.s1 + ); + else { + util_debug("GEN", " DONE {0x00000,0x00000,0x00000}\n"); + break; + } + j++; + } + } + } + + fs_file_close(fp); + return true; +} + +void code_cleanup(code_t *code) { + vec_free(code->statements); + vec_free(code->linenums); + vec_free(code->defs); + vec_free(code->fields); + vec_free(code->functions); + vec_free(code->globals); + vec_free(code->chars); + + util_htdel(code->string_cache); + + mem_d(code); +} diff --git a/misc/source/gmqcc-src/conout.c b/misc/source/gmqcc-src/conout.c new file mode 100644 index 00000000..13a242f4 --- /dev/null +++ b/misc/source/gmqcc-src/conout.c @@ -0,0 +1,447 @@ +/* + * Copyright (C) 2012, 2013 + * Dale Weiler + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "gmqcc.h" + +/* + * isatty/STDERR_FILENO/STDOUT_FILNO + * + some other things likewise. + */ +#ifndef _WIN32 +# include +#else +# include + /* + * Windows and it's posix underscore bullshit. We simply fix this + * with yay, another macro :P + */ +# define isatty _isatty +#endif + +#define GMQCC_IS_STDOUT(X) ((FILE*)((void*)X) == stdout) +#define GMQCC_IS_STDERR(X) ((FILE*)((void*)X) == stderr) +#define GMQCC_IS_DEFINE(X) (GMQCC_IS_STDERR(X) || GMQCC_IS_STDOUT(X)) + +typedef struct { + FILE *handle_err; + FILE *handle_out; + + int color_err; + int color_out; +} con_t; + +/* + * Doing colored output on windows is fucking stupid. The linux way is + * the real way. So we emulate it on windows :) + */ +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include + +/* + * Windows doesn't have constants for FILENO, sadly but the docs tell + * use the constant values. + */ +#undef STDERR_FILENO +#undef STDOUT_FILENO +#define STDERR_FILENO 2 +#define STDOUT_FILENO 1 + +enum { + RESET = 0, + BOLD = 1, + BLACK = 30, + RED, + GREEN, + YELLOW, + BLUE, + MAGENTA, + CYAN, + GRAY, + WHITE = GRAY +}; + +enum { + WBLACK, + WBLUE, + WGREEN = 2, + WRED = 4, + WINTENSE = 8, + WCYAN = WBLUE | WGREEN, + WMAGENTA = WBLUE | WRED, + WYELLOW = WGREEN | WRED, + WWHITE = WBLUE | WGREEN | WRED +}; + +static const int ansi2win[] = { + WBLACK, + WRED, + WGREEN, + WYELLOW, + WBLUE, + WMAGENTA, + WCYAN, + WWHITE +}; + +static int win_fputs(FILE *h, const char *str) { + /* state for translate */ + int acolor; + int wcolor; + int icolor; + + int state = 0; + + /* attributes */ + int intense = -1; + int colors[] = {-1, -1 }; + int colorpos = 1; + int length = 0; + CONSOLE_SCREEN_BUFFER_INFO cinfo; + GetConsoleScreenBufferInfo ( + (GMQCC_IS_STDOUT(h)) ? + GetStdHandle(STD_OUTPUT_HANDLE) : + GetStdHandle(STD_ERROR_HANDLE), &cinfo + ); + icolor = cinfo.wAttributes; + + while (*str) { + if (*str == '\x1B') + state = '\x1B'; + else if (state == '\x1B' && *str == '[') + state = '['; + else if (state == '[') { + if (*str != 'm') { + colors[colorpos] = *str; + colorpos--; + } else { + int find; + int mult; + for (find = colorpos + 1, acolor = 0, mult = 1; find < 2; find++) { + acolor += (colors[find] - 48) * mult; + mult *= 10; + } + + /* convert to windows color */ + if (acolor == BOLD) + intense = WINTENSE; + else if (acolor == RESET) { + intense = WBLACK; + wcolor = icolor; + } + else if (BLACK <= acolor && acolor <= WHITE) + wcolor = ansi2win[acolor - 30]; + else if (acolor == 90) { + /* special gray really white man */ + wcolor = WWHITE; + intense = WBLACK; + } + + SetConsoleTextAttribute ( + (GMQCC_IS_STDOUT(h)) ? + GetStdHandle(STD_OUTPUT_HANDLE) : + GetStdHandle(STD_ERROR_HANDLE), + + wcolor | intense | (icolor & 0xF0) + ); + colorpos = 1; + state = -1; + } + } else { + fs_file_write(str, 1, 1, stdout); + length ++; + } + str++; + } + /* restore */ + SetConsoleTextAttribute( + (GMQCC_IS_STDOUT(h)) ? + GetStdHandle(STD_OUTPUT_HANDLE) : + GetStdHandle(STD_ERROR_HANDLE), + icolor + ); + return length; +} +#endif + +/* + * We use standard files as default. These can be changed at any time + * with con_change(F, F) + */ +static con_t console; + +/* + * Enables color on output if supported. + * NOTE: The support for checking colors is NULL. On windows this will + * always work, on *nix it depends if the term has colors. + * + * NOTE: This prevents colored output to piped stdout/err via isatty + * checks. + */ +static void con_enablecolor(void) { + if (console.handle_err == stderr || console.handle_err == stdout) + console.color_err = !!(isatty(STDERR_FILENO)); + if (console.handle_out == stderr || console.handle_out == stdout) + console.color_out = !!(isatty(STDOUT_FILENO)); +} + +/* + * Does a write to the handle with the format string and list of + * arguments. This colorizes for windows as well via translate + * step. + */ +static int con_write(FILE *handle, const char *fmt, va_list va) { + int ln; + #ifndef _WIN32 + ln = vfprintf(handle, fmt, va); + #else + { + char data[4096]; + memset(data, 0, sizeof(data)); +#ifdef _MSC_VER + vsnprintf_s(data, sizeof(data), sizeof(data), fmt, va); +#else + vsnprintf(data, sizeof(data), fmt, va); +#endif + ln = (GMQCC_IS_DEFINE(handle)) ? win_fputs(handle, data) : fs_file_puts(handle, data); + } + #endif + return ln; +} + +/********************************************************************** + * EXPOSED INTERFACE BEGINS + *********************************************************************/ + +void con_close() { + if (!GMQCC_IS_DEFINE(console.handle_err)) + fs_file_close(console.handle_err); + if (!GMQCC_IS_DEFINE(console.handle_out)) + fs_file_close(console.handle_out); +} + +void con_color(int state) { + if (state) + con_enablecolor(); + else { + console.color_err = 0; + console.color_out = 0; + } +} + +void con_init() { + console.handle_err = stderr; + console.handle_out = stdout; + con_enablecolor(); +} + +void con_reset() { + con_close(); + con_init (); +} + +/* + * This is clever, say you want to change the console to use two + * files for out/err. You pass in two strings, it will properly + * close the existing handles (if they're not std* handles) and + * open them. Now say you want TO use stdout and stderr, this + * allows you to do that so long as you cast them to (char*). + * Say you need stdout for out, but want a file for error, you can + * do this too, just cast the stdout for (char*) and stick to a + * string for the error file. + */ +int con_change(const char *out, const char *err) { + con_close(); + + if (!out) out = (const char *)((!console.handle_out) ? stdout : console.handle_out); + if (!err) err = (const char *)((!console.handle_err) ? stderr : console.handle_err); + + if (GMQCC_IS_DEFINE(out)) { + console.handle_out = GMQCC_IS_STDOUT(out) ? stdout : stderr; + con_enablecolor(); + } else if (!(console.handle_out = fs_file_open(out, "w"))) return 0; + + if (GMQCC_IS_DEFINE(err)) { + console.handle_err = GMQCC_IS_STDOUT(err) ? stdout : stderr; + con_enablecolor(); + } else if (!(console.handle_err = fs_file_open(err, "w"))) return 0; + + return 1; +} + +/* + * Defaultizer because stdio.h shouldn't be used anywhere except here + * and inside file.c To prevent mis-match of wrapper-interfaces. + */ +FILE *con_default_out() { + return (console.handle_out = stdout); +} +FILE *con_default_err() { + return (console.handle_err = stderr); +} + +int con_verr(const char *fmt, va_list va) { + return con_write(console.handle_err, fmt, va); +} +int con_vout(const char *fmt, va_list va) { + return con_write(console.handle_out, fmt, va); +} + +/* + * Standard stdout/stderr printf functions used generally where they need + * to be used. + */ +int con_err(const char *fmt, ...) { + va_list va; + int ln = 0; + va_start(va, fmt); + con_verr(fmt, va); + va_end (va); + return ln; +} +int con_out(const char *fmt, ...) { + va_list va; + int ln = 0; + va_start(va, fmt); + con_vout(fmt, va); + va_end (va); + return ln; +} + +/* + * Utility console message writes for lexer contexts. These will allow + * for reporting of file:line based on lexer context, These are used + * heavily in the parser/ir/ast. + */ +static void con_vprintmsg_c(int level, const char *name, size_t line, size_t column, const char *msgtype, const char *msg, va_list ap, const char *condname) { + /* color selection table */ + static int sel[] = { + CON_WHITE, + CON_CYAN, + CON_RED + }; + + int err = !!(level == LVL_ERROR); + int color = (err) ? console.color_err : console.color_out; + int (*print) (const char *, ...) = (err) ? &con_err : &con_out; + int (*vprint)(const char *, va_list) = (err) ? &con_verr : &con_vout; + + if (color) + print("\033[0;%dm%s:%d:%d: \033[0;%dm%s: \033[0m", CON_CYAN, name, (int)line, (int)column, sel[level], msgtype); + else + print("%s:%d:%d: %s: ", name, (int)line, (int)column, msgtype); + + vprint(msg, ap); + if (condname) + print(" [%s]\n", condname); + else + print("\n"); +} + +void con_vprintmsg(int level, const char *name, size_t line, size_t column, const char *msgtype, const char *msg, va_list ap) { + con_vprintmsg_c(level, name, line, column, msgtype, msg, ap, NULL); +} + +void con_printmsg(int level, const char *name, size_t line, size_t column, const char *msgtype, const char *msg, ...) { + va_list va; + va_start(va, msg); + con_vprintmsg(level, name, line, column, msgtype, msg, va); + va_end (va); +} + +void con_cvprintmsg(void *ctx, int lvl, const char *msgtype, const char *msg, va_list ap) { + con_vprintmsg(lvl, ((lex_ctx*)ctx)->file, ((lex_ctx*)ctx)->line, ((lex_ctx*)ctx)->column, msgtype, msg, ap); +} + +void con_cprintmsg (void *ctx, int lvl, const char *msgtype, const char *msg, ...) { + va_list va; + va_start(va, msg); + con_cvprintmsg(ctx, lvl, msgtype, msg, va); + va_end (va); +} + +/* General error interface */ +size_t compile_errors = 0; +size_t compile_warnings = 0; + +size_t compile_Werrors = 0; +static lex_ctx first_werror; + +void compile_show_werrors() +{ + con_cprintmsg((void*)&first_werror, LVL_ERROR, "first warning", "was here"); +} + +void vcompile_error(lex_ctx ctx, const char *msg, va_list ap) +{ + ++compile_errors; + con_cvprintmsg((void*)&ctx, LVL_ERROR, "error", msg, ap); +} + +void compile_error(lex_ctx ctx, const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); + vcompile_error(ctx, msg, ap); + va_end(ap); +} + +bool GMQCC_WARN vcompile_warning(lex_ctx ctx, int warntype, const char *fmt, va_list ap) +{ + const char *msgtype = "warning"; + int lvl = LVL_WARNING; + char warn_name[1024]; + + if (!OPTS_WARN(warntype)) + return false; + + warn_name[0] = '-'; + warn_name[1] = 'W'; + (void)util_strtononcmd(opts_warn_list[warntype].name, warn_name+2, sizeof(warn_name)-2); + + ++compile_warnings; + if (OPTS_WERROR(warntype)) { + if (!compile_Werrors) + first_werror = ctx; + ++compile_Werrors; + msgtype = "Werror"; + if (OPTS_FLAG(BAIL_ON_WERROR)) { + msgtype = "error"; + ++compile_errors; + } + lvl = LVL_ERROR; + } + + con_vprintmsg_c(lvl, ctx.file, ctx.line, ctx.column, msgtype, fmt, ap, warn_name); + + return OPTS_WERROR(warntype) && OPTS_FLAG(BAIL_ON_WERROR); +} + +bool GMQCC_WARN compile_warning(lex_ctx ctx, int warntype, const char *fmt, ...) +{ + bool r; + va_list ap; + va_start(ap, fmt); + r = vcompile_warning(ctx, warntype, fmt, ap); + va_end(ap); + return r; +} diff --git a/misc/source/gmqcc-src/correct.c b/misc/source/gmqcc-src/correct.c new file mode 100644 index 00000000..f501817a --- /dev/null +++ b/misc/source/gmqcc-src/correct.c @@ -0,0 +1,548 @@ +/* + * Copyright (C) 2012, 2013 + * Dale Weiler + * Wolfgang Bumiller + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include +#include "gmqcc.h" + +/* + * This is a very clever method for correcting mistakes in QuakeC code + * most notably when invalid identifiers are used or inproper assignments; + * we can proprly lookup in multiple dictonaries (depening on the rules + * of what the task is trying to acomplish) to find the best possible + * match. + * + * + * A little about how it works, and probability theory: + * + * When given an identifier (which we will denote I), we're essentially + * just trying to choose the most likely correction for that identifier. + * (the actual "correction" can very well be the identifier itself). + * There is actually no way to know for sure that certian identifers + * such as "lates", need to be corrected to "late" or "latest" or any + * other permutations that look lexically the same. This is why we + * must advocate the usage of probabilities. This means that instead of + * just guessing, instead we're trying to find the correction for C, + * out of all possible corrections that maximizes the probability of C + * for the original identifer I. + * + * Thankfully there exists some theroies for probalistic interpretations + * of data. Since we're operating on two distictive intepretations, the + * transposition from I to C. We need something that can express how much + * degree of I should rationally change to become C. this is called the + * Bayesian interpretation. You can read more about it from here: + * http://www.celiagreen.com/charlesmccreery/statistics/bayestutorial.pdf + * (which is probably the only good online documentation for bayes theroy + * no lie. Everything else just sucks ..) + * + * Bayes' Thereom suggests something like the following: + * AC P(I|C) P(C) / P(I) + * + * However since P(I) is the same for every possibility of I, we can + * completley ignore it giving just: + * AC P(I|C) P(C) + * + * This greatly helps visualize how the parts of the expression are performed + * there is essentially three, from right to left we perform the following: + * + * 1: P(C), the probability that a proposed correction C will stand on its + * own. This is called the language model. + * + * 2: P(I|C), the probability that I would be used, when the programmer + * really meant C. This is the error model. + * + * 3: AC, the control mechanisim, an enumerator if you will, one that + * enumerates all feasible values of C, to determine the one that + * gives the greatest probability score. + * + * In reality the requirement for a more complex expression involving + * two seperate models is considerably a waste. But one must recognize + * that P(C|I) is already conflating two factors. It's just much simpler + * to seperate the two models and deal with them explicitaly. To properly + * estimate P(C|I) you have to consider both the probability of C and + * probability of the transposition from C to I. It's simply much more + * cleaner, and direct to seperate the two factors. + * + * Research tells us that 80% to 95% of all spelling errors have an edit + * distance no greater than one. Knowing this we can optimize for most + * cases of mistakes without taking a performance hit. Which is what we + * base longer edit distances off of. Opposed to the original method of + * I had concieved of checking everything. + * + * A little information on additional algorithms used: + * + * Initially when I implemented this corrector, it was very slow. + * Need I remind you this is essentially a brute force attack on strings, + * and since every transformation requires dynamic memory allocations, + * you can easily imagine where most of the runtime conflated. Yes + * It went right to malloc. More than THREE MILLION malloc calls are + * performed for an identifier about 16 bytes long. This was such a + * shock to me. A forward allocator (or as some call it a bump-point + * allocator, or just a memory pool) was implemented. To combat this. + * + * But of course even other factors were making it slow. Initially + * this used a hashtable. And hashtables have a good constant lookup + * time complexity. But the problem wasn't in the hashtable, it was + * in the hashing (despite having one of the fastest hash functions + * known). Remember those 3 million mallocs? Well for every malloc + * there is also a hash. After 3 million hashes .. you start to get + * very slow. To combat this I had suggested burst tries to Blub. + * The next day he had implemented them. Sure enough this brought + * down the runtime by a factor > 100% + * + * The trie initially was designed to work on all strings, but later it + * became aparent that not only was this not a requirement. It was also + * slowing down get/sets' for the trie. To fully understand, only + * correct_alpha needs to be understood by the trie system, knowing this + * We can combat the slowness using a very clever but evil optimization. + * By Setting a fixed sized amount of branches for the trie using a + * char-to-index map into the branches. We've complelty made the trie + * accesses entierly constant in lookup time. No really, a lookup is + * literally trie[str[0]] [str[1]] [2] .... .value. + * + * + * Future Work (If we really need it) + * + * Currently we can only distinguish one source of error in the + * language model we use. This could become an issue for identifiers + * that have close colliding rates, e.g colate->coat yields collate. + * + * Currently the error model has been fairly trivial, the smaller the + * edit distance the smaller the error. This usually causes some un- + * expected problems. e.g reciet->recite yields recipt. For QuakeC + * this could become a problem when lots of identifiers are involved. + */ + + +#define CORRECT_POOL_SIZE (128*1024*1024) +/* + * A forward allcator for the corrector. This corrector requires a lot + * of allocations. This forward allocator combats all those allocations + * and speeds us up a little. It also saves us space in a way since each + * allocation isn't wasting a little header space for when NOTRACK isn't + * defined. + */ +static unsigned char **correct_pool_data = NULL; +static unsigned char *correct_pool_this = NULL; +static size_t correct_pool_addr = 0; + +static GMQCC_INLINE void correct_pool_new(void) { + correct_pool_addr = 0; + correct_pool_this = (unsigned char *)mem_a(CORRECT_POOL_SIZE); + + vec_push(correct_pool_data, correct_pool_this); +} + +static GMQCC_INLINE void *correct_pool_alloc(size_t bytes) { + void *data; + if (correct_pool_addr + bytes>= CORRECT_POOL_SIZE) + correct_pool_new(); + + data = (void*)correct_pool_this; + correct_pool_this += bytes; + correct_pool_addr += bytes; + return data; +} + +static GMQCC_INLINE void correct_pool_delete(void) { + size_t i; + for (i = 0; i < vec_size(correct_pool_data); ++i) + mem_d(correct_pool_data[i]); + + correct_pool_data = NULL; + correct_pool_this = NULL; + correct_pool_addr = 0; +} + + +static GMQCC_INLINE char *correct_pool_claim(const char *data) { + char *claim = util_strdup(data); + return claim; +} + +/* + * _ is valid in identifiers. I've yet to implement numerics however + * because they're only valid after the first character is of a _, or + * alpha character. + */ +static const char correct_alpha[] = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "_"; /* TODO: Numbers ... */ + +static const size_t correct_alpha_index[0x80] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 52, + 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0 +}; + +/* + * A fast space efficent trie for a dictionary of identifiers. This is + * faster than a hashtable for one reason. A hashtable itself may have + * fast constant lookup time, but the hash itself must be very fast. We + * have one of the fastest hash functions for strings, but if you do a + * lost of hashing (which we do, almost 3 million hashes per identifier) + * a hashtable becomes slow. + */ +correct_trie_t* correct_trie_new() { + correct_trie_t *t = (correct_trie_t*)mem_a(sizeof(correct_trie_t)); + t->value = NULL; + t->entries = NULL; + return t; +} + +static GMQCC_INLINE void correct_trie_del_sub(correct_trie_t *t) { + size_t i; + if (!t->entries) + return; + for (i = 0; i < sizeof(correct_alpha)-1; ++i) { + correct_trie_del_sub(&t->entries[i]); + } + mem_d(t->entries); +} + +static GMQCC_INLINE void correct_trie_del(correct_trie_t *t) { + size_t i; + if (t->entries) { + for (i = 0; i < sizeof(correct_alpha)-1; ++i) + correct_trie_del_sub(&t->entries[i]); + mem_d(t->entries); + } + mem_d(t); +} + +static GMQCC_INLINE void* correct_trie_get(const correct_trie_t *t, const char *key) { + const unsigned char *data = (const unsigned char*)key; + + while (*data) { + if (!t->entries) + return NULL; + t = t->entries + correct_alpha_index[*data]; + ++data; + } + return t->value; +} + +static GMQCC_INLINE void correct_trie_set(correct_trie_t *t, const char *key, void * const value) { + const unsigned char *data = (const unsigned char*)key; + while (*data) { + if (!t->entries) { + t->entries = (correct_trie_t*)mem_a(sizeof(correct_trie_t)*(sizeof(correct_alpha)-1)); + memset(t->entries, 0, sizeof(correct_trie_t)*(sizeof(correct_alpha)-1)); + } + t = t->entries + correct_alpha_index[*data]; + ++data; + } + t->value = value; +} + + +/* + * Implementation of the corrector algorithm commences. A very efficent + * brute-force attack (thanks to tries and mempool :-)). + */ +static GMQCC_INLINE size_t *correct_find(correct_trie_t *table, const char *word) { + return (size_t*)correct_trie_get(table, word); +} + +static GMQCC_INLINE bool correct_update(correct_trie_t* *table, const char *word) { + size_t *data = correct_find(*table, word); + if (!data) + return false; + + (*data)++; + return true; +} + +void correct_add(correct_trie_t* table, size_t ***size, const char *ident) { + size_t *data = NULL; + const char *add = ident; + + if (!correct_update(&table, add)) { + data = (size_t*)mem_a(sizeof(size_t)); + *data = 1; + + vec_push((*size), data); + correct_trie_set(table, add, data); + } +} + +void correct_del(correct_trie_t* dictonary, size_t **data) { + size_t i; + const size_t vs = vec_size(data); + + for (i = 0; i < vs; i++) + mem_d(data[i]); + + vec_free(data); + correct_trie_del(dictonary); +} + +/* + * correcting logic for the following forms of transformations: + * 1) deletion + * 2) transposition + * 3) alteration + * 4) insertion + * + * These functions could take an additional size_t **size paramater + * and store back the results of their new length in an array that + * is the same as **array for the memcmp in correct_exists. I'm just + * not able to figure out how to do that just yet. As my brain is + * not in the mood to figure out that logic. This is a reminder to + * do it, or for someone else to :-) correct_edit however would also + * need to take a size_t ** to carry it along (would all the argument + * overhead be worth it?) + */ +static GMQCC_INLINE size_t correct_deletion(const char *ident, char **array) { + size_t itr = 0; + const size_t len = strlen(ident); + + for (; itr < len; itr++) { + char *a = (char*)correct_pool_alloc(len+1); + memcpy(a, ident, itr); + memcpy(a + itr, ident + itr + 1, len - itr); + array[itr] = a; + } + + return itr; +} + +static GMQCC_INLINE size_t correct_transposition(const char *ident, char **array) { + size_t itr = 0; + const size_t len = strlen(ident); + + for (; itr < len - 1; itr++) { + char tmp; + char *a = (char*)correct_pool_alloc(len+1); + memcpy(a, ident, len+1); + tmp = a[itr]; + a[itr ] = a[itr+1]; + a[itr+1] = tmp; + array[itr] = a; + } + + return itr; +} + +static GMQCC_INLINE size_t correct_alteration(const char *ident, char **array) { + size_t itr = 0; + size_t jtr = 0; + size_t ktr = 0; + const size_t len = strlen(ident); + + for (; itr < len; itr++) { + for (jtr = 0; jtr < sizeof(correct_alpha)-1; jtr++, ktr++) { + char *a = (char*)correct_pool_alloc(len+1); + memcpy(a, ident, len+1); + a[itr] = correct_alpha[jtr]; + array[ktr] = a; + } + } + + return ktr; +} + +static GMQCC_INLINE size_t correct_insertion(const char *ident, char **array) { + size_t itr = 0; + size_t jtr = 0; + const size_t len = strlen(ident); + + for (; itr <= len; itr++) { + for (jtr = 0; jtr < sizeof(correct_alpha)-1; jtr++) { + char *a = (char*)correct_pool_alloc(len+2); + memcpy(a, ident, itr); + memcpy(a + itr + 1, ident + itr, len - itr + 1); + a[itr] = correct_alpha[jtr]; + array[itr * (sizeof(correct_alpha)-1) + jtr] = a; + } + } + + return (len+1)*(sizeof(correct_alpha)-1); +} + +static GMQCC_INLINE size_t correct_size(const char *ident) { + /* + * deletion = len + * transposition = len - 1 + * alteration = len * sizeof(correct_alpha) + * insertion = (len + 1) * sizeof(correct_alpha) + */ + + register size_t len = strlen(ident); + return (len) + (len - 1) + (len * (sizeof(correct_alpha)-1)) + ((len + 1) * (sizeof(correct_alpha)-1)); +} + +static GMQCC_INLINE char **correct_edit(const char *ident, size_t **lens) { + size_t next; + size_t size = correct_size(ident); + char **find = (char**)correct_pool_alloc(size * sizeof(char*)); + + if (!find || !(*lens = (size_t*)correct_pool_alloc(size * sizeof(size_t)))) + return NULL; + + next = correct_deletion (ident, find); + next += correct_transposition(ident, find+next); + next += correct_alteration (ident, find+next); + /*****/ correct_insertion (ident, find+next); + + /* precompute lengths */ + for (next = 0; next < size; next++) + (*lens)[next] = strlen(find[next]); + + return find; +} + +static GMQCC_INLINE int correct_exist(char **array, register size_t *sizes, size_t rows, char *ident, register size_t len) { + size_t itr; + for (itr = 0; itr < rows; itr++) { + /* + * We can save tons of calls to memcmp if we simply ignore comparisions + * that we know cannot contain the same length. + */ + if (sizes[itr] == len && !memcmp(array[itr], ident, len)) + return 1; + } + + return 0; +} + +static GMQCC_INLINE char **correct_known_resize(char **res, size_t *allocated, size_t size) { + size_t oldallocated = *allocated; + char **out; + if (size < oldallocated) + return res; + + out = (char**)correct_pool_alloc(sizeof(*res) * oldallocated + 32); + memcpy(out, res, sizeof(*res) * oldallocated); + + *allocated += 32; + return out; +} + +static char **correct_known(correction_t *corr, correct_trie_t* table, char **array, size_t rows, size_t *next) { + size_t itr = 0; + size_t jtr = 0; + size_t len = 0; + size_t row = 0; + size_t nxt = 8; + char **res = (char**)correct_pool_alloc(sizeof(char *) * nxt); + char **end = NULL; + size_t *bit = NULL; + + for (; itr < rows; itr++) { + if (!array[itr][0]) + continue; + if (vec_size(corr->edits) > itr+1) { + end = corr->edits[itr+1]; + bit = corr->lens [itr+1]; + } else { + end = correct_edit(array[itr], &bit); + vec_push(corr->edits, end); + vec_push(corr->lens, bit); + } + row = correct_size(array[itr]); + + for (jtr = 0; jtr < row; jtr++) { + if (correct_find(table, end[jtr]) && !correct_exist(res, bit, len, end[jtr], bit[jtr])) { + res = correct_known_resize(res, &nxt, len+1); + res[len++] = end[jtr]; + } + } + } + + *next = len; + return res; +} + +static GMQCC_INLINE char *correct_maximum(correct_trie_t* table, char **array, size_t rows) { + char *str = NULL; + size_t *itm = NULL; + size_t itr = 0; + size_t top = 0; + + for (; itr < rows; itr++) { + if ((itm = correct_find(table, array[itr])) && (*itm > top)) { + top = *itm; + str = array[itr]; + } + } + + return str; +} + +/* + * This is the exposed interface: + * takes a table for the dictonary a vector of sizes (used for internal + * probability calculation), and an identifier to "correct". + */ +void correct_init(correction_t *c) +{ + correct_pool_new(); + c->edits = NULL; + c->lens = NULL; +} + +void correct_free(correction_t *c) +{ + vec_free(c->edits); + vec_free(c->lens); + correct_pool_delete(); +} + +char *correct_str(correction_t *corr, correct_trie_t* table, const char *ident) { + char **e1 = NULL; + char **e2 = NULL; + char *e1ident = NULL; + char *e2ident = NULL; + size_t e1rows = 0; + size_t e2rows = 0; + size_t *bits = NULL; + + /* needs to be allocated for free later */ + if (correct_find(table, ident)) + return correct_pool_claim(ident); + + if ((e1rows = correct_size(ident))) { + if (vec_size(corr->edits) > 0) + e1 = corr->edits[0]; + else { + e1 = correct_edit(ident, &bits); + vec_push(corr->edits, e1); + vec_push(corr->lens, bits); + } + + if ((e1ident = correct_maximum(table, e1, e1rows))) + return correct_pool_claim(e1ident); + } + + e2 = correct_known(corr, table, e1, e1rows, &e2rows); + if (e2rows && ((e2ident = correct_maximum(table, e2, e2rows)))) + return correct_pool_claim(e2ident); + + + return util_strdup(ident); +} diff --git a/misc/source/gmqcc-src/distro/Makefile b/misc/source/gmqcc-src/distro/Makefile new file mode 100644 index 00000000..96ddce29 --- /dev/null +++ b/misc/source/gmqcc-src/distro/Makefile @@ -0,0 +1,53 @@ +DROPBOX := dropbox_uploader.sh +UNAME := $(shell uname -m) +DOWNLOAD:= ../doc/html/download.c +BRANCH := $(shell git branch | sed -n -e 's/^\* \(.*\)/\1/p') +ifneq ($(shell uname -m), x86_64) + $(error Cannot build packages without an x86_64 capable CPU) +endif + +.NOTPARALLEL: base +.NOTPARALLEL: upload + +base: + $(MAKE) -C deb/ + $(MAKE) -C deb/ CARCH=i686 + $(MAKE) -C archlinux/this/ + $(MAKE) -C archlinux/this/ CARCH=i686 + $(MAKE) -C win32/ + @mv deb/*.deb ./ + @mv archlinux/this/*pkg.tar.xz ./ + @mv win32/*.zip ./ + +upload: + @echo "APPKEY:76vh3q42hnvmzm3" > dropbox_config + @echo "APPSECRET:tmeecht2cmh72xa" >> dropbox_config + @echo "ACCESS_LEVEL:sandbox" >> dropbox_config + @echo "OAUTH_ACCESS_TOKEN:w0bxzf0dft8edfq" >> dropbox_config + @echo "OAUTH_ACCESS_TOKEN_SECRET:9vosx7x8gy4kgjk" >> dropbox_config + @wget -q "http://raw.github.com/andreafabrizi/Dropbox-Uploader/master/dropbox_uploader.sh" + @chmod +x dropbox_uploader.sh + @sed -i -e "s/~\/.dropbox_uploader/.\/dropbox_config/g" $$(basename $(DROPBOX)) + @find . -type f -regex ".*/.*\.\(xz\|deb\|zip\)" -exec ./$$(basename $(DROPBOX)) upload {} \; + @rm dropbox_config dropbox_uploader.sh + +website: + $(CC) $(DOWNLOAD) -o html.gen + @./html.gen ../ + @rm html.gen + @git stash + @git checkout gh-pages + @rm -f ../download.html + @mv -f download.html ../download.html + @cd ..; git add download.html; git commit -m 'update download page'; git push origin gh-pages; + @git checkout $(BRANCH) + @git stash apply + +clean: + @rm -f *.deb + @rm -f *.pkg.tar.xz + @rm -f *.zip + @rm -f *.gen + @rm -f *.html + +all: base upload diff --git a/misc/source/gmqcc-src/distro/deb/Makefile b/misc/source/gmqcc-src/distro/deb/Makefile new file mode 100644 index 00000000..75944003 --- /dev/null +++ b/misc/source/gmqcc-src/distro/deb/Makefile @@ -0,0 +1,45 @@ +BASEDIR := ../.. +PREFIX := /usr +HEADER := $(BASEDIR)/gmqcc.h +MAJOR := `sed -n -e '/GMQCC_VERSION_MAJOR/{s/.* .* //;p;q;}' $(HEADER)` +MINOR := `sed -n -e '/GMQCC_VERSION_MINOR/{s/.* .* //;p;q;}' $(HEADER)` +PATCH := `sed -n -e '/GMQCC_VERSION_PATCH/{s/.* .* //;p;q;}' $(HEADER)` +DEBDIR := gmqcc-$(MAJOR).$(MINOR).$(PATCH) +CARCH := $(shell uname -m) +DEB := $(DEBDIR)-$(CARCH).deb +CONTROL := $(DEBDIR)/DEBIAN/control + +ifneq (, $(findstring i686, $(CARCH))) + CFLAGS += -m32 + LDFLAGS += -m32 +endif + +base: + $(MAKE) -C $(BASEDIR) clean + CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" \ + $(MAKE) -C $(BASEDIR) DESTDIR=distro/deb/$(DEBDIR) PREFIX=$(PREFIX) install + @install -d -m755 $(DEBDIR)/DEBIAN + @echo "Package: gmqcc" > $(CONTROL) + @echo "Version: $(MAJOR).$(MINOR).$(PATCH)" >> $(CONTROL) + @echo "Section: user/hidden" >> $(CONTROL) + @echo "Priority: optional" >> $(CONTROL) + @echo "Architecture: $(CARCH)" >> $(CONTROL) + @echo "Installed-Size: `du -ks $($(DEBDIR)/usr) | cut -f 1`" >> $(CONTROL) + @echo "Maintainer: Dale Weiler " >> $(CONTROL) + @echo "Description: An improved Quake C Compiler" >> $(CONTROL) + @echo " For an enduring period of time the options for a decent compiler for the Quake C programming language" >> $(CONTROL) + @echo " were confined to a specific compiler known as QCC. Attempts were made to extend and improve upon the" >> $(CONTROL) + @echo " design of QCC, but many foreseen the consequences of building on a broken foundation. The solution" >> $(CONTROL) + @echo " was obvious, a new compiler; one born from the NIH realm of sarcastic wit. We welcome you. You won't" >> $(CONTROL) + @echo " find a better Quake C compiler." >> $(CONTROL) + @tar czf data.tar.gz -C $(DEBDIR)/ . --exclude=DEBIAN + @tar czf control.tar.gz -C $(DEBDIR)/DEBIAN/ . + @echo 2.0 > debian-binary + @ar r $(DEB) debian-binary control.tar.gz data.tar.gz + @rm -rf debian-binary control.tar.gz data.tar.gz $(DEBDIR) + +clean: + $(MAKE) -C $(BASEDIR) clean + @rm -f *.deb + +all: base diff --git a/misc/source/gmqcc-src/distro/gentoo/INSTALL b/misc/source/gmqcc-src/distro/gentoo/INSTALL new file mode 100644 index 00000000..7a4f7784 --- /dev/null +++ b/misc/source/gmqcc-src/distro/gentoo/INSTALL @@ -0,0 +1,35 @@ +To use the ebuilds provided in this gentoo directory you first must +create a directory in your overlay tree. + +If you don't already have your own directory for custom ebuilds, you can +create one. If you already have one, and that directory is set in your +/etc/make.conf for PORTDIR_OVERLAY, this step can be skiped. Otherwise +if you don't already, you can create one as such. + +# mkdir -p /usr/local/portage +# vim /etc/make.conf + Set PORTDIR_OVERLAY=/usr/local/portage + Then save and exit + +Once that is completed, or you skiped that step, you need to create a +directory in your overlay tree for gmqcc, this can be done as such: +(subsitute [[PORTDIR_OVERLAY]] with the one set in /etc/make.conf) + +# mkdir -p [[PORTDIR_OVERLAY]]/gmqcc + +After the directory is created you need to move the correct version ebuild +into that directory depending on which version of GMQCC you want. For +instance, if you want gmqcc 0.3.0, you move gmqcc-0.3.0.ebuild into that +directory. + +# mv gmqcc-{version}.ebuild [[PORTDIR_OVERLAY]]/gmqcc/ + +After the file is moved into your newly created portage overlay tree, you'll +need to build a digest for it with ebuild. A digest is simply a Manifest and +digital signature for the source files used. + +# ebuild gmqcc-0.3.0.ebuild digest + +After the digest is built, you can emerge gmqcc as usual. + +# emerge gmqcc diff --git a/misc/source/gmqcc-src/distro/gentoo/gmqcc-0.3.0.ebuild b/misc/source/gmqcc-src/distro/gentoo/gmqcc-0.3.0.ebuild new file mode 100644 index 00000000..28630d3d --- /dev/null +++ b/misc/source/gmqcc-src/distro/gentoo/gmqcc-0.3.0.ebuild @@ -0,0 +1,20 @@ +EAPI=5 + +DESCRIPTION="An Improved Quake C Compiler" +HOMEPAGE="http://graphitemaster.github.com/gmqcc/" +SRC_URI="https://github.com/graphitemaster/${PN}/archive/${PV}.tar.gz -> ${P}.tar.gz" + +LICENSE="MIT" + +SLOT="0" +IUSE="" +KEYWORDS="~amd64 ~x86" + +src_prepare() { + sed -i -e "s:-Werror ::" Makefile || die +} + +src_install() { + emake install PREFIX="${D}/usr" + dodoc README +} diff --git a/misc/source/gmqcc-src/distro/slackware/this/Makefile b/misc/source/gmqcc-src/distro/slackware/this/Makefile new file mode 100644 index 00000000..3f70b8bb --- /dev/null +++ b/misc/source/gmqcc-src/distro/slackware/this/Makefile @@ -0,0 +1,35 @@ +BASEDIR := ../../../ +PREFIX := /usr +HEADER := $(BASEDIR)/gmqcc.h +MAJOR := $(shell sed -n -e '/GMQCC_VERSION_MAJOR/{s/.* .* //;p;q;}' $(HEADER)) +MINOR := $(shell sed -n -e '/GMQCC_VERSION_MINOR/{s/.* .* //;p;q;}' $(HEADER)) +PATCH := $(shell sed -n -e '/GMQCC_VERSION_PATCH/{s/.* .* //;p;q;}' $(HEADER)) +CARCH := $(shell uname -m) +PKGDIR := gmqcc-$(MAJOR).$(MINOR).$(PATCH)-$(CARCH)-git +PKG := $(PKGDIR).txz +PKGINFO := $(PKGDIR)/.PKGINFO +DESTDIR := distro/slackware/this/$(PKGDIR) +CFLAGS := + + +ifneq (, $(findstring i686, $(CARCH))) + CFLAGS += -m32 + LDFLAGS += -m32 +endif + +base: + $(MAKE) -C $(BASEDIR) clean + CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" \ + $(MAKE) -C $(BASEDIR) "DESTDIR=$(DESTDIR)" "PREFIX=$(PREFIX)" install + gzip -9 $(PKGDIR)/usr/share/man/man?/*.? + strip -s $(PKGDIR)/usr/bin/* + mkdir $(PKGDIR)/install + cp slack-desc $(PKGDIR)/install + @tar -cJvf $(PKG) -C $(PKGDIR)/ install/ usr/ + @rm -rf $(PKGDIR) + +clean: + $(MAKE) -C $(BASEDIR) clean + @rm -f *.txz + +all: base diff --git a/misc/source/gmqcc-src/distro/slackware/this/slack-desc b/misc/source/gmqcc-src/distro/slackware/this/slack-desc new file mode 100644 index 00000000..3384ef97 --- /dev/null +++ b/misc/source/gmqcc-src/distro/slackware/this/slack-desc @@ -0,0 +1,12 @@ + |-----handy-ruler------------------------------------------------------| +gmqcc: gmqcc (Quake C compiler) +gmqcc: +gmqcc: A modern written-from-scratch compiler for the QuakeC language with +gmqcc: support for many common features found in other QC compilers. +gmqcc: Additionally contains a standalone QCVM executor, and a tool to deal +gmqcc: with .pak archive files. +gmqcc: +gmqcc: +gmqcc: github page: +gmqcc: http://github.com/graphitemaster/gmqcc +gmqcc: diff --git a/misc/source/gmqcc-src/distro/win32/Makefile b/misc/source/gmqcc-src/distro/win32/Makefile new file mode 100644 index 00000000..3a4ed248 --- /dev/null +++ b/misc/source/gmqcc-src/distro/win32/Makefile @@ -0,0 +1,17 @@ +BASEDIR := ../.. +HEADER := $(BASEDIR)/gmqcc.h +MAJOR := `sed -n -e '/GMQCC_VERSION_MAJOR/{s/.* .* //;p;q;}' $(HEADER)` +MINOR := `sed -n -e '/GMQCC_VERSION_MINOR/{s/.* .* //;p;q;}' $(HEADER)` +PATCH := `sed -n -e '/GMQCC_VERSION_PATCH/{s/.* .* //;p;q;}' $(HEADER)` +BINDIR := gmqcc-$(MAJOR).$(MINOR).$(PATCH) + +base: + $(MAKE) CC=i486-mingw32-gcc UNAME=MINGW32 -C $(BASEDIR) clean + $(MAKE) CC=i486-mingw32-gcc UNAME=MINGW32 -C $(BASEDIR) DESTDIR=distro/win32/$(BINDIR) PREFIX=/ install + @zip -r $(BINDIR)-win32.zip $(BINDIR) + @rm -rf $(BINDIR) +clean: + $(MAKE) -C $(BASEDIR) clean + @rm -f *.zip + +all: base diff --git a/misc/source/gmqcc-src/doc/gmqcc.1 b/misc/source/gmqcc-src/doc/gmqcc.1 new file mode 100644 index 00000000..6ede944f --- /dev/null +++ b/misc/source/gmqcc-src/doc/gmqcc.1 @@ -0,0 +1,650 @@ +.\"mdoc +.Dd January 24, 2013 +.Dt GMQCC 1 PRM +.Os +.Sh NAME +.Nm gmqcc +.Nd A Quake C compiler built from the NIH realm of sarcastic wit +.Sh SYNOPSIS +.Nm gmqcc +.Op Cm options +.Op Ar files... +.Sh DESCRIPTION +Traditionally, a QC compiler reads the file +.Pa progs.src +which in its first line contains the output filename, and the rest is a +list of QC source files that are to be compiled in order. +.Nm gmqcc +optionally takes options to specify the output and +input files on the commandline, and also accepts assembly files. +.Sh OPTIONS +.Nm gmqcc +mostly tries to mimic gcc's commandline handling, though +there are also traditional long-options available. +.Bl -tag -width Ds +.It Fl h , Fl -help +Show a usage message and exit. +.It Fl o , Fl -output= Ns Ar filename +Specify the output filename. Defaults to progs.dat. This will overwrite +the output file listed in a +.Pa progs.src +file in case such a file is used. +.Bl -tag -width indent +.It Fl O Ns Ar number +Specify the optimization level +.It Ar 3 +Highest optimization level +.It Ar 2 +Default optimization level +.It Ar 1 +Minimal optimization level +.It Ar 0 +Disable optimization entirely +.El +.Pp +.It Fl O Ns Ar name , Fl Ono- Ns Ar name +Enable or disable a specific optimization. Note that these options +must be used after setting the optimization level, otherwise they'll +be overwritten. +.It Fl O Ns Cm help +List all possible optimizations and the optimization level they're +activated at. +.It Fl q , Fl -quiet +Be less verbose. In particular removes the messages about which files +are being processed, and which compilation mode is being used, and +some others. Warnings and errors will of course still be displayed. +.It Fl D Ns Ar macroname , Fl D Ns Ar macroname Ns = Ns Ar value +Predefine a macro, optionally with a optional value. +.It Fl E +Run only the preprocessor as if +.Fl f Ns Cm ftepp +was used and print the preprocessed code to stdout. +.It Fl W Ns Ar warning , Fl Wno- Ns Ar warning +Enable or disable a warning. +.It Fl W Ns Cm all +Enable almost all warnings. Overrides preceding +.Fl W +parameters. +.Pp +The following warnings will +.Em not +be enabled: +.Bl -tag -width indent -offset indent +.It Fl W Ns Cm uninitialized-global +.El +.It Fl W Ns Cm error , Fl Wno- Ns Cm error +Controls whether or not all warnings should be treated as errors. +.It Fl Werror- Ns Ar warning , Fl Wno-error- Ns Ar warning +Controls whether a specific warning should be an error. +.It Fl W Ns Cm help +List all possible warn flags. +.It Fl f Ns Ar flag , Fl fno- Ns Ar flag +Enable or disable a specific compile flag. See the list of flags +below. +.It Fl f Ns Cm help +List all possible compile flags. +.It Fl nocolor +Disables colored output +.It Fl config= Ns Ar file +Use an ini file to read all the +.Fl O , Fl W +and +.Fl f +flag from. See the +.It Fl "debug" +Turn on some compiler debugging mechanisms. +.It Fl memchk +Turn on compiler mem-check. (Shows allocations and checks for leaks.) +.It Fl -memdumpcols Ns Ar columns +Changes the number of columns to use for the debug memory dump, defaults to 16. +.Sx CONFIG +section about the file format. +.It Fl redirout= Ns Ar file +Redirects standard output to a +.Ar file +.It Fl redirerr= Ns Ar file +Redirects standard error to a +.Ar file +.It Fl std= Ns Ar standard +Use the specified standard for parsing QC code. The following standards +are available: +.Ar gmqcc , Ar qcc , Ar fteqcc +Selecting a standard also implies some +.Fl f +options and behaves as if +those options have been written right after the +.Fl std +option, meaning +if you changed them before the +.Fl -std +option, you're now overwriting them. +.Pp +.Fl std= Ns Cm gmqcc No includes: +.Bl -tag -width indent -compact -offset Ds +.It Fl f Ns Cm adjust-vector-fields +.It Fl f Ns Cm correct-logic +.It Fl f Ns Cm true-empty-strings +.It Fl f Ns Cm loop-labels +.It Fl f Ns Cm initialized-nonconstants +.It Fl f Ns Cm translatable-strings +.It Fl fno- Ns Cm false-empty-strings +.It Fl W Ns Cm invalid-parameter-count +.It Fl W Ns Cm missing-returnvalues +.It Fl f Ns Cm correct-ternary Li (cannot be turned off) +.El +.Pp +.Fl std= Ns Cm qcc No includes: +.Bl -tag -width indent -compact -offset Ds +.It Fl f Ns Cm assign-function-types +.It Fl fIno- Ns Cm adjust-vector-fields +.El +.Pp +.Fl std= Ns Cm fteqcc No includes: +.Bl -tag -width indent -compact -offset Ds +.It Fl f Ns Cm ftepp +.It Fl f Ns Cm translatable-strings +.It Fl f Ns Cm assign-function-types +.It Fl W Ns Cm ternary-precedence +.It Fl fno- Ns Cm adjust-vector-fields +.It Fl fno- Ns Cm correct-ternary +.El +.It Fl -add-info +Adds compiler information to the generated binary file. Currently +this includes the following globals: +.Bl -tag -width indent -compact +.It Li reserved:version +String containing the compiler version as printed by the --version +parameter. +.El +.It Fl -correct , Fl -no-correct +When enabled, errors about undefined values try to suggest an existing +value via spell checking. +.It Fl dump +DEBUG OPTION. Print the code's intermediate representation before the +optimization and finalization passes to stdout before generating the +binary. +.It Fl dumpfin +DEBUG OPTION. Print the code's intermediate representation after the +optimization and finalization passes to stdout before generating the +binary. The instructions will be enumerated, and values will contain a +list of liferanges. +.El +.Sh COMPILE WARNINGS +.Bl -tag -width Ds +.It Fl W Ns Cm unused-variable +Generate a warning about variables which are declared but never used. +This can be avoided by adding the +.Ql noref +keyword in front of the +variable declaration. Additionally a complete section of unreferenced +variables can be opened using +.Ql #pragma noref 1 +and closed via +.Ql #pragma noref 0 Ns . +.It Fl W Ns Cm used-uninitialized +Generate a warning if it is possible that a variable can be used +without prior initialization. Note that this warning is not +necessarily reliable if the initialization happens only under certain +conditions. The other way is +.Em not +possible: that the warning is +.Em not +generated when uninitialized use +.Em is +possible. +.It Fl W Ns Cm unknown-control-sequence +Generate an error when an unrecognized control sequence in a string is +used. Meaning: when there's a character after a backslash in a string +which has no known meaning. +.It Fl W Ns Cm extensions +Warn when using special extensions which are not part of the selected +standard. +.It Fl W Ns Cm field-redeclared +Generally QC compilers ignore redeclaration of fields. Here you can +optionally enable a warning. +.It Fl W Ns Cm missing-return-values +Functions which aren't of type +.Ft void +will warn if it possible to +reach the end without returning an actual value. +.It Fl W Ns Cm invalid-parameter-count +Warn about a function call with an invalid number of parameters. +.It Fl W Ns Cm local-shadows +Warn when a locally declared variable shadows variable. +.It Fl W Ns Cm local-constants +Warn when the initialization of a local variable turns the variable +into a constant. This is default behaviour unless +.Fl f Ns Cm initialized-nonconstants +is used. +.It Fl W Ns Cm void-variables +There are only 2 known global variables of type void: +.Ql end_sys_globals +and +.Ql end_sys_fields Ns . +Any other void-variable will warn. +.It Fl W Ns Cm implicit-function-pointer +A global function which is not declared with the +.Ql var +keyword is +expected to have an implementing body, or be a builtin. If neither is +the case, it implicitly becomes a function pointer, and a warning is +generated. +.It Fl W Ns Cm variadic-function +Currently there's no way for an in QC implemented function to access +variadic parameters. If a function with variadic parameters has an +implementing body, a warning will be generated. +.It Fl W Ns Cm frame-macros +Generate warnings about +.Ql $frame +commands, for instance about +duplicate frame definitions. +.It Fl W Ns Cm effectless-statement +Warn about statements which have no effect. Any expression which does +not call a function or assigns a variable. +.It Fl W Ns Cm end-sys-fields +The +.Ql end_sys_fields +variable is supposed to be a global variable +of type +.Ft void Ns . +It is also recognized as a \fIfield\fR but this +will generate a warning. +.It Fl W Ns Cm assign-function-types +Warn when assigning to a function pointer with an unmatching +signature. This usually happens in cases like assigning the null +function to an entity's .think function pointer. +.It Fl W Ns Cm cpp +Show warnings created using the preprocessor's '#warning' directive. +.It Fl W Ns Cm multifile-if +Warn if there's a preprocessor \fI#if\fR spanning across several +files. +.It Fl W Ns Cm double-declaration +Warn about multiple declarations of globals. This seems pretty common +in QC code so you probably do not want this unless you want to clean +up your code. +.It Fl W Ns Cm const-var +The combination of \fIconst\fR and \fIvar\fR is not illegal, however +different compilers may handle them differently. We were told, the +intention is to create a function-pointer which is not assignable. +This is exactly how we interpret it. However for this interpretation +the +.Ql var +keyword is considered superfluous (and philosophically +wrong), so it is possible to generate a warning about this. +.It Fl W Ns Cm multibyte-character +Warn about multibyte character constants, they do not work right now. +.It Fl W Ns Cm ternary-precedence +Warn if a ternary expression which contains a comma operator is used +without enclosing parenthesis, since this is most likely not what you +actually want. We recommend the +.Fl f Ns Cm correct-ternary +option. +.It Fl W Ns Cm unknown-pragmas +Warn when encountering an unrecognized +.Ql #pragma +line. +.It Fl W Ns Cm unreachable-code +Warn about unreachable code. That is: code after a return statement, +or code after a call to a function marked as 'noreturn'. +.It Fl W Ns Cm debug +Enable some warnings added in order to help debugging in the compiler. +You won't need this. +.It Fl W Ns Cm unknown-attribute +Warn on an unknown attribute. The warning will inlclude only the first +token inside the enclosing attribute-brackets. This may change when +the actual attribute syntax is better defined. +.It Fl W Ns Cm reserved-names +Warn when using reserved names such as +.Ql nil Ns . +.It Fl W Ns Cm uninitialized-constant +Warn about global constants (using the +.Ql const +keyword) with no +assigned value. +.It Fl W Ns Cm uninitialized-global +Warn about global variables with no initializing value. This is off by +default, and is added mostly to help find null-values which are +supposed to be replaced by the untyped 'nil' constant. +.It Fl W Ns Cm different-qualifiers +Warn when a variables is redeclared with a different qualifier. For +example when redeclaring a variable as \'var\' which was previously +marked \'const\'. +.It Fl W Ns Cm different-attributes +Similar to the above but for attributes like +.Ql [[noreturn]] Ns . +.It Fl W Ns Cm deprecated +Warn when a function is marked with the attribute +"[[deprecated]]". This flag enables a warning on calls to functions +marked as such. +.It Fl W Ns Cm parenthesis +Warn about possible mistakes caused by missing or wrong parenthesis, +like an assignment in an 'if' condition when there's no additional set +of parens around the assignment. +.El +.Sh COMPILE FLAGS +.Bl -tag -width Ds +.It Fl f Ns Cm darkplaces-string-table-bug +Add some additional characters to the string table in order to +compensate for a wrong boundcheck in some specific version of the +darkplaces engine. +.It Fl f Ns Cm adjust-vector-fields +When assigning to field pointers of type \fI.vector\fR the common +behaviour in compilers like \fIfteqcc\fR is to only assign the +x-component of the pointer. This means that you can use the vector as +such, but you cannot use its y and z components directly. This flag +fixes this behaviour. Before using it make sure your code does not +depend on the buggy behaviour. +.It Fl f Ns Cm ftepp +Enable a partially fteqcc-compatible preprocessor. It supports all the +features used in the Xonotic codebase. If you need more, write a +ticket. +.It Fl f Ns Cm ftepp-predefs +Enable some predefined macros. This only works in combination with +\'-fftepp' and is currently not included by '-std=fteqcc'. The +following macros will be added: +.Bd -literal -offset indent +__LINE__ +__FILE__ +__COUNTER__ +__COUNTER_LAST__ +__RANDOM__ +__RANDOM_LAST__ +__DATE__ +__TIME__ +__FUNC__ +.Ed +.Pp +Note that +.Li __FUNC__ +is not actually a preprocessor macro, but is recognized by the parser +even with the preprocessor disabled. +.Pp +Note that fteqcc also defines +.Li __NULL__ +which becomes the first global. Assigning it to a vector does not +yield the same result as in gmqcc where +.Li __NULL__ +is defined to +.Li nil +(See +.Fl f Ns Cm untyped-nil +), which will cause the vector to be zero in all components. With fteqcc +only the first component will be 0, while the other two will become +the first to of the global return value. This behavior is odd and +relying on it should be discouraged, and thus is not supported by +gmqcc. +.It Fl f Ns Cm relaxed-switch +Allow switch cases to use non constant variables. +.It Fl f Ns Cm short-logic +Perform early out in logical AND and OR expressions. The final result +will be either a 0 or a 1, see the next flag for more possibilities. +.It Fl f Ns Cm perl-logic +In many languages, logical expressions perform early out in a special +way: If the left operand of an AND yeilds true, or the one of an OR +yields false, the complete expression evaluates to the right side. +Thus +.Ql true && 5 +evaluates to 5 rather than 1. +.It Fl f Ns Cm translatable-strings +Enable the underscore intrinsic: Using +.Ql _("A string constant") +will cause the string immediate to get a name with a "dotranslate_" +prefix. The darkplaces engine recognizes these and translates them in +a way similar to how gettext works. +.It Fl f Ns Cm initialized-nonconstants +Don't implicitly convert initialized variables to constants. With this +flag, the \fIconst\fR keyword is required to make a constant. +.It Fl f Ns Cm assign-function-types +If this flag is not set, (and it is set by default in the qcc and +fteqcc standards), assigning function pointers of mismatching +signatures will result in an error rather than a warning. +.It Fl f Ns Cm lno +Produce a linenumber file along with the output .dat file. +.It Fl f Ns Cm correct-ternary +Use C's operator precedence for ternary expressions. Unless your code +depends on fteqcc-compatible behaviour, you'll want to use thi +soption. +.It Fl f Ns Cm single-vector-defs +Normally vectors generate 4 defs, once for the vector, and once for +its components with _x, _y, _z suffixes. This option +prevents components from being listed. +.It Fl f Ns Cm correct-logic +Most QC compilers translate +.Ql if(a_vector) +directly as an IF on the +vector, which means only the x-component is checked. This option causes +vectors to be cast to actual booleans via a NOT_V and, if necessary, a +NOT_F chained to it. +.Bd -literal -offset indent +if (a_vector) // becomes +if not(!a_vector) +// likewise +a = a_vector && a_float // becomes +a = !!a_vector && a_float +.Ed +.It Fl f Ns Cm true-empty-strings +An empty string is considered to be true everywhere. The NOT_S +instruction usually considers an empty string to be false, this option +effectively causes the unary not in strings to use NOT_F instead. +.It Fl f Ns Cm false-empty-strings +An empty string is considered to be false everywhere. This means loops +and if statements which depend on a string will perform a NOT_S +instruction on the string before using it. +.It Fl f Ns Cm utf8 +Enable utf8 characters. This allows utf-8 encoded character constants, +and escape sequence codepoints in the valid utf-8 range. Effectively +enabling escape sequences like '\\{x2211}'. +.It Fl f Ns Cm bail-on-werror +When a warning is treated as an error, and this option is set (which +it is by default), it is like any other error and will cause +compilation to stop. When disabling this flag by using +\-fno-bail-on-werror, compilation will continue until the end, but no +output is generated. Instead the first such error message's context is +shown. +.It Fl f Ns Cm loop-labels +Allow loops to be labeled, and allow 'break' and 'continue' to take an +optional label to decide which loop to actually jump out of or +continue. +.Bd -literal -offset indent +for :outer (i = 0; i < n; ++i) { + while (inner) { + ...; + if (something) + continue outer; + } +} +.Ed +.It Fl f Ns Cm untyped-nil +Adds a global named 'nil' which is of no type and can be assigned to +anything. No typechecking will be performed on assignments. Assigning +to it is forbidden, using it in any other kind of expression is also +not allowed. +.sp +Note that this is different from fteqcc's __NULL__: In fteqcc, +__NULL__ maps to the integer written as '0i'. It's can be assigned to +function pointers and integers, but it'll error about invalid +instructions when assigning it to floats without enabling the FTE +instruction set. There's also a bug which allows it to be assigned to +vectors, for which the source will be the global at offset 0, meaning +the vector's y and z components will contain the OFS_RETURN x and y +components. +.sp +In that gmqcc the nil global is an actual global filled with zeroes, +and can be assigned to anything including fields, vectors or function +pointers, and they end up becoming zeroed. +.It Fl f Ns Cm permissive +Various effects, usually to weaken some conditions. +.Bl -tag -width indent -offset indent +.It with Fl f Ns Cm untyped-nil +Allow local variables named +.Ql nil Ns . +(This will not allow declaring a global of that name.) +.El +.It Fl f Ns Cm variadic-args +Allow variadic parameters to be accessed by QC code. This can be +achieved via the '...' function, which takes a parameter index and a +typename. +.Pp +Example: +.Bd -literal -offset indent +void vafunc(string...count) { + float i; + for (i = 0; i < count; ++i) + print(...(i, string), "\\n"); +} +.Ed +.It Fl f Ns Cm legacy-vector-maths +Most Quake VMs, including the one from FTEQW or up till recently +Darkplaces, do not cope well with vector instructions with overlapping +input and output. This option will avoid producing such code. +.It Fl f Ns Cm expressions-for-builtins +Usually builtin-numbers are just immediate constants. With this flag +expressions can be used, as long as they are compile-time constant. +.Pp +Example: +.Bd -literal -offset indent +void printA() = #1; // the usual way +void printB() = #2-1; // with a constant expression +.Ed +.It Fl f Ns Cm return-assignments +Enabiling this option will allow assigning values or expressions to the +return keyword as if it were a local variable of the same type as the +function's signature's return type. +.Pp +Example: +.Bd -literal -offset indent +float bar() { return 1024; } +float fun() { + return = bar(); + return; // returns value of bar +} +.El +.Sh OPTIMIZATIONS +.Bl -tag -width Ds +.It Fl O Ns Cm peephole +Some general peephole optimizations. For instance the code `a = b + c` +typically generates 2 instructions, an ADD and a STORE. This +optimization removes the STORE and lets the ADD write directly into A. +.It Fl O Ns Cm tail-recursion +Tail recursive function calls will be turned into loops to avoid the +overhead of the CALL and RETURN instructions. +.It Fl O Ns Cm overlap-locals +Make all functions which use neither local arrays nor have locals +which are seen as possibly uninitialized use the same local section. +This should be pretty safe compared to other compilers which do not +check for uninitialized values properly. The problem is that there's +QC code out there which really doesn't initialize some values. This is +fine as long as this kind of optimization isn't used, but also, only +as long as the functions cannot be called in a recursive manner. Since +it's hard to know whether or not an array is actually fully +initialized, especially when initializing it via a loop, we assume +functions with arrays to be too dangerous for this optimization. +.It Fl O Ns Cm local-temps +This promotes locally declared variables to "temps". Meaning when a +temporary result of an operation has to be stored somewhere, a local +variable which is not 'alive' at that point can be used to keep the +result. This can reduce the size of the global section. +This will not have declared variables overlap, even if it was +possible. +.It Fl O Ns Cm global-temps +Causes temporary values which do not need to be backed up on a CALL to +not be stored in the function's locals-area. With this, a CALL to a +function may need to back up fewer values and thus execute faster. +.It Fl O Ns Cm strip-constant-names +Don't generate defs for immediate values or even declared constants. +Meaning variables which are implicitly constant or qualified as such +using the 'const' keyword. +.It Fl O Ns Cm overlap-strings +Aggressively reuse strings in the string section. When a string should +be added which is the trailing substring of an already existing +string, the existing string's tail will be returned instead of the new +string being added. +.Pp +For example the following code will only generate 1 string: +.Bd -literal -offset indent +print("Hell you!\\n"); +print("you!\\n"); // trailing substring of "Hello you!\\n" +.Ed +.Pp +There's however one limitation. Strings are still processed in order, +so if the above print statements were reversed, this optimization +would not happen. +.It Fl O Ns Cm call-stores +By default, all parameters of a CALL are copied into the +parameter-globals right before the CALL instructions. This is the +easiest and safest way to translate calls, but also adds a lot of +unnecessary copying and unnecessary temporary values. This +optimization makes operations which are used as a parameter evaluate +directly into the parameter-global if that is possible, which is when +there's no other CALL instruction in between. +.It Fl O Ns Cm void-return +Usually an empty RETURN instruction is added to the end of a void +typed function. However, additionally after every function a DONE +instruction is added for several reasons. (For example the qcvm's +disassemble switch uses it to know when the function ends.). This +optimization replaces that last RETURN with DONE rather than adding +the DONE additionally. +.It Fl O Ns Cm vector-components +Because traditional QC code doesn't allow you to access individual +vector components of a computed vector without storing it in a local +first, sometimes people multiply it by a constant like +.Ql '0 1 0' +to get, +in this case, the y component of a vector. This optimization will turn +such a multiplication into a direct component access. If the factor is +anything other than 1, a float-multiplication will be added, which is +still faster than a vector multiplication. +.El +.Sh CONFIG +The configuration file is similar to regular .ini files. Comments +start with hashtags or semicolons, sections are written in square +brackets and in each section there can be arbitrary many key-value +pairs. +.Pp +There are 3 sections currently: +.Ql flags Ns , +.Ql warnings Ns , +.Ql optimizations Ns . +They contain a list of boolean values of the form +.Ql VARNAME = true +or +.Ql VARNAME = false Ns . +The variable names are the same as for the +corresponding +.Fl W , Fl f +or +.Fl O +flag written with only capital letters and +dashes replaced by underscores. +.Pp +Here's an example: +.Bd -literal -offset indent +# a GMQCC configuration file +[flags] + FTEPP = true + ADJUST_VECTOR_FIELDS = false + LNO = true + +[warnings] + UNUSED_VARIABLE = false + USED_UNINITIALIZED = true + +[optimizations] + PEEPHOLE = true + TAIL_RECURSION = true +.Ed +.Sh FILES +.Bl -tag -width Ds +.It gmqcc.ini.example +A documented example for a gmqcc.ini file. +.El +.Sh SEE ALSO +.Xr qcvm 1 +.Sh AUTHOR +See . +.Sh BUGS +Currently the '-fftepp-predefs' flag is not included by '-std=fteqcc', +partially because it is not entirely conformant to fteqcc. +.Pp +Please report bugs on , +or see on how to contact us. diff --git a/misc/source/gmqcc-src/doc/gmqpak.1 b/misc/source/gmqcc-src/doc/gmqpak.1 new file mode 100644 index 00000000..967d4320 --- /dev/null +++ b/misc/source/gmqcc-src/doc/gmqpak.1 @@ -0,0 +1,38 @@ +.\" gmqpak mdoc manpage +.Dd April 27, 2013 +.Dt GMQPAK 2 PRM +.Os +.Sh NAME +.Nm gmqpak +.Nd A standalone Quake PAK utility +.Sh SYNOPSIS +.Nm gmqpak +.Op Cm options +.Op Cm files +.Sh DESCRIPTION +.Nm gmqpak +Is a standalone Quake PAK file utility supporting the extraction of files, +directories, or whole PAKs, as well as the opposite (creation of PAK files). +.Sh OPTIONS +.Bl -tag +.It Fl -file Ar file +Specify the PAK file to create or extract +.It Fl -e +Used to denote the extraction operation on a PAK file. +.It Fl -c +Used to denote the creation operation on a PAK file. +.El +.Sh EXAMPLES +Here's some examples of how to use the utility to manipulate PAK files. +.Bl -ohang +.It Li gmqpak -file id1.pak -e +.D1 extracts a PAK to ./ +.It Li gmqpak -file new.pak -c file1 dir/file2 +.D1 creates a PAK with the files specified +.It Li gmqpak -file new1.pak -c directory. +.D1 creates a PAK from files within the directory, including subdirectories and files. +.Sh AUTHOR +See . +.Sh BUGS +Please report bugs on , +or see on how to contact us. diff --git a/misc/source/gmqcc-src/doc/html/download.c b/misc/source/gmqcc-src/doc/html/download.c new file mode 100644 index 00000000..33845a78 --- /dev/null +++ b/misc/source/gmqcc-src/doc/html/download.c @@ -0,0 +1,284 @@ +#include +#include +#include +#include + +/* + * protect some information, not that I care, but this is just to stay + * safer. + */ +#define SECURITY_BASE "\ +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" +#define SECURITY_TOKEN "\ +P29hdXRoX2NvbnN1bWVyX2tleT03NnZoM3E0Mmhudm16bTMmb2F1dGhfdG9rZW49\ +dzBieHpmMGRmdDhlZGZxJm9hdXRoX3NpZ25hdHVyZV9tZXRob2Q9UExBSU5URVhU\ +Jm9hdXRoX3NpZ25hdHVyZT10bWVlY2h0MmNtaDcyeGElMjY5dm9zeDd4OGd5NGtn\ +amsmb2F1dGhfdGltZXN0YW1wPSZvYXV0aF9ub25jZT0xMjE2NQo=" + +int isbase64(char c) { + return !!(c && strchr(SECURITY_BASE, c) != NULL); +} +char value(char c) { + const char *load = SECURITY_BASE; + const char *find = strchr(load, c); + + return (find) ? find - load : 0; +} + +int security_decode(unsigned char *dest, const unsigned char *src, int srclen) { + unsigned char *p; + + if(!*src) + return 0; + + *dest = 0; + p = dest; + + do { + *p++ = (value(src[0]) << 2) | (value(src[1]) >> 4); + *p++ = (value(src[1]) << 4) | (value(src[2]) >> 2); + *p++ = (value(src[2]) << 6) | (value(src[3]) >> 0); + + if(!isbase64(src[1])) { + p -= 2; + break; + } + else if(!isbase64(src[2])) { + p -= 2; + break; + } + else if(!isbase64(src[3])) { + p--; + break; + } + src += 4; + + while(*src && (*src == 13 || *src == 10)) + src++; + } while(srclen-= 4); + + *p = 0; + return p-dest; +} + +#define BASEURL " https://api-content.dropbox.com/1/files/sandbox/" + +/* + * If more platforms are supported add the entries between the start + * tag here, and the end tag below. Nothing else needs to be done + * (the table needs to match the HTML too) + */ +#define ARCHLINUX_32_REF "%sgmqcc-%c.%c.%c-1-i686.pkg.tar.xz%s" +#define ARCHLINUX_64_REF "%sgmqcc-%c.%c.%c-1-x86_64.pkg.tar.xz%s" +#define DEBIAN_32_REF "%sgmqcc-%c.%c.%c-i686.deb%s" +#define DEBIAN_64_REF "%sgmqcc-%c.%c.%c-x86_64.deb%s" +#define WINDOWS_32_REF "%sgmqcc-%c.%c.%c-win32.zip%s" +#define WINDOWS_64_REF "%sgmqcc-%c.%c.%c-win64.zip%s" + +#define HTML "\ +\ +\ +\ + \ + \ + GMQCC\ + \ + \ + \ + \ + \ +\ +\ +
\ +
\ +
\ +

GMQCC

\ +

An Improved Quake C Compiler

\ + \ +
\ +
\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +
Operating System / Distributionx86 Architecturex86_64 Architecture
ArchlinuxDownloadDownload
DebianDownloadDownload
WindowsDownloadDownload
\ +
\ +
\ + \ +
\ +
\ + \ +\ +\ +" + +static char build_table[][4096] = { + ARCHLINUX_32_REF, ARCHLINUX_64_REF, + DEBIAN_32_REF, DEBIAN_64_REF, + WINDOWS_32_REF, WINDOWS_64_REF +}; +/*
*/ + +#define ISXDIGIT(c) ((c >= 48 && c <= 57) || ((c & ~0x20) >= 65 && (c & ~0x20) <= 70)) +typedef struct { + char *data; + unsigned int len; + unsigned int size; +} url_t; +void escape(url_t *str) { + char *p, *ptr; + char hexstr[3]; + unsigned int i=0; + unsigned long l=0; + + p = str->data; + for(i=0; i < str->len; i++) { + if((p - str->data) >= str->len) + break; + if(*p == '%' && + ((p - str->data)+2) < str->len && + ISXDIGIT(*(p+1)) && + ISXDIGIT(*(p+2)) + ) { + p++; + hexstr[0] = *p++; + hexstr[1] = *p++; + hexstr[2] = 0; + l = strtoul(hexstr, &ptr, 16); + str->data[i] = (char)(l & 0x7f); + continue; + } + if(*p == '+') { + *p = ' '; + } + str->data[i] = *p++; + } + str->data[i] = 0; + str->len = i; +} + +void version(const char *directory, char *major, char *minor, char *patch) { + FILE *handle; + char file[4096]; + size_t size = 0; + char *data = NULL; + snprintf(file, sizeof(file), "%s/gmqcc.h", directory); + + handle = fopen(file, "r"); + if (!handle) { + fprintf(stderr, "failed to open %s for reading version (%s)\n", + file, strerror(errno) + ); + abort(); + } + + while (getline(&data, &size, handle) != EOF) { + + #define TEST(TYPE, STORE) \ + if (strstr(data, "#define GMQCC_VERSION_" TYPE )) { \ + char *get = data; \ + while (!isdigit(*get)) \ + get++; \ + *STORE = *get; \ + } + + TEST("MAJOR", major) + TEST("MINOR", minor) + TEST("PATCH", patch) + + #undef TEST + } + + free(data); +} + +void genhtml() { + FILE *fp = fopen("download.html", "w"); + if (!fp) { + fprintf(stderr, "failed to generate HTML: %s\n", strerror(errno)); + abort(); + } + + fprintf(fp, HTML, + build_table[0], build_table[1], + build_table[2], build_table[3], + build_table[4], build_table[5] + ); + fclose (fp); +} + +/* + * Builds a list of download links with the right version and handles the + * rest of the magic. + */ +void build(const char *directory) { + /* Figure out version number */ + char find[3]; + char decode[4096]; + size_t size; + version(directory, &find[0], &find[1], &find[2]); + + /* + * decode the secuity stuff for preparing the URLs which will be used + * as links. + */ + memset(decode, 0, sizeof(decode)); + security_decode(decode, SECURITY_TOKEN, strlen(SECURITY_TOKEN)); + + for (size = 0; size < sizeof(build_table) / sizeof(*build_table); size++) { + char *load = strdup(build_table[size]); + url_t esc = { NULL, 0 }; + + snprintf(build_table[size], 4096, load, BASEURL, find[0], find[1], find[2], decode); + esc.data = strdup(build_table[size]); + esc.size = strlen(build_table[size]); + esc.len = esc.size; + + /* Yes we also need to escape URLs just incase */ + escape(&esc); + free(load); + } + + /* + * Now generate the HTML file for those download links by asking tinyurl to + */ + genhtml(); +} + +int main(int argc, char **argv) { + size_t itr; + + argc--; + argv++; + if (!argc) { + printf("usage: %s [gmqcc.h location]\n", argv[-1]); + return 0; + } + + build(*argv); +} diff --git a/misc/source/gmqcc-src/doc/html/gmqcc.png b/misc/source/gmqcc-src/doc/html/gmqcc.png new file mode 100644 index 0000000000000000000000000000000000000000..0d9b79fb691074cf60e8598cdc20a69e3a4134f5 GIT binary patch literal 2399 zcmZWrc{mh$7yenYW+}@gMwBh);zqVy%UGJZOv8}1#9YiRgeY5>vL!S(L}i&qA((7ybcJ*tAXs9ks{4 z^EAMrf(#^JmWvD7j6Be<+RiMn&>?#)?I?M(6BB}oZv%>M!}*{SQko%?-iV@3VP>+b>Ht$AmG~9rb#ChM(TTJ^ zgF65}I*0yy2G>|Fzl|(p@6DerJVzD5+ePNNo3x@SDF{k8RJ-G`091m;e!v=IG* zUu3VaMsv*m{0EodV|TZdPcZWvX zR)s=KbfaB^!or40)zt|FJKBNa;TP`Z=FS~He0a>EPb}<$`)aB+3-D zJm!6)y80X>-zW#+t|=?#&N4AEX_8hkQ>#~ysJ$CQ7n;U=C@e2$nKK6a`DR+Z12}k1uq1xp%|RRhL*;h!YT~ zc`va3bxQeEC^>whUjC_`{n5L>yJ~A!LKX*0l=p|MC&a~7^+KUgrxhz1R&NXh0_l2o zwz@($DzBR3!5Hl6`3w&Y4V6eFnuIaVzWJj|#Z+HkUuE?;XEsOza4fLWUanJ=jQ(U4 z6d0kY`EG4YCeyHN9M60m@4DHZiOf>`#b=RI`#+-Wx3t-|?B;oHDD^Qgt^P*7 zX@K{1<_vw?-5wNdunz5W$0g?7+#0S9iN7j_Mj|uYii&)Vp#Br}LEHR?t*R?Lr?OwA z43iiwQI&F7-e)}C#=O_${ko7xm9x*Dv&bukr<hLFWTS9&PEaLu831Ev|E7|JA{yxd$tldzdRN2)`X#Y-UI<2RyrA%k zu8(!d7`fUG;BzK zu19>EphDq}GJdm)T6dqE*qkcI95>L`))tg_x~1WEEMNC^u|dXF99F7Rn;ORpRtif= zC0~;9^BmzdhD|Ewe7W}5b)mIgJI|Y!fSR_A*U^lSXz}>vGT~@Hsn$6TN4dDTcxjEO zpH}zGtw@MWIH^Cj&^&HUb~CM*7aiE=W3JF0Z{=BHXeJ5c9|n&Ydp3+CFAqxqFCoOJ zcna^vAb$1QF;w=K$g-}3$Tj7wnUE{{LiXXujS5v$Y??-H7}CgLs88qm?S^n{d${M zNTDW~{uxD%w4QuynA%ThR?U0fsxa8n=S$9(;Vl_>=?+0{DtFo6;T23*)A4HCo`=Z40#C~X)>h@aLoD;g0 zxO_X&ubk4fV%r6!m1$|(PRt0rw^AwRI;InIG;ke7w`4X-)I`DmbccqB|B2z52METd zdV`-bKZG2MclU~R3lENt3AhpjKrBNN(ciB(#EX)0)iD|~8Z|Noz>qCGup^n^@H4a^ M9nRR-!2NIj2j+`qEdT%j literal 0 HcmV?d00001 diff --git a/misc/source/gmqcc-src/doc/html/style.css b/misc/source/gmqcc-src/doc/html/style.css new file mode 100644 index 00000000..fa67901c --- /dev/null +++ b/misc/source/gmqcc-src/doc/html/style.css @@ -0,0 +1,92 @@ +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font: inherit; + vertical-align: baseline; +} + +body { + font-family:"Helvetica Neue", Helvetica, Arial, sans-serif; + margin: 0; + line-height: 1.8em; + -webkit-font-smoothing: antialiased; + background: #CDC9C9; +} + +h1, h2, h3, h4, h5, h6 { + color:#232323; + margin:36px 0 10px; +} + +.head-ltitle, .head-rtitle, .head-vol { + font: bold 0.8em Arial, Helvectia, sans-serif; +} + +.head-vol { + visibility: hidden; +} + +.name { + font: bold 0.8em Monospace, serif; +} + +.ftype { + font: normal 1em Monospace, serif; +} + +h1 { + color: #c30000; + margin-top: 0.3em; + margin-bottom: 0.3em; + line-height: 1.3; + font: normal 1.4em Arvo, Monaco, sans-serif; + /*font: bold 1.4em Arial, Helvetica, sans-serif*/ +} + +.section { + margin-bottom: 1em; + margin-top: 1em; + padding-left: 1em; + border-top: 1px #cccccc solid; + font: normal 0.9em Arial, Helvetica, sans-serif; + text-align: justify; + border: 0; + padding-bottom: 1em; + border-bottom: 1px solid #f0e0e0; +} + +.list-tag { + padding-left: 1em; +} + +pre { + border: 1px dashed #ffffff; + background: #ddd8d8; +} + +.flag { + font: normal 1em Monospace, serif; +} + +a { + color:#C30000; + font-weight:200; + text-decoration:none; +} + +a:hover { + text-decoration: underline; +} diff --git a/misc/source/gmqcc-src/doc/qcvm.1 b/misc/source/gmqcc-src/doc/qcvm.1 new file mode 100644 index 00000000..3a0562f0 --- /dev/null +++ b/misc/source/gmqcc-src/doc/qcvm.1 @@ -0,0 +1,105 @@ +.\" qcvm mdoc manpage +.Dd January 31, 2013 +.Dt QCVM 1 PRM +.Os +.Sh NAME +.Nm qcvm +.Nd A standalone QuakeC VM binary executor +.Sh SYNOPSIS +.Nm qcvm +.Op Cm options +.Op Cm parameters +.Ar program-file +.Sh DESCRIPTION +.Nm qcvm +is an executor for QuakeC VM binary files created using a QC +compiler such as gmqcc(1) or fteqcc. It provides a small set of +builtin functions, and by default executes the +.Fn main +function if there is one. Some options useful for debugging are +available as well. +.Sh OPTIONS +There are 2 types of options. Options for the executor, and parameter +options used to add parameters which are passed to the main function +on execution. +.Bl -tag -width Ds +.It Fl h , Fl -help +Show a usage message and exit. +.It Fl trace +Trace the execution. Each instruction will be printed to stdout before +executing it. +.It Fl profile +Perform some profiling. This is currently not really implemented, the +option is available nonetheless. +.It Fl info +Print information from the program's header instead of executing. +.It Fl disasm +Disassemble the program by function instead of executing. +.It Fl disasm-func Ar function +Search for and disassemble the given function. +.It Fl printdefs +List all entries from the program's defs-section. Effectively +listing all the global variables of the program. +This option disables execution. +.It Fl printfields +List all entries from the program's fields-section. Listing all +entity-fields declared in the program. +This option disables execution. +.It Fl printfuns +List functions and some information about their parameters. +This option disables execution. With a verbosity level of 1, builtin +numbers are printed. With a verbosity of 2, the function's sizes are +printed as well. This takes a little longer since the size is found by +searching for a +.Ql DONE +instruction in the code. +.It Fl v +Increase verbosity level, can be used multiple times. +.It Fl vector Ar 'x y z' +Append a vector parameter to be passed to +.Fn main Ns . +.It Fl float Ar number +Append a float parameter to be passed to +.Fn main Ns . +.It Fl string Ar 'text' +Append a string parameter to be passed to +.Fn main Ns . +.El +.Sh BUILTINS +The following builtin functions are available: +.Bl -ohang +.It Li 1) void print(string...) = #1; +.D1 Print the passed strings to stdout. At most 8 strings are allowed. +.It Li 2) string ftos(float) = #2; +.D1 Convert a float to a string. +.It Li 3) entity spawn() = #3; +.D1 Spawn an entity. +.It Li 4) void remove(entity) = #4; +.D1 Remove an entity. +.It Li 5) string vtos(vector) = #5; +.D1 Convert a vector to a string. +.It Li 6) void error(string...) = #6; +.D1 Print at most 8 strings to stdout and then exit with an error. +.It Li 7) float vlen(vector) = #7; +.D1 Get the length of a vector. +.It Li 8) string etos(entity) = #8; +.D1 Get the entity ID as string. +.It Li 9) float stof(string) = #9; +.D1 Convert a string to a float. +.It Li 10) string strcat(string, string) = #10; +.D1 Concatenate two strings, returning a tempstring. +.It Li 11) float strcmp(string, string) = #11; +.Li 12) float strncmp(string, string, float) = #11; +.D1 Compare two strings. Returns the same as the corresponding C functions. +.It Li 12) vector normalize(vector) = #12; +.D1 Normalize a vector so its length is 1. +.It Li 13) float sqrt(float) = #13; +.D1 Get a value's square root. +.El +.Sh SEE ALSO +.Xr gmqcc 1 +.Sh AUTHOR +See . +.Sh BUGS +Please report bugs on , +or see on how to contact us. diff --git a/misc/source/gmqcc-src/doc/specification.tex b/misc/source/gmqcc-src/doc/specification.tex new file mode 100644 index 00000000..74190fc4 --- /dev/null +++ b/misc/source/gmqcc-src/doc/specification.tex @@ -0,0 +1,759 @@ +\documentclass{article} + +%%% PACKAGES +\usepackage{geometry} +\usepackage[utf8]{inputenc} +\usepackage[parfill]{parskip} +\usepackage{subfig} +\usepackage{listings} +\usepackage{color} +\usepackage{sectsty} + +%%% GEOMETRY FOR DOCUMENT +\geometry{a4paper} + +%%% HEADERS/FOOTERS APPEARANCE +\usepackage{fancyhdr} % This should be set AFTER setting up the page geometry +\pagestyle{fancy} % options: empty , plain , fancy +\renewcommand{\headrulewidth}{0pt} % customise the layout... +\lhead{}\chead{}\rhead{} +\lfoot{}\cfoot{\thepage}\rfoot{} + +%%% SECTION TITLE APPEARANCE +\allsectionsfont{\sffamily\mdseries\upshape} % (See the fntguide.pdf for font help) + +%%% ToC APPEARANCE +\usepackage[nottoc,notlof,notlot]{tocbibind} % Put the bibliography in the ToC +\usepackage[titles,subfigure]{tocloft} % Alter the style of the Table of Contents +\renewcommand{\cftsecfont}{\rmfamily\mdseries\upshape} +\renewcommand{\cftsecpagefont}{\rmfamily\mdseries\upshape} % No bold! + +%%% listing language definitions +%%% BNF for now, QuakeC will be later +\definecolor{keyword1}{RGB}{0,102,153} +\definecolor{keyword2}{RGB}{0,153,102} +\definecolor{keyword3}{RGB}{0,153,255} +\definecolor{comment}{RGB}{204,0,0} +\definecolor{function}{RGB}{153,102,255} +\definecolor{digit}{RGB}{255,0,0} +\definecolor{string}{RGB}{255,0,204} +\definecolor{rule}{RGB}{192,192,192} +\definecolor{back}{RGB}{250,250,250} + +\lstdefinelanguage{bnf}{ + keywordstyle={\color{keyword2}\bfseries}, + keywords={}, + otherkeywords={::=,|}, + morecomment=[s][\color{comment}]{(*}{*)}, + stringstyle=\color{string}, + showstringspaces=false, + frame=none, + rulecolor=\color{rule}, + backgroundcolor=\color{back} +} + +%% Title Information %% +\title{The GMQCC QuakeC Programming Language} +\author{Dale Weiler} +\date{\today} + +\begin{document} + +%% Title Page %% +\maketitle +\thispagestyle{empty} +\raggedright +\abstract +This document specifies the form and establishes the interpretation of programs written in +the GMQCC QuakeC programming language variant (refereed simply as QuakeC throughout this +document). It specifies: +\begin{itemize} + \item the representation of QuakeC programs; + \item the syntax and constraints of the QuakeC language; + \item the semantic rules for interpreting QuakeC programs; + \item the representation of input data to be processed by QuakeC programs; + \item the representation of output data produced by QuakeC programs; + \item the restrictions and limits imposed by a conforming implementation of QuakeC. +\end{itemize} +This document does not specify +\begin{itemize} + \item the mechanism by which QuakeC programs are transformed for use by a data- + processing system; + \item the mechanism by which QuakeC programs are invoked for use by a data-processing + system; + \item the mechanism by which input data are transformed for use by a QuakeC program; + \item the size or complexity of a program and its data that will exceed the capacity + of any specific data-processing system or the capacity of a particular + execution environment; + \item all minimal requirements of a data-processing system that is capable of + supporting a conforming implementation. +\end{itemize} + +%% Table Of Contents %% +\newpage +\thispagestyle{empty} +\tableofcontents +\newpage + +%% Begin Contents %% +\raggedright % No weird TEX spacing on lines to fill page + +%% -> Terms, definitions, and symbols %% +\section{Terms, definitions, and symbols} +\subsection*{argument} +Expression in the comma-separated list bounded by the parentheses in a function call +expression, or a sequence of preprocessing tokens in the comma-separated list bounded +by the parentheses in a function-like macro invocation. + +\subsection*{behavior} +External appearance or action + +\subsection*{implementation-defined behavior} +Unspecified behavior where each implementation documents how the choice is made. + +\subsection*{undefined behavior} +Behavior, upon use of a non-portable or erroneous program construct or of erroneous data, +for which this document imposes no actual requirements. + +\subsection*{unspecified behavior} +Use of an unspecified value, or other behavior where this document provides two or more +possibilities and imposes no further requirements on which is chosen in any instance. + +\subsection*{constraint} +Restriction, either syntactic or semantic, by which the exposition of language elements +is to be interpreted. + +\subsection*{diagnostic message} +Message belonging to an implementation-defined subset of the implementation's message +output. + +\subsection*{object} +Region of data storage in the execution environment, the contents of which can represent +values. + +\subsection*{parameter} +Object declared as part of a function declaration or definition that acquires a value on +entry to the function, or an identifier from the comma-separated list bounded by the +parentheses immediately following the macro name in a function-like macro definition. + +\subsection*{recommended practice} +Specification that is strongly recommended as being in keeping with the intent of this +document, but that may be impractical for some implementations. + +\subsection*{value} +Precise meaning of the contents of an object when interpreted as having a specific type. + +\subsection*{implementation} +Particular set of software, running in a particular translation environment under +particular control options, that performs translation of programs for, and supports +execution of functions in, a particular execution environment. + +\subsection*{implementation-defined value} +Unspecified value where each implementation documents how the choice is made. + +\subsection*{unspecified value} +Valid value of the relevant type where this document imposes no requirements on which +value is chosen in any instance. + +%% -> Conformance %% +\section{Conformance} +In this document, "shall" is to be interpreted as a requirement on an implementation +or on a program; conversely, "shall not" is to be interpreted as a prohibition. \\ +If a "shall" or "shall not" requirement that appears outside of a constraint is violated, +the behavior is undefined. Undefined behavior is otherwise indicated in this document by +the words "undefined behavior" or by the omission of any explicit definition of behavior. +There is no difference in emphasis among these three; they all describe "behavior that is +undefined". + +%% -> Enviroment %% +\section{Environment} +An implementation that translates QuakeC source files and executes QuakeC programs in two +data processing-system environments, which will be called the translation environment and +the execution environment in this document. Their characteristics define and constrain the +results of executing QuakeC programs constructed according to the syntactic and semantic +rules for conforming implementations. +\subsection{Conceptual models} +\subsubsection{Translation environment} +\paragraph*{Translation steps} +The precedence among the syntax rules of translation is specified by the following steps +\begin{enumerate} + \item Physical source file characters are mapped, in an implementation-defined manner, + to the source character set (introducing new-line characters for end-of-line + indicators) if necessary. Trigraph and digraph sequences are replaced by their + corresponding single-character internal representations. + \item The source file is decomposed into preprocessing tokens and sequences of white- + space characters (including comments). A source file shall not end in a partial + preprocessing token or in a partial comment. Each comment is replaced by one + space character. New-line characters are retained. Whether each nonempty + sequences of white-space characters other than new-line is retained or replaced + by one space character is implementation-defined. + \item Preprocessing directives are executed, macro invocations are expanded + recursively. A \#include preprocessing directive causes the named header or + source file to be processed from step one through step three, recursively. All + preprocessing directives are then deleted. + \item Each source character set member and escape sequence in character constants and + string literals is converted to the corresponding member of the execution + character set; if there is no corresponding member, it is converted to an + implementation-defined member other than the null character. + \item Adjacent string literal tokens are concatenated. + \item White-space characters seperating tokens are no longer significant. Each + preprocessing token is converted into a token. The resulting tokens are then + syntactically and semantically analyzed and translated. +\end{enumerate} +\subparagraph*{Footnotes} +Implementations shall behave as if these steps occur separately, even though many are likely +to be folded together in practice. Source files need not be stored as file, nor need there +be any one-to-one correspondence between these items and any external representation. The +description is conceptual only, and does not specify any particular implementation. + +\paragraph*{Diagnostics} +A conforming implementation shall produce at least on diagnostic message(identified in an +implementation-defined manner) if a source file contains a violation of any syntax rule or +constraint, even if the behavior is also explicitly specified as undefined or +implementation-defined. Diagnostic messages need not be produced in other circumstances. + +%% ->-> Execution environments %% +\subsubsection{Execution environment} +A conforming execution environment shall provide at minimal the following 15 definitions +for built in functions, with an accompanying header or source file that defines them. +\begin{enumerate} + \item entity () spawn + \item void (entity) remove + + \item string (float) ftos + \item string (vector) vtos + \item string (entity) etos + \item float (string) stof + + \item void (string, ...) dprint + \item void (entity) eprint + + \item float (float) rint + \item float (float) floor + \item float (float) ceil + \item float (float) fabs + \item float (float) sin + \item float (float) cos + \item float (float) sqrt +\end{enumerate} +The numbers of which these built-ins are assigned is implementation-defined; +an implementation is allowed to use these built-ins however it sees fit. + +\pagebreak +%% -> Language %% +\section{Language} +\subsection{Notation} +The syntax notation used in this document is that of a BNF specification. A set of +derivation rules, often written as: +\begin{lstlisting}[language=bnf] + symbol ::= expression +\end{lstlisting} +Where symbol is a nonterminal, and the expression consists of one or more sequences of +symbols; more sequences are separated by a vertical bar \textbar, indicating a choice, +the whole being a possible substitution for the symbol on the left. Symbols that never +appear on the left side are terminals. +\linebreak + +This document defines language syntax throughout it's way at defining language +constructs If you're interested in a summary of the language syntax, one is given in +annex A. + +%% -> Concepts %% +\subsection{Concepts} +%% ->-> Scopes of identifiers %% +\subsubsection{Scopes of identifiers} +An identifier can denote an object; a function, or enumeration; a label name; a macro +name; or a macro parameter. The same identifier can denote different items at different +points in the program. A member of an enumeration is called an enumeration constant. +Macro names and macro parameters are not considered further here, because prior to the +semantic phase of program translation any occurrences of macro names in the source file +are replaced by the preprocessing token sequences that constitute their macro definitions. +\linebreak + +For each different item that an identifier designates, the identifier is visible (i.e, +can be used) only within a region of program text called its scope. Different items +designated by the same identifier either have different scopes, or are in different name +spaces. There are four kinds of scopes: function, file, block and function prototype. +(A function prototype is a declaration of a function that declares the types of its +parameters.) +\linebreak + +A label name is the only kind of identifier that has function scope. It can be used (in +a goto statement) anywhere in the function in which it appears, and is declared +implicitly by its syntactic appearance (prefixed by a colon :, and suffixed with a +statement). +\linebreak + +Every other identifier has scope determined by the placement of its declaration (in a +declarator or type specifier). If the declarator or type specifier that declares the +identifier appears outside any block or list of parameters, the identifier has file +scope, which terminates at the end of the file. If the declartor or type specifier that +declares the identifier appears inside a block or within the list of parameter +declarations in a function definition, the identifier has block scope, which terminates +at the end of the associated block. If the declarator or type specifier that declares +the identifier appears within the list of parameter declarations in a function prototype +(not part of a function definition), the identifier has function prototype scope, which +terminates at the end of the function declarator. If an identifier designates two +different items in the same name space, the scopes might overlap. If so, the scope of +one item (the inner scope) will be a strict subset of the scope of the other item (the +outer scope). Within the inner scope, the identifier designates the item declared in the +inner scope; the item declared in the outer scope is hidden (and not visible) within +the inner scope. +\linebreak + +Unless explicitly stated otherwise, where this document uses the term "identifier" to +refer to some item (as opposed to the syntactic construct), it refers to the item in the +relevant name space whose declaration is visible at the point the identifier occurs. +\linebreak + +Two identifiers have the same scope if and only if their scopes terminate at the same +point. +\linebreak + +Each enumeration constant has scope that begins just after the appearance of its defining +enumerator in an enumerator list. Any other identifier has scope that begins just after +the completion of its declarator. + +%% ->-> Name spaces of identifiers %% +\subsubsection{Name spaces of identifiers} +If more than one declaration of a particular identifier is visible at any point in a +source file, the syntactic context disambiguates uses that refer to different items. +Thus, there are separate name spaces for various categories of identifiers, as follows: +\linebreak +\begin{itemize} + \item Label names (disambiguated by the syntax of the label declaration and use); + \item Enumerations (disambiguated by following the keyword enum); + \item All other identifiers, called ordinary identifiers (declared in ordinary + declarators or as enumeration constants). +\end{itemize} + +%% ->-> Types %% +\subsubsection{Types} +The meaning of a value stored in an object returned by a function is determined by the +type of the expression used to access it. (An identifier declared to be an object is the simplest +such expression; the type is specified in the declaration of the identifier.) Types are +partitioned into object types (types that fully describe objects), function types(types +that describe functions), and incomplete types(types that describe objects but lack +information). +\linebreak + +An object declared type bool is large enough to store the values 0 and 1. +\linebreak + +An object declared type float is a real type; An object declared type vector is a +comprised set of three floats that respectively represent the \underline{x,y,z} +components of a three-dimensional vector. +\linebreak + +An enumeration comprises a set of named integer constant values. Each distinct +enumeration constitutes a different enumerated type. +\linebreak + +Enumeration types and float are collectively called arithmetic types. Each arithmetic +type belongs to one type domain. +\linebreak + +The void type comprises an empty set of values; it is an incomplete type that cannot be +completed. +\linebreak + +A number of derived types can be constructed from the object, function and incomplete +types, as follows: +\linebreak + +\begin{itemize} + \item An array type describes a contiguously allocated nonempty set of objects with a + particular object type, called the element type. Array types are characterized + by their element type and by the number of elements in the array. An array type + is said to be derived from its element type, and if its element is type T, the + array type is sometimes called "array of T". The construction of an array type + from an element type is called "array type derivation". + \item A function type describes a function with a specified return type. A function + type is characterized by its return type and the number and types of its + parameters. A function type is said to be derived from its return type, and if + its return type is T, the function type is sometimes called "function returning + T". The construction of a function type from a return type is called "function + type derivation". +\end{itemize} + +Arithmetic types are collectively called scalar types. Arrays and vectors are +collectively called aggregate types. +\linebreak + +An array of unknown size is an incomplete type. It is completed, for an identifier of +that type, by specifying the size in a later declaration. Arrays are required to have +known constant size. +\linebreak + +A type is characterized by its type category, which is either the outermost derivation +of a derived type (as noted above in the construction of derived types), or the type +itself if the type consists of no derived types. +\linebreak + +Any type so far mentioned is an unqualified type. Each unqualified type has several +qualified versions of its type, corresponding to the combinations of one, two, or all +two of const and volatile qualifiers. The qualified or unqualified versions of a type +are distinct types that belong to the same type category and have the same representation. +A derived type is not qualified by the qualifiers (if any) of the type from which it +is derived. +\linebreak + +%% ->-> Compatible types and composite type %% +\subsubsection{Compatible types and composite type} +Two types have compatible type if their types are the same. +\linebreak + +All declarations that refer to the same object or function shall have compatible type; +otherwise the behavior is undefined. +\linebreak + +A composite type can be constructed from two types that are compatible; it is a type that +is compatible with both of the two types and satisfies the following conditions: +\begin{itemize} + \item If one type is an array, the composite type is an array of that size. + \item If only one type is a function type with a parameter type list(a function + prototype), the composite type is a function prototype with the parameter type + list. + \item If both types are function types with parameter type lists, the type of each + parameter in the composite parameter type list is the composite type of the + corresponding parameters. +\end{itemize} +These rules apply recursively to types from which the two types are derived. +\linebreak + +%% ->Conversions %% +\subsection{Conversions} +Several operators convert operand values from one type to another automatically. This +sub-clause specifies the result required from such an implicit conversion. +\linebreak + +Conversion from an operand value to a compatible type causes no change to the value or +the representation. +\linebreak + +TODO: Specify all implicit conversions. + +%% ->->Aritmetic operands %% +\subsubsection{Arithmetic operands} +\paragraph*{Boolean type} +When any scalar value is converted to bool, the result is 0 if the value compares equal +to 0; otherwise the result is 1. + +%% ->->Other operands %% +\subsubsection{Other operands} +\paragraph{Lvalues, arrays and function designators} +An lvalue is an expression with an object type or an incomplete type other than void; +if an lvalue does not designate an object when it is evaluated, the behavior is undefined. +When an object is said to have a particular type, the type is specified by the lvalue +used to designate the object. A modifiable lvalue is an lvalue that does not have an +array type, does not have an incomplete type, and does not have a const-qualified type. +\linebreak + +Except when it is the operand of the unary \& operator, the ++ operator, the -- operator, +or the left operand of the . operator or an assignment operator, an lvalue that does not +have array type is converted to the value stored in the designated object (and is no +longer an lvalue). If the lvalue has qualified type, the value has the unqualified +version of the type of the lvalue; otherwise, the value has the type of the lvalue. If +the lvalue has an incomplete type and does not have array type, the behavior is undefined. +\linebreak + +A function designator is an expression that has function type. + +\paragraph*{void} +The (nonexistent) value of a void expression (an expression that has type void) shall not +be used in any way, and implicit conversions (except to void) shall not be applied to +such an expression. If an expression of any other type is evaluated as a void expression, +its value or designator is discarded. (A void expression is only evaluated for its +side effects.) +\pagebreak + +\subsection{Lexical elements} +\paragraph*{Syntax} +\begin{lstlisting}[language=bnf] +token ::= keyword + | identifier + | constant + | string-literal + | punctuator +preprocessing-token ::= header-name + | identifier + | pp-number + | string-literal + | punctuator +\end{lstlisting} +\paragraph*{Constraints} +Each preprocessing token that is converted to a token shall have the lexical form of a +keyword, an identifier, a constant, a string literal, or a punctuator. + +\paragraph*{Semantics} +A token is the minimal lexical element of the language in translation steps six and seven. +The categories of tokens are: keywords, identifiers, constants, string literals, and +punctuators. A preprocessing token is the minimal lexical element of the language in +translation steps three through five. The categories of preprocessing tokens are: header +names, identifiers, preprocessing numbers, string literals, punctuators and other single +non-white-space characters that do not lexically match the other preprocessing token +categories. If a ' or a " character matches the last category, the behavior is undefined. +Preprocessing tokens can be separated by white space; this consists of comments (described +later), or white-space characters (space, horizontal tab, new-line, vertical tab, and form +-feed), or both. In certain circumstances during translation step four, white space (or +the absence thereof) serves as more than preprocessing token separation. White space may +appear within a preprocessing token only as part of a header name or between the quotation +characters in a string literal. +\linebreak + +If the input stream has been parsed into preprocessing tokens up to a given character, the +next preprocessing token is the longest sequence of characters that could constitute a +preprocessing token. There is one exception to this rule: header name preprocessing tokens +are recognized only within \#include preprocessing directives and in implementation-defined +locations within \#pragma directives. In such contexts, a sequence of characters that +could be either a header name or string literal is recognized as the former. + +%% ->-> Keywords %% +\subsubsection{Keywords} +\paragraph*{Syntax} +\begin{lstlisting}[language=bnf] +keyword ::= enum | break | return | void + | case | float | volatile | for + | while | const | goto | bool + | continue | if | static | default + | inline | do | switch | else + | vector | entity +\end{lstlisting} +\paragraph*{Semantics} +The above tokens (case sensitive) are reserved (in translation step seven and eight) for +use as keywords, and shall not be used otherwise. + +%% ->->Identifiers %% +\subsubsection{Identifiers} +\begin{lstlisting}[language=bnf] +identifier ::= nondigit + | identifier nondigit + | identifier digit + +nondigit ::= _ | a | b | c | d | e | f | g | h | i + | j | k | l | m | n | o | p | q | r | s + | t | u | v | w | x | y | z | A | B | C + | D | E | F | G | H | I | J | K | L | M + | N | P | Q | R | S | T | U | V | W | X + | Y | Z + +digit ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 +\end{lstlisting} +\paragraph*{Semantics} +An identifier is a sequence of nondigit characters (including the underscore \_, the lower +case and upper case Latin letters, and other characters) and digits, which designates one +or more items. Lowercase and uppercase letters are distinct. There is a specific limit of +65535 characters for an identifier. +\linebreak + +When preprocessing tokens are converted to tokens during translation step six, if a +preprocessing token could not be converted to either a keyword or an identifier, it is +converted to a keyword. + +\paragraph*{Predefined identifiers} +Any identifiers that begin with the prefix \_\_builtin, or are within the reserved name +space are reserved by the implementation. + +%% ->->Constants %% +\subsubsection{Constants} +\begin{lstlisting}[language=bnf] +constant ::= integer-constant + | floating-constant + | enumeration-constant + | character-constant + | vector-constant + +integer-constant ::= decimal-constant + | octal-constant + | hexadecimal-constant + +decimal-constant ::= nonzero-digit + | decimal-constant digit + +octal-constant ::= 0 + | octal-constant octal-digit + +hexadecimal-constant ::= hexdecimal-prefix + hexadecimal-digit + | hexadecimal-digit + hexadecimal-constant + +hexadecimal-prefix: ::= 0x | 0X + +nonzero-digit ::= 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 + | 9 + +octal-digit ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 + +hexadecimal-digit ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 + | 8 | 9 | a | b | c | d | e | f + | A | B | C | D | E | F +\end{lstlisting} + +%% ->-> String literals %% +\subsubsection{String literals} +\begin{lstlisting}[language=bnf] +string-literal := " s-char-sequence " + +s-char-sequence := s-char + | s-char-sequence s-char + +s-char := ` | ! | @ | # | $ | % | ^ | & | * + | ( | ) | _ | - | + | = | { | } | [ + | ] | | | : | ; | ' | < | , | > | . + | ? | / | 1 | 2 | 3 | 4 | 5 | 6 | 7 + | 8 | 9 | 0 | q | w | e | r | t | y + | u | i | o | p | a | s | d | f | g + | h | j | k | l | z | x | c | v | b + | n | m | Q | W | E | R | T | Y | U + | I | O | P | A | S | D | F | G | | + | H | J | K | L | Z | X | C | V | B + | N | M +\end{lstlisting} +\paragraph*{Description} +A character string literal is a sequence of zero or more characters enclosed in +double-quotes, as in "xyz". +\linebreak + +The same considerations apply to each element of the sequence in a character string +literal as if it where an integer character constant, except that the single-quote +' is representable either by itself or by the escape sequence \textbackslash', but +the double-quote " shall be represented by the escape sequence \textbackslash". + +\paragraph*{Semantics} +In translation stage six, the character sequences specified by any sequence of adjacent +character string literal tokens are concatenated into a single character sequence. + +%% ->-> Punctuators %% +\subsubsection{Punctuators} +TODO: BNF + +A punctuator is a symbol that has independent syntactic and semantic significance. +Depending on context, it may specify an operation to be performed (which in turn +may yield a value or a function designator, produce a side effect, or some combination +thereof) in which case it is known as an operator (other forms of operator also exist +in some contexts). An operand is an item on which an operator acts. +\linebreak + +TODO: Trigraphs \& Digraphs + +\subsubsection{Header names} +TODO +\subsubsection{Preprocessing numbers} +TODO +\subsubsection{Comments} +Except within a character constant, a string literal, or a comment, the characters /* +introduce a comment. The contents of such a comment are examined only to identify +characters and to find the characters */ that terminate it. +\linebreak + +Except within a character constant, a string literal, or a comment, the characters // +introduce a comment that includes all characters up to, but not including, the next +new-line character. The contents of such a comment are examined only to identify +characters and to find the terminating new-line character. +\linebreak + +%% -> Expressions %% +\subsection{Expressions} +An expression is a sequence of operators and operands that specifies computation of a +value, or that designates an object or function, or that generates side effects, or that +performs a combination thereof. +\linebreak + +Between the previous and next sequence point an object shall have its stored value +modified at most once by the evaluation of an expression. Furthermore, the prior value +shall be read only to determine the value to be stored. +\linebreak + +The grouping of operators and operands is indicated by the syntax. Except as specified +later (for the function call (), \&\&, \textbar\textbar ?:, and comma operators), the +order of evaluation of sub-expressions and the order in which side effects take place +are both unspecified. +\linebreak + +Some operators (the unary \textasciitilde operator, and the binary operators \textless +\textless, \textgreater\textgreater, \&, \^, and \textbar, collectively describe bitwise +operators) are required to have operands that are either integer, or floating point with +zero points of decimal precision. +\linebreak + +If an exceptional condition occurs during the evaluation of an expression (that is, if +the result is not mathematically defined or not in the range or representable values for +its type), the behavior is undefined. + +%% ->-> Primary expressions %% +\subsubsection{Primary expressions} +\paragraph*{Syntax} +\begin{lstlisting}[language=bnf] +primary-expression ::= identifier + | constant + | string-literal + ( expression ) +\end{lstlisting} +\paragraph*{Semantics} +An identifier is a primary expression, provided it has been declared as designating an +object(in which case it is an lvalue) or a function(in which case it is a function +designator). +\linebreak + +A constant is a primary expression. Its type depends on its form and value. +\linebreak + +A string literal is a primary expression. It is an lvalue. +\linebreak + +A parenthesized expression is a primary expression. Its type and value identical to +those of the unparenthesized expression. It is an lvalue, a function designator, or a +void expression if the unparenthesized expression is, respectively, an lvalue, a +function designator, or a void expression. + +%% ->-> Constant expressions %% +\subsubsection{Constant expressions} +\paragraph*{Syntax} +\begin{lstlisting}[language=bnf] +constant-expression ::= conditional-expression +\end{lstlisting} +\paragraph*{Description} +A constant expression can be evaluated during translation rather than runtime, and +accordingly may be used in any place that a constant may be. +\paragraph*{Constraints} +\begin{itemize} + \item Constant expressions shall not contain assignment, increment, decrement, + function-call, or comma operators, except when contained within a subexpression + that is not evaluated. + \item Each constant expression shall evaluate to a constant that is in range of + representable values for its type. +\end{itemize} +\paragraph*{Semantics} +An expression that evaluates to a constant is required in several contexts. If a floating +point expression is evaluated in the translation environment, the arithmetic precision range +shall be as great is if the expression were being evaluated in the execution environment. +\linebreak + +An integer constant expression shall have integer type and shall only have operands that +are integer constants, enumeration constants, character constants, and floating constants +that are the immediate operand of casts. Cast operators in an integer constant expression +shall only convert arithmetic types to integer types. +\linebreak + +More latitude is permitted for constant expressions in initializers. Such a constant expression +shall be, or evaluate to an arithmetic constant expression. +\linebreak + +An arithmetic constant expression shall have arithmetic type and shall only have operands that +are integer constants, floating constants, enumeration constants, and character constants. Cast +operators in an arithmetic constant expression shall only convert arithmetic types to arithmetic +types. +\linebreak + +An implementation may accept other forms of constant expressions. +\linebreak + +The semantic rules for the evaluation of a constant expression are the same as for nonconstant +expressions. + + +\bibliographystyle{abbrv} +\bibliography{main} + +\end{document} diff --git a/misc/source/gmqcc-src/exec.c b/misc/source/gmqcc-src/exec.c new file mode 100644 index 00000000..6934a0c0 --- /dev/null +++ b/misc/source/gmqcc-src/exec.c @@ -0,0 +1,1624 @@ +/* + * Copyright (C) 2012, 2013 + * Wolfgang Bumiller + * Dale Weiler + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef QCVM_LOOP +#include +#include +#include +#include +#include + +#include "gmqcc.h" + +static void loaderror(const char *fmt, ...) +{ + int err = errno; + va_list ap; + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + printf(": %s\n", util_strerror(err)); +} + +static void qcvmerror(qc_program *prog, const char *fmt, ...) +{ + va_list ap; + + prog->vmerror++; + + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + putchar('\n'); +} + +qc_program* prog_load(const char *filename, bool skipversion) +{ + qc_program *prog; + prog_header header; + FILE *file = fs_file_open(filename, "rb"); + + if (!file) + return NULL; + + if (fs_file_read(&header, sizeof(header), 1, file) != 1) { + loaderror("failed to read header from '%s'", filename); + fs_file_close(file); + return NULL; + } + + if (!skipversion && header.version != 6) { + loaderror("header says this is a version %i progs, we need version 6\n", header.version); + fs_file_close(file); + return NULL; + } + + prog = (qc_program*)mem_a(sizeof(qc_program)); + if (!prog) { + fs_file_close(file); + fprintf(stderr, "failed to allocate program data\n"); + return NULL; + } + memset(prog, 0, sizeof(*prog)); + + prog->entityfields = header.entfield; + prog->crc16 = header.crc16; + + prog->filename = util_strdup(filename); + if (!prog->filename) { + loaderror("failed to store program name"); + goto error; + } + +#define read_data(hdrvar, progvar, reserved) \ + if (fs_file_seek(file, header.hdrvar.offset, SEEK_SET) != 0) { \ + loaderror("seek failed"); \ + goto error; \ + } \ + if (fs_file_read ( \ + vec_add(prog->progvar, header.hdrvar.length + reserved), \ + sizeof(*prog->progvar), \ + header.hdrvar.length, \ + file \ + )!= header.hdrvar.length \ + ) { \ + loaderror("read failed"); \ + goto error; \ + } +#define read_data1(x) read_data(x, x, 0) +#define read_data2(x, y) read_data(x, x, y) + + read_data (statements, code, 0); + read_data1(defs); + read_data1(fields); + read_data1(functions); + read_data1(strings); + read_data2(globals, 2); /* reserve more in case a RETURN using with the global at "the end" exists */ + + fs_file_close(file); + + /* profile counters */ + memset(vec_add(prog->profile, vec_size(prog->code)), 0, sizeof(prog->profile[0]) * vec_size(prog->code)); + + /* Add tempstring area */ + prog->tempstring_start = vec_size(prog->strings); + prog->tempstring_at = vec_size(prog->strings); + memset(vec_add(prog->strings, 16*1024), 0, 16*1024); + + /* spawn the world entity */ + vec_push(prog->entitypool, true); + memset(vec_add(prog->entitydata, prog->entityfields), 0, prog->entityfields * sizeof(prog->entitydata[0])); + prog->entities = 1; + + return prog; + +error: + if (prog->filename) + mem_d(prog->filename); + vec_free(prog->code); + vec_free(prog->defs); + vec_free(prog->fields); + vec_free(prog->functions); + vec_free(prog->strings); + vec_free(prog->globals); + vec_free(prog->entitydata); + vec_free(prog->entitypool); + mem_d(prog); + return NULL; +} + +void prog_delete(qc_program *prog) +{ + if (prog->filename) mem_d(prog->filename); + vec_free(prog->code); + vec_free(prog->defs); + vec_free(prog->fields); + vec_free(prog->functions); + vec_free(prog->strings); + vec_free(prog->globals); + vec_free(prog->entitydata); + vec_free(prog->entitypool); + vec_free(prog->localstack); + vec_free(prog->stack); + vec_free(prog->profile); + mem_d(prog); +} + +/*********************************************************************** + * VM code + */ + +const char* prog_getstring(qc_program *prog, qcint str) { + /* cast for return required for C++ */ + if (str < 0 || str >= (qcint)vec_size(prog->strings)) + return "<<>>"; + + return prog->strings + str; +} + +prog_section_def* prog_entfield(qc_program *prog, qcint off) { + size_t i; + for (i = 0; i < vec_size(prog->fields); ++i) { + if (prog->fields[i].offset == off) + return (prog->fields + i); + } + return NULL; +} + +prog_section_def* prog_getdef(qc_program *prog, qcint off) +{ + size_t i; + for (i = 0; i < vec_size(prog->defs); ++i) { + if (prog->defs[i].offset == off) + return (prog->defs + i); + } + return NULL; +} + +qcany* prog_getedict(qc_program *prog, qcint e) { + if (e >= (qcint)vec_size(prog->entitypool)) { + prog->vmerror++; + fprintf(stderr, "Accessing out of bounds edict %i\n", (int)e); + e = 0; + } + return (qcany*)(prog->entitydata + (prog->entityfields * e)); +} + +qcint prog_spawn_entity(qc_program *prog) { + char *data; + qcint e; + for (e = 0; e < (qcint)vec_size(prog->entitypool); ++e) { + if (!prog->entitypool[e]) { + data = (char*)(prog->entitydata + (prog->entityfields * e)); + memset(data, 0, prog->entityfields * sizeof(qcint)); + return e; + } + } + vec_push(prog->entitypool, true); + prog->entities++; + data = (char*)vec_add(prog->entitydata, prog->entityfields); + memset(data, 0, prog->entityfields * sizeof(qcint)); + return e; +} + +void prog_free_entity(qc_program *prog, qcint e) { + if (!e) { + prog->vmerror++; + fprintf(stderr, "Trying to free world entity\n"); + return; + } + if (e >= (qcint)vec_size(prog->entitypool)) { + prog->vmerror++; + fprintf(stderr, "Trying to free out of bounds entity\n"); + return; + } + if (!prog->entitypool[e]) { + prog->vmerror++; + fprintf(stderr, "Double free on entity\n"); + return; + } + prog->entitypool[e] = false; +} + +qcint prog_tempstring(qc_program *prog, const char *str) { + size_t len = strlen(str); + size_t at = prog->tempstring_at; + + /* when we reach the end we start over */ + if (at + len >= vec_size(prog->strings)) + at = prog->tempstring_start; + + /* when it doesn't fit, reallocate */ + if (at + len >= vec_size(prog->strings)) + { + (void)vec_add(prog->strings, len+1); + memcpy(prog->strings + at, str, len+1); + return at; + } + + /* when it fits, just copy */ + memcpy(prog->strings + at, str, len+1); + prog->tempstring_at += len+1; + return at; +} + +static size_t print_escaped_string(const char *str, size_t maxlen) { + size_t len = 2; + putchar('"'); + --maxlen; /* because we're lazy and have escape sequences */ + while (*str) { + if (len >= maxlen) { + putchar('.'); + putchar('.'); + putchar('.'); + len += 3; + break; + } + switch (*str) { + case '\a': len += 2; putchar('\\'); putchar('a'); break; + case '\b': len += 2; putchar('\\'); putchar('b'); break; + case '\r': len += 2; putchar('\\'); putchar('r'); break; + case '\n': len += 2; putchar('\\'); putchar('n'); break; + case '\t': len += 2; putchar('\\'); putchar('t'); break; + case '\f': len += 2; putchar('\\'); putchar('f'); break; + case '\v': len += 2; putchar('\\'); putchar('v'); break; + case '\\': len += 2; putchar('\\'); putchar('\\'); break; + case '"': len += 2; putchar('\\'); putchar('"'); break; + default: + ++len; + putchar(*str); + break; + } + ++str; + } + putchar('"'); + return len; +} + +static void trace_print_global(qc_program *prog, unsigned int glob, int vtype) { + static char spaces[28+1] = " "; + prog_section_def *def; + qcany *value; + int len; + + if (!glob) { + len = printf(","); + goto done; + } + + def = prog_getdef(prog, glob); + value = (qcany*)(&prog->globals[glob]); + + len = printf("[@%u] ", glob); + if (def) { + const char *name = prog_getstring(prog, def->name); + if (name[0] == '#') + len += printf("$"); + else + len += printf("%s ", name); + vtype = def->type & DEF_TYPEMASK; + } + + switch (vtype) { + case TYPE_VOID: + case TYPE_ENTITY: + case TYPE_FIELD: + case TYPE_FUNCTION: + case TYPE_POINTER: + len += printf("(%i),", value->_int); + break; + case TYPE_VECTOR: + len += printf("'%g %g %g',", value->vector[0], + value->vector[1], + value->vector[2]); + break; + case TYPE_STRING: + if (value->string) + len += print_escaped_string(prog_getstring(prog, value->string), sizeof(spaces)-len-5); + else + len += printf("(null)"); + len += printf(","); + /* len += printf("\"%s\",", prog_getstring(prog, value->string)); */ + break; + case TYPE_FLOAT: + default: + len += printf("%g,", value->_float); + break; + } +done: + if (len < (int)sizeof(spaces)-1) { + spaces[sizeof(spaces)-1-len] = 0; + fs_file_puts(stdout, spaces); + spaces[sizeof(spaces)-1-len] = ' '; + } +} + +static void prog_print_statement(qc_program *prog, prog_section_statement *st) { + if (st->opcode >= (sizeof(asm_instr)/sizeof(asm_instr[0]))) { + printf("\n", st->opcode); + return; + } + if ((prog->xflags & VMXF_TRACE) && vec_size(prog->function_stack)) { + size_t i; + for (i = 0; i < vec_size(prog->function_stack); ++i) + printf("->"); + printf("%s:", vec_last(prog->function_stack)); + } + printf(" <> %-12s", asm_instr[st->opcode].m); + if (st->opcode >= INSTR_IF && + st->opcode <= INSTR_IFNOT) + { + trace_print_global(prog, st->o1.u1, TYPE_FLOAT); + printf("%d\n", st->o2.s1); + } + else if (st->opcode >= INSTR_CALL0 && + st->opcode <= INSTR_CALL8) + { + trace_print_global(prog, st->o1.u1, TYPE_FUNCTION); + printf("\n"); + } + else if (st->opcode == INSTR_GOTO) + { + printf("%i\n", st->o1.s1); + } + else + { + int t[3] = { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT }; + switch (st->opcode) + { + case INSTR_MUL_FV: + t[1] = t[2] = TYPE_VECTOR; + break; + case INSTR_MUL_VF: + t[0] = t[2] = TYPE_VECTOR; + break; + case INSTR_MUL_V: + t[0] = t[1] = TYPE_VECTOR; + break; + case INSTR_ADD_V: + case INSTR_SUB_V: + case INSTR_EQ_V: + case INSTR_NE_V: + t[0] = t[1] = t[2] = TYPE_VECTOR; + break; + case INSTR_EQ_S: + case INSTR_NE_S: + t[0] = t[1] = TYPE_STRING; + break; + case INSTR_STORE_F: + case INSTR_STOREP_F: + t[2] = -1; + break; + case INSTR_STORE_V: + t[0] = t[1] = TYPE_VECTOR; t[2] = -1; + break; + case INSTR_STORE_S: + t[0] = t[1] = TYPE_STRING; t[2] = -1; + break; + case INSTR_STORE_ENT: + t[0] = t[1] = TYPE_ENTITY; t[2] = -1; + break; + case INSTR_STORE_FLD: + t[0] = t[1] = TYPE_FIELD; t[2] = -1; + break; + case INSTR_STORE_FNC: + t[0] = t[1] = TYPE_FUNCTION; t[2] = -1; + break; + case INSTR_STOREP_V: + t[0] = TYPE_VECTOR; t[1] = TYPE_ENTITY; t[2] = -1; + break; + case INSTR_STOREP_S: + t[0] = TYPE_STRING; t[1] = TYPE_ENTITY; t[2] = -1; + break; + case INSTR_STOREP_ENT: + t[0] = TYPE_ENTITY; t[1] = TYPE_ENTITY; t[2] = -1; + break; + case INSTR_STOREP_FLD: + t[0] = TYPE_FIELD; t[1] = TYPE_ENTITY; t[2] = -1; + break; + case INSTR_STOREP_FNC: + t[0] = TYPE_FUNCTION; t[1] = TYPE_ENTITY; t[2] = -1; + break; + } + if (t[0] >= 0) trace_print_global(prog, st->o1.u1, t[0]); + else printf("(none), "); + if (t[1] >= 0) trace_print_global(prog, st->o2.u1, t[1]); + else printf("(none), "); + if (t[2] >= 0) trace_print_global(prog, st->o3.u1, t[2]); + else printf("(none)"); + printf("\n"); + } +} + +static qcint prog_enterfunction(qc_program *prog, prog_section_function *func) { + qc_exec_stack st; + size_t parampos; + int32_t p; + + /* back up locals */ + st.localsp = vec_size(prog->localstack); + st.stmt = prog->statement; + st.function = func; + + if (prog->xflags & VMXF_TRACE) { + const char *str = prog_getstring(prog, func->name); + vec_push(prog->function_stack, str); + } + +#ifdef QCVM_BACKUP_STRATEGY_CALLER_VARS + if (vec_size(prog->stack)) + { + prog_section_function *cur; + cur = prog->stack[vec_size(prog->stack)-1].function; + if (cur) + { + qcint *globals = prog->globals + cur->firstlocal; + vec_append(prog->localstack, cur->locals, globals); + } + } +#else + { + qcint *globals = prog->globals + func->firstlocal; + vec_append(prog->localstack, func->locals, globals); + } +#endif + + /* copy parameters */ + parampos = func->firstlocal; + for (p = 0; p < func->nargs; ++p) + { + size_t s; + for (s = 0; s < func->argsize[p]; ++s) { + prog->globals[parampos] = prog->globals[OFS_PARM0 + 3*p + s]; + ++parampos; + } + } + + vec_push(prog->stack, st); + + return func->entry; +} + +static qcint prog_leavefunction(qc_program *prog) { + prog_section_function *prev = NULL; + size_t oldsp; + + qc_exec_stack st = vec_last(prog->stack); + + if (prog->xflags & VMXF_TRACE) { + if (vec_size(prog->function_stack)) + vec_pop(prog->function_stack); + } + +#ifdef QCVM_BACKUP_STRATEGY_CALLER_VARS + if (vec_size(prog->stack) > 1) { + prev = prog->stack[vec_size(prog->stack)-2].function; + oldsp = prog->stack[vec_size(prog->stack)-2].localsp; + } +#else + prev = prog->stack[vec_size(prog->stack)-1].function; + oldsp = prog->stack[vec_size(prog->stack)-1].localsp; +#endif + if (prev) { + qcint *globals = prog->globals + prev->firstlocal; + memcpy(globals, prog->localstack + oldsp, prev->locals * sizeof(prog->localstack[0])); + /* vec_remove(prog->localstack, oldsp, vec_size(prog->localstack)-oldsp); */ + vec_shrinkto(prog->localstack, oldsp); + } + + vec_pop(prog->stack); + + return st.stmt - 1; /* offset the ++st */ +} + +bool prog_exec(qc_program *prog, prog_section_function *func, size_t flags, long maxjumps) { + long jumpcount = 0; + size_t oldxflags = prog->xflags; + prog_section_statement *st; + + prog->vmerror = 0; + prog->xflags = flags; + + st = prog->code + prog_enterfunction(prog, func); + --st; + switch (flags) + { + default: + case 0: + { +#define QCVM_LOOP 1 +#define QCVM_PROFILE 0 +#define QCVM_TRACE 0 +# include __FILE__ + } + case (VMXF_TRACE): + { +#define QCVM_PROFILE 0 +#define QCVM_TRACE 1 +# include __FILE__ + } + case (VMXF_PROFILE): + { +#define QCVM_PROFILE 1 +#define QCVM_TRACE 0 +# include __FILE__ + } + case (VMXF_TRACE|VMXF_PROFILE): + { +#define QCVM_PROFILE 1 +#define QCVM_TRACE 1 +# include __FILE__ + } + }; + +cleanup: + prog->xflags = oldxflags; + vec_free(prog->localstack); + vec_free(prog->stack); + if (prog->vmerror) + return false; + return true; +} + +/*********************************************************************** + * main for when building the standalone executor + */ + +#if defined(QCVM_EXECUTOR) +#include + +opts_cmd_t opts; + +const char *type_name[TYPE_COUNT] = { + "void", + "string", + "float", + "vector", + "entity", + "field", + "function", + "pointer", + "integer", + + "variant", + + "struct", + "union", + "array", + + "nil", + "noexpr" +}; + +typedef struct { + int vtype; + const char *value; +} qcvm_parameter; + +static qcvm_parameter *main_params = NULL; + +#define CheckArgs(num) do { \ + if (prog->argc != (num)) { \ + prog->vmerror++; \ + fprintf(stderr, "ERROR: invalid number of arguments for %s: %i, expected %i\n", \ + __FUNCTION__, prog->argc, (num)); \ + return -1; \ + } \ +} while (0) + +#define GetGlobal(idx) ((qcany*)(prog->globals + (idx))) +#define GetArg(num) GetGlobal(OFS_PARM0 + 3*(num)) +#define Return(any) *(GetGlobal(OFS_RETURN)) = (any) + +static int qc_print(qc_program *prog) { + size_t i; + const char *laststr = NULL; + for (i = 0; i < (size_t)prog->argc; ++i) { + qcany *str = (qcany*)(prog->globals + OFS_PARM0 + 3*i); + laststr = prog_getstring(prog, str->string); + printf("%s", laststr); + } + if (laststr && (prog->xflags & VMXF_TRACE)) { + size_t len = strlen(laststr); + if (!len || laststr[len-1] != '\n') + printf("\n"); + } + return 0; +} + +static int qc_error(qc_program *prog) { + fprintf(stderr, "*** VM raised an error:\n"); + qc_print(prog); + prog->vmerror++; + return -1; +} + +static int qc_ftos(qc_program *prog) { + char buffer[512]; + qcany *num; + qcany str; + CheckArgs(1); + num = GetArg(0); + util_snprintf(buffer, sizeof(buffer), "%g", num->_float); + str.string = prog_tempstring(prog, buffer); + Return(str); + return 0; +} + +static int qc_stof(qc_program *prog) { + qcany *str; + qcany num; + CheckArgs(1); + str = GetArg(0); + num._float = (float)strtod(prog_getstring(prog, str->string), NULL); + Return(num); + return 0; +} + +static int qc_vtos(qc_program *prog) { + char buffer[512]; + qcany *num; + qcany str; + CheckArgs(1); + num = GetArg(0); + util_snprintf(buffer, sizeof(buffer), "'%g %g %g'", num->vector[0], num->vector[1], num->vector[2]); + str.string = prog_tempstring(prog, buffer); + Return(str); + return 0; +} + +static int qc_etos(qc_program *prog) { + char buffer[512]; + qcany *num; + qcany str; + CheckArgs(1); + num = GetArg(0); + util_snprintf(buffer, sizeof(buffer), "%i", num->_int); + str.string = prog_tempstring(prog, buffer); + Return(str); + return 0; +} + +static int qc_spawn(qc_program *prog) { + qcany ent; + CheckArgs(0); + ent.edict = prog_spawn_entity(prog); + Return(ent); + return (ent.edict ? 0 : -1); +} + +static int qc_kill(qc_program *prog) { + qcany *ent; + CheckArgs(1); + ent = GetArg(0); + prog_free_entity(prog, ent->edict); + return 0; +} + +static int qc_sqrt(qc_program *prog) { + qcany *num, out; + CheckArgs(1); + num = GetArg(0); + out._float = sqrt(num->_float); + Return(out); + return 0; +} + +static int qc_vlen(qc_program *prog) { + qcany *vec, len; + CheckArgs(1); + vec = GetArg(0); + len._float = sqrt(vec->vector[0] * vec->vector[0] + + vec->vector[1] * vec->vector[1] + + vec->vector[2] * vec->vector[2]); + Return(len); + return 0; +} + +static int qc_normalize(qc_program *prog) { + double len; + qcany *vec; + qcany out; + CheckArgs(1); + vec = GetArg(0); + len = sqrt(vec->vector[0] * vec->vector[0] + + vec->vector[1] * vec->vector[1] + + vec->vector[2] * vec->vector[2]); + if (len) + len = 1.0 / len; + else + len = 0; + out.vector[0] = len * vec->vector[0]; + out.vector[1] = len * vec->vector[1]; + out.vector[2] = len * vec->vector[2]; + Return(out); + return 0; +} + +static int qc_strcat(qc_program *prog) { + char *buffer; + size_t len1, len2; + qcany *str1, *str2; + qcany out; + + const char *cstr1; + const char *cstr2; + + CheckArgs(2); + str1 = GetArg(0); + str2 = GetArg(1); + cstr1 = prog_getstring(prog, str1->string); + cstr2 = prog_getstring(prog, str2->string); + len1 = strlen(cstr1); + len2 = strlen(cstr2); + buffer = (char*)mem_a(len1 + len2 + 1); + memcpy(buffer, cstr1, len1); + memcpy(buffer+len1, cstr2, len2+1); + out.string = prog_tempstring(prog, buffer); + mem_d(buffer); + Return(out); + return 0; +} + +static int qc_strcmp(qc_program *prog) { + qcany *str1, *str2; + qcany out; + + const char *cstr1; + const char *cstr2; + + if (prog->argc != 2 && prog->argc != 3) { + fprintf(stderr, "ERROR: invalid number of arguments for strcmp/strncmp: %i, expected 2 or 3\n", + prog->argc); + return -1; + } + + str1 = GetArg(0); + str2 = GetArg(1); + cstr1 = prog_getstring(prog, str1->string); + cstr2 = prog_getstring(prog, str2->string); + if (prog->argc == 3) + out._float = strncmp(cstr1, cstr2, GetArg(2)->_float); + else + out._float = strcmp(cstr1, cstr2); + Return(out); + return 0; +} + +static int qc_floor(qc_program *prog) { + qcany *num, out; + CheckArgs(1); + num = GetArg(0); + out._float = floor(num->_float); + Return(out); + return 0; +} + +static prog_builtin qc_builtins[] = { + NULL, + &qc_print, /* 1 */ + &qc_ftos, /* 2 */ + &qc_spawn, /* 3 */ + &qc_kill, /* 4 */ + &qc_vtos, /* 5 */ + &qc_error, /* 6 */ + &qc_vlen, /* 7 */ + &qc_etos, /* 8 */ + &qc_stof, /* 9 */ + &qc_strcat, /* 10 */ + &qc_strcmp, /* 11 */ + &qc_normalize, /* 12 */ + &qc_sqrt, /* 13 */ + &qc_floor /* 14 */ +}; +static size_t qc_builtins_count = sizeof(qc_builtins) / sizeof(qc_builtins[0]); + +static const char *arg0 = NULL; + +static void version(void) { + printf("GMQCC-QCVM %d.%d.%d Built %s %s\n", + GMQCC_VERSION_MAJOR, + GMQCC_VERSION_MINOR, + GMQCC_VERSION_PATCH, + __DATE__, + __TIME__ + ); +} + +static void usage(void) { + printf("usage: %s [options] [parameters] file\n", arg0); + printf("options:\n"); + printf(" -h, --help print this message\n" + " -trace trace the execution\n" + " -profile perform profiling during execution\n" + " -info print information from the prog's header\n" + " -disasm disassemble and exit\n" + " -disasm-func func disassemble and exit\n" + " -printdefs list the defs section\n" + " -printfields list the field section\n" + " -printfuns list functions information\n" + " -v be verbose\n" + " -vv be even more verbose\n"); + printf("parameters:\n"); + printf(" -vector pass a vector parameter to main()\n" + " -float pass a float parameter to main()\n" + " -string pass a string parameter to main() \n"); +} + +static void prog_main_setparams(qc_program *prog) { + size_t i; + qcany *arg; + + for (i = 0; i < vec_size(main_params); ++i) { + arg = GetGlobal(OFS_PARM0 + 3*i); + arg->vector[0] = 0; + arg->vector[1] = 0; + arg->vector[2] = 0; + switch (main_params[i].vtype) { + case TYPE_VECTOR: +#ifdef _MSC_VER + (void)sscanf_s(main_params[i].value, " %f %f %f ", + &arg->vector[0], + &arg->vector[1], + &arg->vector[2]); +#else + (void)sscanf(main_params[i].value, " %f %f %f ", + &arg->vector[0], + &arg->vector[1], + &arg->vector[2]); +#endif + break; + case TYPE_FLOAT: + arg->_float = atof(main_params[i].value); + break; + case TYPE_STRING: + arg->string = prog_tempstring(prog, main_params[i].value); + break; + default: + fprintf(stderr, "error: unhandled parameter type: %i\n", main_params[i].vtype); + break; + } + } +} + +void escapestring(char* dest, const char* src) { + char c; + while ((c = *(src++))) { + switch(c) { + case '\t': + *(dest++) = '\\', *(dest++) = 't'; + break; + case '\n': + *(dest++) = '\\', *(dest++) = 'n'; + break; + case '\r': + *(dest++) = '\\', *(dest++) = 'r'; + break; + case '\\': + *(dest++) = '\\', *(dest++) = '\\'; + break; + case '\"': + *(dest++) = '\\', *(dest++) = '\"'; + break; + default: + *(dest++) = c; + } + } + *dest = '\0'; +} + +void prog_disasm_function(qc_program *prog, size_t id); + +int main(int argc, char **argv) { + size_t i; + qcint fnmain = -1; + qc_program *prog; + size_t xflags = VMXF_DEFAULT; + bool opts_printfields = false; + bool opts_printdefs = false; + bool opts_printfuns = false; + bool opts_disasm = false; + bool opts_info = false; + bool noexec = false; + const char *progsfile = NULL; + const char **dis_list = NULL; + int opts_v = 0; + + arg0 = argv[0]; + + if (argc < 2) { + usage(); + exit(1); + } + + while (argc > 1) { + if (!strcmp(argv[1], "-h") || + !strcmp(argv[1], "-help") || + !strcmp(argv[1], "--help")) + { + usage(); + exit(0); + } + else if (!strcmp(argv[1], "-v")) { + ++opts_v; + --argc; + ++argv; + } + else if (!strncmp(argv[1], "-vv", 3)) { + const char *av = argv[1]+1; + for (; *av; ++av) { + if (*av == 'v') + ++opts_v; + else { + usage(); + exit(1); + } + } + --argc; + ++argv; + } + else if (!strcmp(argv[1], "-version") || + !strcmp(argv[1], "--version")) + { + version(); + exit(0); + } + else if (!strcmp(argv[1], "-trace")) { + --argc; + ++argv; + xflags |= VMXF_TRACE; + } + else if (!strcmp(argv[1], "-profile")) { + --argc; + ++argv; + xflags |= VMXF_PROFILE; + } + else if (!strcmp(argv[1], "-info")) { + --argc; + ++argv; + opts_info = true; + noexec = true; + } + else if (!strcmp(argv[1], "-disasm")) { + --argc; + ++argv; + opts_disasm = true; + noexec = true; + } + else if (!strcmp(argv[1], "-disasm-func")) { + --argc; + ++argv; + if (argc <= 1) { + usage(); + exit(1); + } + vec_push(dis_list, argv[1]); + --argc; + ++argv; + noexec = true; + } + else if (!strcmp(argv[1], "-printdefs")) { + --argc; + ++argv; + opts_printdefs = true; + noexec = true; + } + else if (!strcmp(argv[1], "-printfuns")) { + --argc; + ++argv; + opts_printfuns = true; + noexec = true; + } + else if (!strcmp(argv[1], "-printfields")) { + --argc; + ++argv; + opts_printfields = true; + noexec = true; + } + else if (!strcmp(argv[1], "-vector") || + !strcmp(argv[1], "-string") || + !strcmp(argv[1], "-float") ) + { + qcvm_parameter p; + if (argv[1][1] == 'f') + p.vtype = TYPE_FLOAT; + else if (argv[1][1] == 's') + p.vtype = TYPE_STRING; + else if (argv[1][1] == 'v') + p.vtype = TYPE_VECTOR; + + --argc; + ++argv; + if (argc < 2) { + usage(); + exit(1); + } + p.value = argv[1]; + + vec_push(main_params, p); + --argc; + ++argv; + } + else if (!strcmp(argv[1], "--")) { + --argc; + ++argv; + break; + } + else if (argv[1][0] != '-') { + if (progsfile) { + fprintf(stderr, "only 1 program file may be specified\n"); + usage(); + exit(1); + } + progsfile = argv[1]; + --argc; + ++argv; + } + else + { + fprintf(stderr, "unknown parameter: %s\n", argv[1]); + usage(); + exit(1); + } + } + + if (argc == 2 && !progsfile) { + progsfile = argv[1]; + --argc; + ++argv; + } + + if (!progsfile) { + fprintf(stderr, "must specify a program to execute\n"); + usage(); + exit(1); + } + + prog = prog_load(progsfile, noexec); + if (!prog) { + fprintf(stderr, "failed to load program '%s'\n", progsfile); + exit(1); + } + + prog->builtins = qc_builtins; + prog->builtins_count = qc_builtins_count; + + if (opts_info) { + printf("Program's system-checksum = 0x%04x\n", (unsigned int)prog->crc16); + printf("Entity field space: %u\n", (unsigned int)prog->entityfields); + printf("Globals: %u\n", (unsigned int)vec_size(prog->globals)); + printf("Counts:\n" + " code: %lu\n" + " defs: %lu\n" + " fields: %lu\n" + " functions: %lu\n" + " strings: %lu\n", + (unsigned long)vec_size(prog->code), + (unsigned long)vec_size(prog->defs), + (unsigned long)vec_size(prog->fields), + (unsigned long)vec_size(prog->functions), + (unsigned long)vec_size(prog->strings)); + } + + if (opts_info) { + prog_delete(prog); + return 0; + } + for (i = 0; i < vec_size(dis_list); ++i) { + size_t k; + printf("Looking for `%s`\n", dis_list[i]); + for (k = 1; k < vec_size(prog->functions); ++k) { + const char *name = prog_getstring(prog, prog->functions[k].name); + if (!strcmp(name, dis_list[i])) { + prog_disasm_function(prog, k); + break; + } + } + } + if (opts_disasm) { + for (i = 1; i < vec_size(prog->functions); ++i) + prog_disasm_function(prog, i); + return 0; + } + if (opts_printdefs) { + char *escape = NULL; + const char *getstring = NULL; + + for (i = 0; i < vec_size(prog->defs); ++i) { + printf("Global: %8s %-16s at %u%s", + type_name[prog->defs[i].type & DEF_TYPEMASK], + prog_getstring(prog, prog->defs[i].name), + (unsigned int)prog->defs[i].offset, + ((prog->defs[i].type & DEF_SAVEGLOBAL) ? " [SAVE]" : "")); + if (opts_v) { + switch (prog->defs[i].type & DEF_TYPEMASK) { + case TYPE_FLOAT: + printf(" [init: %g]", ((qcany*)(prog->globals + prog->defs[i].offset))->_float); + break; + case TYPE_INTEGER: + printf(" [init: %i]", (int)( ((qcany*)(prog->globals + prog->defs[i].offset))->_int )); + break; + case TYPE_ENTITY: + case TYPE_FUNCTION: + case TYPE_FIELD: + case TYPE_POINTER: + printf(" [init: %u]", (unsigned)( ((qcany*)(prog->globals + prog->defs[i].offset))->_int )); + break; + case TYPE_STRING: + getstring = prog_getstring(prog, ((qcany*)(prog->globals + prog->defs[i].offset))->string); + escape = (char*)mem_a(strlen(getstring) * 2 + 1); /* will be enough */ + escapestring(escape, getstring); + printf(" [init: `%s`]", escape); + + mem_d(escape); /* free */ + break; + default: + break; + } + } + printf("\n"); + } + } + if (opts_printfields) { + for (i = 0; i < vec_size(prog->fields); ++i) { + printf("Field: %8s %-16s at %u%s\n", + type_name[prog->fields[i].type], + prog_getstring(prog, prog->fields[i].name), + (unsigned int)prog->fields[i].offset, + ((prog->fields[i].type & DEF_SAVEGLOBAL) ? " [SAVE]" : "")); + } + } + if (opts_printfuns) { + for (i = 0; i < vec_size(prog->functions); ++i) { + int32_t a; + printf("Function: %-16s taking %i parameters:(", + prog_getstring(prog, prog->functions[i].name), + (unsigned int)prog->functions[i].nargs); + for (a = 0; a < prog->functions[i].nargs; ++a) { + printf(" %i", prog->functions[i].argsize[a]); + } + if (opts_v > 1) { + int32_t start = prog->functions[i].entry; + if (start < 0) + printf(") builtin %i\n", (int)-start); + else { + size_t funsize = 0; + prog_section_statement *st = prog->code + start; + for (;st->opcode != INSTR_DONE; ++st) + ++funsize; + printf(") - %lu instructions", (unsigned long)funsize); + if (opts_v > 2) { + printf(" - locals: %i + %i\n", + prog->functions[i].firstlocal, + prog->functions[i].locals); + } + else + printf("\n"); + } + } + else if (opts_v) { + printf(") locals: %i + %i\n", + prog->functions[i].firstlocal, + prog->functions[i].locals); + } + else + printf(")\n"); + } + } + if (!noexec) { + for (i = 1; i < vec_size(prog->functions); ++i) { + const char *name = prog_getstring(prog, prog->functions[i].name); + if (!strcmp(name, "main")) + fnmain = (qcint)i; + } + if (fnmain > 0) + { + prog_main_setparams(prog); + prog_exec(prog, &prog->functions[fnmain], xflags, VM_JUMPS_DEFAULT); + } + else + fprintf(stderr, "No main function found\n"); + } + + prog_delete(prog); + return 0; +} + +void prog_disasm_function(qc_program *prog, size_t id) { + prog_section_function *fdef = prog->functions + id; + prog_section_statement *st; + + if (fdef->entry < 0) { + printf("FUNCTION \"%s\" = builtin #%i\n", prog_getstring(prog, fdef->name), (int)-fdef->entry); + return; + } + else + printf("FUNCTION \"%s\"\n", prog_getstring(prog, fdef->name)); + + st = prog->code + fdef->entry; + while (st->opcode != INSTR_DONE) { + prog_print_statement(prog, st); + ++st; + } +} +#endif +#else /* !QCVM_LOOP */ +/* + * Everything from here on is not including into the compilation of the + * executor. This is simply code that is #included via #include __FILE__ + * see when QCVM_LOOP is defined, the rest of the code above do not get + * re-included. So this really just acts like one large macro, but it + * sort of isn't, which makes it nicer looking. + */ + +#define OPA ( (qcany*) (prog->globals + st->o1.u1) ) +#define OPB ( (qcany*) (prog->globals + st->o2.u1) ) +#define OPC ( (qcany*) (prog->globals + st->o3.u1) ) + +#define GLOBAL(x) ( (qcany*) (prog->globals + (x)) ) + +/* to be consistent with current darkplaces behaviour */ +#if !defined(FLOAT_IS_TRUE_FOR_INT) +# define FLOAT_IS_TRUE_FOR_INT(x) ( (x) & 0x7FFFFFFF ) +#endif + +while (1) { + prog_section_function *newf; + qcany *ed; + qcany *ptr; + + ++st; + +#if QCVM_PROFILE + prog->profile[st - prog->code]++; +#endif + +#if QCVM_TRACE + prog_print_statement(prog, st); +#endif + + switch (st->opcode) + { + default: + qcvmerror(prog, "Illegal instruction in %s\n", prog->filename); + goto cleanup; + + case INSTR_DONE: + case INSTR_RETURN: + /* TODO: add instruction count to function profile count */ + GLOBAL(OFS_RETURN)->ivector[0] = OPA->ivector[0]; + GLOBAL(OFS_RETURN)->ivector[1] = OPA->ivector[1]; + GLOBAL(OFS_RETURN)->ivector[2] = OPA->ivector[2]; + + st = prog->code + prog_leavefunction(prog); + if (!vec_size(prog->stack)) + goto cleanup; + + break; + + case INSTR_MUL_F: + OPC->_float = OPA->_float * OPB->_float; + break; + case INSTR_MUL_V: + OPC->_float = OPA->vector[0]*OPB->vector[0] + + OPA->vector[1]*OPB->vector[1] + + OPA->vector[2]*OPB->vector[2]; + break; + case INSTR_MUL_FV: + { + qcfloat f = OPA->_float; + OPC->vector[0] = f * OPB->vector[0]; + OPC->vector[1] = f * OPB->vector[1]; + OPC->vector[2] = f * OPB->vector[2]; + break; + } + case INSTR_MUL_VF: + { + qcfloat f = OPB->_float; + OPC->vector[0] = f * OPA->vector[0]; + OPC->vector[1] = f * OPA->vector[1]; + OPC->vector[2] = f * OPA->vector[2]; + break; + } + case INSTR_DIV_F: + if (OPB->_float != 0.0f) + OPC->_float = OPA->_float / OPB->_float; + else + OPC->_float = 0; + break; + + case INSTR_ADD_F: + OPC->_float = OPA->_float + OPB->_float; + break; + case INSTR_ADD_V: + OPC->vector[0] = OPA->vector[0] + OPB->vector[0]; + OPC->vector[1] = OPA->vector[1] + OPB->vector[1]; + OPC->vector[2] = OPA->vector[2] + OPB->vector[2]; + break; + case INSTR_SUB_F: + OPC->_float = OPA->_float - OPB->_float; + break; + case INSTR_SUB_V: + OPC->vector[0] = OPA->vector[0] - OPB->vector[0]; + OPC->vector[1] = OPA->vector[1] - OPB->vector[1]; + OPC->vector[2] = OPA->vector[2] - OPB->vector[2]; + break; + + case INSTR_EQ_F: + OPC->_float = (OPA->_float == OPB->_float); + break; + case INSTR_EQ_V: + OPC->_float = ((OPA->vector[0] == OPB->vector[0]) && + (OPA->vector[1] == OPB->vector[1]) && + (OPA->vector[2] == OPB->vector[2]) ); + break; + case INSTR_EQ_S: + OPC->_float = !strcmp(prog_getstring(prog, OPA->string), + prog_getstring(prog, OPB->string)); + break; + case INSTR_EQ_E: + OPC->_float = (OPA->_int == OPB->_int); + break; + case INSTR_EQ_FNC: + OPC->_float = (OPA->function == OPB->function); + break; + case INSTR_NE_F: + OPC->_float = (OPA->_float != OPB->_float); + break; + case INSTR_NE_V: + OPC->_float = ((OPA->vector[0] != OPB->vector[0]) || + (OPA->vector[1] != OPB->vector[1]) || + (OPA->vector[2] != OPB->vector[2]) ); + break; + case INSTR_NE_S: + OPC->_float = !!strcmp(prog_getstring(prog, OPA->string), + prog_getstring(prog, OPB->string)); + break; + case INSTR_NE_E: + OPC->_float = (OPA->_int != OPB->_int); + break; + case INSTR_NE_FNC: + OPC->_float = (OPA->function != OPB->function); + break; + + case INSTR_LE: + OPC->_float = (OPA->_float <= OPB->_float); + break; + case INSTR_GE: + OPC->_float = (OPA->_float >= OPB->_float); + break; + case INSTR_LT: + OPC->_float = (OPA->_float < OPB->_float); + break; + case INSTR_GT: + OPC->_float = (OPA->_float > OPB->_float); + break; + + case INSTR_LOAD_F: + case INSTR_LOAD_S: + case INSTR_LOAD_FLD: + case INSTR_LOAD_ENT: + case INSTR_LOAD_FNC: + if (OPA->edict < 0 || OPA->edict >= prog->entities) { + qcvmerror(prog, "progs `%s` attempted to read an out of bounds entity", prog->filename); + goto cleanup; + } + if ((unsigned int)(OPB->_int) >= (unsigned int)(prog->entityfields)) { + qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)", + prog->filename, + OPB->_int); + goto cleanup; + } + ed = prog_getedict(prog, OPA->edict); + OPC->_int = ((qcany*)( ((qcint*)ed) + OPB->_int ))->_int; + break; + case INSTR_LOAD_V: + if (OPA->edict < 0 || OPA->edict >= prog->entities) { + qcvmerror(prog, "progs `%s` attempted to read an out of bounds entity", prog->filename); + goto cleanup; + } + if (OPB->_int < 0 || OPB->_int + 3 > (qcint)prog->entityfields) + { + qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)", + prog->filename, + OPB->_int + 2); + goto cleanup; + } + ed = prog_getedict(prog, OPA->edict); + ptr = (qcany*)( ((qcint*)ed) + OPB->_int ); + OPC->ivector[0] = ptr->ivector[0]; + OPC->ivector[1] = ptr->ivector[1]; + OPC->ivector[2] = ptr->ivector[2]; + break; + + case INSTR_ADDRESS: + if (OPA->edict < 0 || OPA->edict >= prog->entities) { + qcvmerror(prog, "prog `%s` attempted to address an out of bounds entity %i", prog->filename, OPA->edict); + goto cleanup; + } + if ((unsigned int)(OPB->_int) >= (unsigned int)(prog->entityfields)) + { + qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)", + prog->filename, + OPB->_int); + goto cleanup; + } + + ed = prog_getedict(prog, OPA->edict); + OPC->_int = ((qcint*)ed) - prog->entitydata + OPB->_int; + break; + + case INSTR_STORE_F: + case INSTR_STORE_S: + case INSTR_STORE_ENT: + case INSTR_STORE_FLD: + case INSTR_STORE_FNC: + OPB->_int = OPA->_int; + break; + case INSTR_STORE_V: + OPB->ivector[0] = OPA->ivector[0]; + OPB->ivector[1] = OPA->ivector[1]; + OPB->ivector[2] = OPA->ivector[2]; + break; + + case INSTR_STOREP_F: + case INSTR_STOREP_S: + case INSTR_STOREP_ENT: + case INSTR_STOREP_FLD: + case INSTR_STOREP_FNC: + if (OPB->_int < 0 || OPB->_int >= (qcint)vec_size(prog->entitydata)) { + qcvmerror(prog, "`%s` attempted to write to an out of bounds edict (%i)", prog->filename, OPB->_int); + goto cleanup; + } + if (OPB->_int < (qcint)prog->entityfields && !prog->allowworldwrites) + qcvmerror(prog, "`%s` tried to assign to world.%s (field %i)\n", + prog->filename, + prog_getstring(prog, prog_entfield(prog, OPB->_int)->name), + OPB->_int); + ptr = (qcany*)(prog->entitydata + OPB->_int); + ptr->_int = OPA->_int; + break; + case INSTR_STOREP_V: + if (OPB->_int < 0 || OPB->_int + 2 >= (qcint)vec_size(prog->entitydata)) { + qcvmerror(prog, "`%s` attempted to write to an out of bounds edict (%i)", prog->filename, OPB->_int); + goto cleanup; + } + if (OPB->_int < (qcint)prog->entityfields && !prog->allowworldwrites) + qcvmerror(prog, "`%s` tried to assign to world.%s (field %i)\n", + prog->filename, + prog_getstring(prog, prog_entfield(prog, OPB->_int)->name), + OPB->_int); + ptr = (qcany*)(prog->entitydata + OPB->_int); + ptr->ivector[0] = OPA->ivector[0]; + ptr->ivector[1] = OPA->ivector[1]; + ptr->ivector[2] = OPA->ivector[2]; + break; + + case INSTR_NOT_F: + OPC->_float = !FLOAT_IS_TRUE_FOR_INT(OPA->_int); + break; + case INSTR_NOT_V: + OPC->_float = !OPA->vector[0] && + !OPA->vector[1] && + !OPA->vector[2]; + break; + case INSTR_NOT_S: + OPC->_float = !OPA->string || + !*prog_getstring(prog, OPA->string); + break; + case INSTR_NOT_ENT: + OPC->_float = (OPA->edict == 0); + break; + case INSTR_NOT_FNC: + OPC->_float = !OPA->function; + break; + + case INSTR_IF: + /* this is consistent with darkplaces' behaviour */ + if(FLOAT_IS_TRUE_FOR_INT(OPA->_int)) + { + st += st->o2.s1 - 1; /* offset the s++ */ + if (++jumpcount >= maxjumps) + qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename, jumpcount); + } + break; + case INSTR_IFNOT: + if(!FLOAT_IS_TRUE_FOR_INT(OPA->_int)) + { + st += st->o2.s1 - 1; /* offset the s++ */ + if (++jumpcount >= maxjumps) + qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename, jumpcount); + } + break; + + case INSTR_CALL0: + case INSTR_CALL1: + case INSTR_CALL2: + case INSTR_CALL3: + case INSTR_CALL4: + case INSTR_CALL5: + case INSTR_CALL6: + case INSTR_CALL7: + case INSTR_CALL8: + prog->argc = st->opcode - INSTR_CALL0; + if (!OPA->function) + qcvmerror(prog, "NULL function in `%s`", prog->filename); + + if(!OPA->function || OPA->function >= (qcint)vec_size(prog->functions)) + { + qcvmerror(prog, "CALL outside the program in `%s`", prog->filename); + goto cleanup; + } + + newf = &prog->functions[OPA->function]; + newf->profile++; + + prog->statement = (st - prog->code) + 1; + + if (newf->entry < 0) + { + /* negative statements are built in functions */ + qcint builtinnumber = -newf->entry; + if (builtinnumber < (qcint)prog->builtins_count && prog->builtins[builtinnumber]) + prog->builtins[builtinnumber](prog); + else + qcvmerror(prog, "No such builtin #%i in %s! Try updating your gmqcc sources", + builtinnumber, prog->filename); + } + else + st = prog->code + prog_enterfunction(prog, newf) - 1; /* offset st++ */ + if (prog->vmerror) + goto cleanup; + break; + + case INSTR_STATE: + qcvmerror(prog, "`%s` tried to execute a STATE operation", prog->filename); + break; + + case INSTR_GOTO: + st += st->o1.s1 - 1; /* offset the s++ */ + if (++jumpcount == 10000000) + qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename, jumpcount); + break; + + case INSTR_AND: + OPC->_float = FLOAT_IS_TRUE_FOR_INT(OPA->_int) && + FLOAT_IS_TRUE_FOR_INT(OPB->_int); + break; + case INSTR_OR: + OPC->_float = FLOAT_IS_TRUE_FOR_INT(OPA->_int) || + FLOAT_IS_TRUE_FOR_INT(OPB->_int); + break; + + case INSTR_BITAND: + OPC->_float = ((int)OPA->_float) & ((int)OPB->_float); + break; + case INSTR_BITOR: + OPC->_float = ((int)OPA->_float) | ((int)OPB->_float); + break; + } +} + +#undef QCVM_PROFILE +#undef QCVM_TRACE +#endif /* !QCVM_LOOP */ diff --git a/misc/source/gmqcc-src/fs.c b/misc/source/gmqcc-src/fs.c new file mode 100644 index 00000000..501041fe --- /dev/null +++ b/misc/source/gmqcc-src/fs.c @@ -0,0 +1,312 @@ +/* + * Copyright (C) 2012, 2013 + * Dale Weiler + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "gmqcc.h" + +/* + * This is essentially a "wrapper" interface around standard C's IO + * library. There is two reason we implement this, 1) visual studio + * hearts for "secure" varations, as part of it's "Security Enhancements + * in the CRT" (http://msdn.microsoft.com/en-us/library/8ef0s5kh.aspx). + * 2) But one of the greater reasons is for the possibility of large file + * support in the future. I don't expect to reach the 2GB limit any + * time soon (mainly because that would be insane). But when it comes + * to adding support for some other larger IO tasks (in the test-suite, + * or even the QCVM we'll need it). There is also a third possibility of + * building .dat files directly from zip files (which would be very cool + * at least I think so). + */ +#ifdef _MSC_VER +#include /* _CrtSetReportMode, _CRT_ASSERT */ +/* {{{ */ + /* + * Visual Studio has security CRT features which I actually want to support + * if we ever port to Windows 8, and want GMQCC to be API safe. + * + * We handle them here, for all file-operations. + */ + + static void file_exception ( + const wchar_t *expression, + const wchar_t *function, + const wchar_t *file, + unsigned int line, + uintptr_t reserved + ) { + wprintf(L"Invalid parameter dectected %s:%d %s [%s]\n", file, line, function, expression); + wprintf(L"Aborting ...\n"); + exit(EXIT_FAILURE); + } + + static void file_init() { + static bool init = false; + + if (init) + return; + + _set_invalid_parameter_handler(&file_exception); + + /* + * Turnoff the message box for CRT asserations otherwise + * we don't get the error reported to the console as we should + * otherwise get. + */ + _CrtSetReportMode(_CRT_ASSERT, 0); + init = !init; + } + + + FILE *fs_file_open(const char *filename, const char *mode) { + FILE *handle = NULL; + file_init(); + + return (fopen_s(&handle, filename, mode) != 0) ? NULL : handle; + } + + size_t fs_file_read(void *buffer, size_t size, size_t count, FILE *fp) { + file_init(); + return fread_s(buffer, size*count, size, count, fp); + } + + int fs_file_printf(FILE *fp, const char *format, ...) { + int rt; + va_list va; + va_start(va, format); + + file_init(); + rt = vfprintf_s(fp, format, va); + va_end (va); + + return rt; + } + +/* }}} */ +#else +/* {{{ */ + /* + * All other compilers/platforms that don't restrict insane policies on + * IO for no aparent reason. + */ + FILE *fs_file_open(const char *filename, const char *mode) { + return fopen(filename, mode); + } + + size_t fs_file_read(void *buffer, size_t size, size_t count, FILE *fp) { + return fread(buffer, size, count, fp); + } + + int fs_file_printf(FILE *fp, const char *format, ...) { + int rt; + va_list va; + va_start(va, format); + rt = vfprintf(fp, format, va); + va_end (va); + + return rt; + } + +/* }}} */ +#endif + +/* + * These are implemented as just generic wrappers to keep consistency in + * the API. Not as macros though + */ +void fs_file_close(FILE *fp) { + /* Invokes file_exception on windows if fp is null */ + fclose (fp); +} + +size_t fs_file_write ( + const void *buffer, + size_t size, + size_t count, + FILE *fp +) { + /* Invokes file_exception on windows if fp is null */ + return fwrite(buffer, size, count, fp); +} + +int fs_file_error(FILE *fp) { + /* Invokes file_exception on windows if fp is null */ + return ferror(fp); +} + +int fs_file_getc(FILE *fp) { + /* Invokes file_exception on windows if fp is null */ + return fgetc(fp); +} + +int fs_file_puts(FILE *fp, const char *str) { + /* Invokes file_exception on windows if fp is null */ + return fputs(str, fp); +} + +int fs_file_seek(FILE *fp, long int off, int whence) { + /* Invokes file_exception on windows if fp is null */ + return fseek(fp, off, whence); +} + +long int fs_file_tell(FILE *fp) { + /* Invokes file_exception on windows if fp is null */ + return ftell(fp); +} + +/* + * Implements libc getline for systems that don't have it, which is + * assmed all. This works the same as getline(). + */ +int fs_file_getline(char **lineptr, size_t *n, FILE *stream) { + int chr; + int ret; + char *pos; + + if (!lineptr || !n || !stream) + return -1; + if (!*lineptr) { + if (!(*lineptr = (char*)mem_a((*n=64)))) + return -1; + } + + chr = *n; + pos = *lineptr; + + for (;;) { + int c = fs_file_getc(stream); + + if (chr < 2) { + *n += (*n > 16) ? *n : 64; + chr = *n + *lineptr - pos; + if (!(*lineptr = (char*)mem_r(*lineptr,*n))) + return -1; + pos = *n - chr + *lineptr; + } + + if (ferror(stream)) + return -1; + if (c == EOF) { + if (pos == *lineptr) + return -1; + else + break; + } + + *pos++ = c; + chr--; + if (c == '\n') + break; + } + *pos = '\0'; + return (ret = pos - *lineptr); +} + +/* + * Now we implement some directory functionality. Windows lacks dirent.h + * this is such a pisss off, we implement it here. + */ +#if defined(_WIN32) && !defined(__MINGW32__) + DIR *fs_dir_open(const char *name) { + DIR *dir = (DIR*)mem_a(sizeof(DIR) + strlen(name)); + if (!dir) + return NULL; + + util_strncpy(dir->dd_name, name, strlen(name)); + return dir; + } + + int fs_dir_close(DIR *dir) { + FindClose((HANDLE)dir->dd_handle); + mem_d ((void*)dir); + return 0; + } + + struct dirent *fs_dir_read(DIR *dir) { + WIN32_FIND_DATA info; + struct dirent *data; + int rets; + + if (!dir->dd_handle) { + char *dirname; + if (*dir->dd_name) { + size_t n = strlen(dir->dd_name); + if ((dirname = (char*)mem_a(n + 5) /* 4 + 1 */)) { + util_strncpy(dirname, dir->dd_name, n); + util_strncpy(dirname + n, "\\*.*", 4); /* 4 + 1 */ + } + } else { + if (!(dirname = util_strdup("\\*.*"))) + return NULL; + } + + dir->dd_handle = (long)FindFirstFile(dirname, &info); + mem_d(dirname); + rets = !(!dir->dd_handle); + } else if (dir->dd_handle != -11) { + rets = FindNextFile ((HANDLE)dir->dd_handle, &info); + } else { + rets = 0; + } + + if (!rets) + return NULL; + + if ((data = (struct dirent*)mem_a(sizeof(struct dirent)))) { + util_strncpy(data->d_name, info.cFileName, FILENAME_MAX - 1); + data->d_name[FILENAME_MAX - 1] = '\0'; /* terminate */ + data->d_namlen = strlen(data->d_name); + } + return data; + } + + int fs_dir_change(const char *path) { + return !SetCurrentDirectory(path); + } + + int fs_dir_make(const char *path) { + return !CreateDirectory(path, NULL); + } +#else +# if !defined(__MINGW32__) +# include /* mkdir */ + + int fs_dir_make(const char *path) { + return mkdir(path, 0700); + } +# else + int fs_dir_make(const char *path) { + return mkdir(path); + } +# endif /*! !defined(__MINGW32__) */ + +DIR *fs_dir_open(const char *name) { + return opendir(name); +} + +int fs_dir_close(DIR *dir) { + return closedir(dir); +} + +struct dirent *fs_dir_read(DIR *dir) { + return readdir(dir); +} + +#endif /*! defined(_WIN32) && !defined(__MINGW32__) */ diff --git a/misc/source/gmqcc-src/ftepp.c b/misc/source/gmqcc-src/ftepp.c new file mode 100644 index 00000000..55d4fffb --- /dev/null +++ b/misc/source/gmqcc-src/ftepp.c @@ -0,0 +1,1909 @@ +/* + * Copyright (C) 2012, 2013 + * Wolfgang Bumiller + * Dale Weiler + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include +#include +#include +#include + +#include "gmqcc.h" +#include "lexer.h" + +#define HT_MACROS 1024 +typedef struct { + bool on; + bool was_on; + bool had_else; +} ppcondition; + +typedef struct { + int token; + char *value; + /* a copy from the lexer */ + union { + vector v; + int i; + double f; + int t; /* type */ + } constval; +} pptoken; + +typedef struct { + lex_ctx ctx; + + char *name; + char **params; + /* yes we need an extra flag since `#define FOO x` is not the same as `#define FOO() x` */ + bool has_params; + bool variadic; + + pptoken **output; +} ppmacro; + +typedef struct ftepp_s { + lex_file *lex; + int token; + unsigned int errors; + + bool output_on; + ppcondition *conditions; + /*ppmacro **macros;*/ + ht macros; /* hashtable */ + char *output_string; + + char *itemname; + char *includename; + bool in_macro; +} ftepp_t; + +/* + * Implement the predef subsystem now. We can do this safely with the + * help of lexer contexts. + */ +static uint32_t ftepp_predef_countval = 0; +static uint32_t ftepp_predef_randval = 0; + +/* __DATE__ */ +static char *ftepp_predef_date(lex_file *context) { + struct tm *itime = NULL; + time_t rtime; + char *value = (char*)mem_a(82); + /* 82 is enough for strftime but we also have " " in our string */ + + (void)context; + + /* get time */ + time (&rtime); + +#ifdef _MSC_VER + localtime_s(itime, &rtime); +#else + itime = localtime(&rtime); +#endif + + strftime(value, 82, "\"%b %d %Y\"", itime); + + return value; +} + +/* __TIME__ */ +static char *ftepp_predef_time(lex_file *context) { + struct tm *itime = NULL; + time_t rtime; + char *value = (char*)mem_a(82); + /* 82 is enough for strftime but we also have " " in our string */ + + (void)context; + + /* get time */ + time (&rtime); + +#ifdef _MSC_VER + localtime_s(itime, &rtime); +#else + itime = localtime(&rtime); +#endif + + strftime(value, 82, "\"%X\"", itime); + + return value; +} + +/* __LINE__ */ +static char *ftepp_predef_line(lex_file *context) { + char *value; + util_asprintf(&value, "%d", (int)context->line); + return value; +} +/* __FILE__ */ +static char *ftepp_predef_file(lex_file *context) { + size_t length = strlen(context->name) + 3; /* two quotes and a terminator */ + char *value = (char*)mem_a(length); + util_snprintf(value, length, "\"%s\"", context->name); + + return value; +} +/* __COUNTER_LAST__ */ +static char *ftepp_predef_counterlast(lex_file *context) { + char *value; + util_asprintf(&value, "%u", ftepp_predef_countval); + + (void)context; + return value; +} +/* __COUNTER__ */ +static char *ftepp_predef_counter(lex_file *context) { + char *value; + ftepp_predef_countval ++; + util_asprintf(&value, "%u", ftepp_predef_countval); + (void)context; + + return value; +} +/* __RANDOM__ */ +static char *ftepp_predef_random(lex_file *context) { + char *value; + ftepp_predef_randval = (util_rand() % 0xFF) + 1; + util_asprintf(&value, "%u", ftepp_predef_randval); + + (void)context; + return value; +} +/* __RANDOM_LAST__ */ +static char *ftepp_predef_randomlast(lex_file *context) { + char *value; + util_asprintf(&value, "%u", ftepp_predef_randval); + + (void)context; + return value; +} +/* __TIMESTAMP__ */ +static char *ftepp_predef_timestamp(lex_file *context) { + struct stat finfo; + char *find; + char *value; + size_t size; + if (stat(context->name, &finfo)) + return util_strdup("\"\""); + + /* + * ctime and its fucking annoying newline char, no worries, we're + * professionals here. + */ + find = ctime(&finfo.st_mtime); + value = (char*)mem_a(strlen(find) + 1); + memcpy(&value[1], find, (size = strlen(find)) - 1); + + value[0] = '"'; + value[size] = '"'; + + return value; +} + +typedef struct { + const char *name; + char *(*func)(lex_file *); +} ftepp_predef_t; + +static const ftepp_predef_t ftepp_predefs[] = { + { "__LINE__", &ftepp_predef_line }, + { "__FILE__", &ftepp_predef_file }, + { "__COUNTER__", &ftepp_predef_counter }, + { "__COUNTER_LAST__", &ftepp_predef_counterlast }, + { "__RANDOM__", &ftepp_predef_random }, + { "__RANDOM_LAST__", &ftepp_predef_randomlast }, + { "__DATE__", &ftepp_predef_date }, + { "__TIME__", &ftepp_predef_time }, + { "__TIME_STAMP__", &ftepp_predef_timestamp } +}; + +static GMQCC_INLINE int ftepp_predef_index(const char *name) { + /* no hashtable here, we simply check for one to exist the naive way */ + int i; + for(i = 0; i < (int)(sizeof(ftepp_predefs)/sizeof(*ftepp_predefs)); i++) + if (!strcmp(ftepp_predefs[i].name, name)) + return i; + return -1; +} + +bool ftepp_predef_exists(const char *name) { + return ftepp_predef_index(name) != -1; +} + +/* singleton because we're allowed */ +static GMQCC_INLINE char *(*ftepp_predef(const char *name))(lex_file *context) { + int i = ftepp_predef_index(name); + return (i != -1) ? ftepp_predefs[i].func : NULL; +} + +#define ftepp_tokval(f) ((f)->lex->tok.value) +#define ftepp_ctx(f) ((f)->lex->tok.ctx) + +static void ftepp_errorat(ftepp_t *ftepp, lex_ctx ctx, const char *fmt, ...) +{ + va_list ap; + + ftepp->errors++; + + va_start(ap, fmt); + con_cvprintmsg((void*)&ctx, LVL_ERROR, "error", fmt, ap); + va_end(ap); +} + +static void ftepp_error(ftepp_t *ftepp, const char *fmt, ...) +{ + va_list ap; + + ftepp->errors++; + + va_start(ap, fmt); + con_cvprintmsg((void*)&ftepp->lex->tok.ctx, LVL_ERROR, "error", fmt, ap); + va_end(ap); +} + +static bool GMQCC_WARN ftepp_warn(ftepp_t *ftepp, int warntype, const char *fmt, ...) +{ + bool r; + va_list ap; + + va_start(ap, fmt); + r = vcompile_warning(ftepp->lex->tok.ctx, warntype, fmt, ap); + va_end(ap); + return r; +} + +static pptoken *pptoken_make(ftepp_t *ftepp) +{ + pptoken *token = (pptoken*)mem_a(sizeof(pptoken)); + token->token = ftepp->token; +#if 0 + if (token->token == TOKEN_WHITE) + token->value = util_strdup(" "); + else +#else + token->value = util_strdup(ftepp_tokval(ftepp)); +#endif + memcpy(&token->constval, &ftepp->lex->tok.constval, sizeof(token->constval)); + return token; +} + +static GMQCC_INLINE void pptoken_delete(pptoken *self) +{ + mem_d(self->value); + mem_d(self); +} + +static ppmacro *ppmacro_new(lex_ctx ctx, const char *name) +{ + ppmacro *macro = (ppmacro*)mem_a(sizeof(ppmacro)); + + (void)ctx; + memset(macro, 0, sizeof(*macro)); + macro->name = util_strdup(name); + return macro; +} + +static void ppmacro_delete(ppmacro *self) +{ + size_t i; + for (i = 0; i < vec_size(self->params); ++i) + mem_d(self->params[i]); + vec_free(self->params); + for (i = 0; i < vec_size(self->output); ++i) + pptoken_delete(self->output[i]); + vec_free(self->output); + mem_d(self->name); + mem_d(self); +} + +static ftepp_t* ftepp_new(void) +{ + ftepp_t *ftepp; + + ftepp = (ftepp_t*)mem_a(sizeof(*ftepp)); + memset(ftepp, 0, sizeof(*ftepp)); + + ftepp->macros = util_htnew(HT_MACROS); + ftepp->output_on = true; + + return ftepp; +} + +static GMQCC_INLINE void ftepp_flush_do(ftepp_t *self) +{ + vec_free(self->output_string); +} + +static void ftepp_delete(ftepp_t *self) +{ + ftepp_flush_do(self); + if (self->itemname) + mem_d(self->itemname); + if (self->includename) + vec_free(self->includename); + + util_htrem(self->macros, (void (*)(void*))&ppmacro_delete); + + vec_free(self->conditions); + if (self->lex) + lex_close(self->lex); + mem_d(self); +} + +static void ftepp_out(ftepp_t *ftepp, const char *str, bool ignore_cond) +{ + if (ignore_cond || ftepp->output_on) + { + size_t len; + char *data; + len = strlen(str); + data = vec_add(ftepp->output_string, len); + memcpy(data, str, len); + } +} + +static GMQCC_INLINE void ftepp_update_output_condition(ftepp_t *ftepp) +{ + size_t i; + ftepp->output_on = true; + for (i = 0; i < vec_size(ftepp->conditions); ++i) + ftepp->output_on = ftepp->output_on && ftepp->conditions[i].on; +} + +static GMQCC_INLINE ppmacro* ftepp_macro_find(ftepp_t *ftepp, const char *name) +{ + return (ppmacro*)util_htget(ftepp->macros, name); +} + +static GMQCC_INLINE void ftepp_macro_delete(ftepp_t *ftepp, const char *name) +{ + util_htrm(ftepp->macros, name, (void (*)(void*))&ppmacro_delete); +} + +static GMQCC_INLINE int ftepp_next(ftepp_t *ftepp) +{ + return (ftepp->token = lex_do(ftepp->lex)); +} + +/* Important: this does not skip newlines! */ +static bool ftepp_skipspace(ftepp_t *ftepp) +{ + if (ftepp->token != TOKEN_WHITE) + return true; + while (ftepp_next(ftepp) == TOKEN_WHITE) {} + if (ftepp->token >= TOKEN_EOF) { + ftepp_error(ftepp, "unexpected end of preprocessor directive"); + return false; + } + return true; +} + +/* this one skips EOLs as well */ +static bool ftepp_skipallwhite(ftepp_t *ftepp) +{ + if (ftepp->token != TOKEN_WHITE && ftepp->token != TOKEN_EOL) + return true; + do { + ftepp_next(ftepp); + } while (ftepp->token == TOKEN_WHITE || ftepp->token == TOKEN_EOL); + if (ftepp->token >= TOKEN_EOF) { + ftepp_error(ftepp, "unexpected end of preprocessor directive"); + return false; + } + return true; +} + +/** + * The huge macro parsing code... + */ +static bool ftepp_define_params(ftepp_t *ftepp, ppmacro *macro) +{ + do { + ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + if (ftepp->token == ')') + break; + switch (ftepp->token) { + case TOKEN_IDENT: + case TOKEN_TYPENAME: + case TOKEN_KEYWORD: + vec_push(macro->params, util_strdup(ftepp_tokval(ftepp))); + break; + case TOKEN_DOTS: + macro->variadic = true; + break; + default: + ftepp_error(ftepp, "unexpected token in parameter list"); + return false; + } + ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + if (macro->variadic && ftepp->token != ')') { + ftepp_error(ftepp, "cannot have parameters after the variadic parameters"); + return false; + } + } while (ftepp->token == ','); + + if (ftepp->token != ')') { + ftepp_error(ftepp, "expected closing paren after macro parameter list"); + return false; + } + ftepp_next(ftepp); + /* skipspace happens in ftepp_define */ + return true; +} + +static bool ftepp_define_body(ftepp_t *ftepp, ppmacro *macro) +{ + pptoken *ptok; + while (ftepp->token != TOKEN_EOL && ftepp->token < TOKEN_EOF) { + bool subscript = false; + size_t index = 0; + if (macro->variadic && !strcmp(ftepp_tokval(ftepp), "__VA_ARGS__")) { + subscript = !!(ftepp_next(ftepp) == '#'); + + if (subscript && ftepp_next(ftepp) != '#') { + ftepp_error(ftepp, "expected `##` in __VA_ARGS__ for subscripting"); + return false; + } else if (subscript) { + if (ftepp_next(ftepp) == '[') { + if (ftepp_next(ftepp) != TOKEN_INTCONST) { + ftepp_error(ftepp, "expected index for __VA_ARGS__ subscript"); + return false; + } + + index = (int)strtol(ftepp_tokval(ftepp), NULL, 10); + + if (ftepp_next(ftepp) != ']') { + ftepp_error(ftepp, "expected `]` in __VA_ARGS__ subscript"); + return false; + } + + /* + * mark it as an array to be handled later as such and not + * as traditional __VA_ARGS__ + */ + ftepp->token = TOKEN_VA_ARGS_ARRAY; + ptok = pptoken_make(ftepp); + ptok->constval.i = index; + vec_push(macro->output, ptok); + ftepp_next(ftepp); + } else { + ftepp_error(ftepp, "expected `[` for subscripting of __VA_ARGS__"); + return false; + } + } else { + int old = ftepp->token; + ftepp->token = TOKEN_VA_ARGS; + ptok = pptoken_make(ftepp); + vec_push(macro->output, ptok); + ftepp->token = old; + } + } + else if (macro->variadic && !strcmp(ftepp_tokval(ftepp), "__VA_COUNT__")) { + ftepp->token = TOKEN_VA_COUNT; + ptok = pptoken_make(ftepp); + vec_push(macro->output, ptok); + ftepp_next(ftepp); + } else { + ptok = pptoken_make(ftepp); + vec_push(macro->output, ptok); + ftepp_next(ftepp); + } + } + /* recursive expansion can cause EOFs here */ + if (ftepp->token != TOKEN_EOL && ftepp->token != TOKEN_EOF) { + ftepp_error(ftepp, "unexpected junk after macro or unexpected end of file"); + return false; + } + return true; +} + +static bool ftepp_define(ftepp_t *ftepp) +{ + ppmacro *macro = NULL; + size_t l = ftepp_ctx(ftepp).line; + + (void)ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + + switch (ftepp->token) { + case TOKEN_IDENT: + case TOKEN_TYPENAME: + case TOKEN_KEYWORD: + macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp)); + if (macro && ftepp->output_on) { + if (ftepp_warn(ftepp, WARN_CPP, "redefining `%s`", ftepp_tokval(ftepp))) + return false; + ftepp_macro_delete(ftepp, ftepp_tokval(ftepp)); + } + macro = ppmacro_new(ftepp_ctx(ftepp), ftepp_tokval(ftepp)); + break; + default: + ftepp_error(ftepp, "expected macro name"); + return false; + } + + (void)ftepp_next(ftepp); + + if (ftepp->token == '(') { + macro->has_params = true; + if (!ftepp_define_params(ftepp, macro)) { + ppmacro_delete(macro); + return false; + } + } + + if (!ftepp_skipspace(ftepp)) { + ppmacro_delete(macro); + return false; + } + + if (!ftepp_define_body(ftepp, macro)) { + ppmacro_delete(macro); + return false; + } + + if (ftepp->output_on) + util_htset(ftepp->macros, macro->name, (void*)macro); + else { + ppmacro_delete(macro); + } + + for (; l < ftepp_ctx(ftepp).line; ++l) + ftepp_out(ftepp, "\n", true); + return true; +} + +/** + * When a macro is used we have to handle parameters as well + * as special-concatenation via ## or stringification via # + * + * Note: parenthesis can nest, so FOO((a),b) is valid, but only + * this kind of parens. Curly braces or [] don't count towards the + * paren-level. + */ +typedef struct { + pptoken **tokens; +} macroparam; + +static void macroparam_clean(macroparam *self) +{ + size_t i; + for (i = 0; i < vec_size(self->tokens); ++i) + pptoken_delete(self->tokens[i]); + vec_free(self->tokens); +} + +/* need to leave the last token up */ +static bool ftepp_macro_call_params(ftepp_t *ftepp, macroparam **out_params) +{ + macroparam *params = NULL; + pptoken *ptok; + macroparam mp; + size_t parens = 0; + size_t i; + + if (!ftepp_skipallwhite(ftepp)) + return false; + while (ftepp->token != ')') { + mp.tokens = NULL; + if (!ftepp_skipallwhite(ftepp)) + return false; + while (parens || ftepp->token != ',') { + if (ftepp->token == '(') + ++parens; + else if (ftepp->token == ')') { + if (!parens) + break; + --parens; + } + ptok = pptoken_make(ftepp); + vec_push(mp.tokens, ptok); + if (ftepp_next(ftepp) >= TOKEN_EOF) { + ftepp_error(ftepp, "unexpected EOF in macro call"); + goto on_error; + } + } + vec_push(params, mp); + mp.tokens = NULL; + if (ftepp->token == ')') + break; + if (ftepp->token != ',') { + ftepp_error(ftepp, "expected closing paren or comma in macro call"); + goto on_error; + } + if (ftepp_next(ftepp) >= TOKEN_EOF) { + ftepp_error(ftepp, "unexpected EOF in macro call"); + goto on_error; + } + } + /* need to leave that up + if (ftepp_next(ftepp) >= TOKEN_EOF) { + ftepp_error(ftepp, "unexpected EOF in macro call"); + goto on_error; + } + */ + *out_params = params; + return true; + +on_error: + if (mp.tokens) + macroparam_clean(&mp); + for (i = 0; i < vec_size(params); ++i) + macroparam_clean(¶ms[i]); + vec_free(params); + return false; +} + +static bool macro_params_find(ppmacro *macro, const char *name, size_t *idx) +{ + size_t i; + for (i = 0; i < vec_size(macro->params); ++i) { + if (!strcmp(macro->params[i], name)) { + *idx = i; + return true; + } + } + return false; +} + +static void ftepp_stringify_token(ftepp_t *ftepp, pptoken *token) +{ + char chs[2]; + const char *ch; + chs[1] = 0; + switch (token->token) { + case TOKEN_STRINGCONST: + ch = token->value; + while (*ch) { + /* in preprocessor mode strings already are string, + * so we don't get actual newline bytes here. + * Still need to escape backslashes and quotes. + */ + switch (*ch) { + case '\\': ftepp_out(ftepp, "\\\\", false); break; + case '"': ftepp_out(ftepp, "\\\"", false); break; + default: + chs[0] = *ch; + ftepp_out(ftepp, chs, false); + break; + } + ++ch; + } + break; + case TOKEN_WHITE: + ftepp_out(ftepp, " ", false); + break; + case TOKEN_EOL: + ftepp_out(ftepp, "\\n", false); + break; + default: + ftepp_out(ftepp, token->value, false); + break; + } +} + +static void ftepp_stringify(ftepp_t *ftepp, macroparam *param) +{ + size_t i; + ftepp_out(ftepp, "\"", false); + for (i = 0; i < vec_size(param->tokens); ++i) + ftepp_stringify_token(ftepp, param->tokens[i]); + ftepp_out(ftepp, "\"", false); +} + +static void ftepp_recursion_header(ftepp_t *ftepp) +{ + ftepp_out(ftepp, "\n#pragma push(line)\n", false); +} + +static void ftepp_recursion_footer(ftepp_t *ftepp) +{ + ftepp_out(ftepp, "\n#pragma pop(line)\n", false); +} + +static void ftepp_param_out(ftepp_t *ftepp, macroparam *param) +{ + size_t i; + pptoken *out; + for (i = 0; i < vec_size(param->tokens); ++i) { + out = param->tokens[i]; + if (out->token == TOKEN_EOL) + ftepp_out(ftepp, "\n", false); + else + ftepp_out(ftepp, out->value, false); + } +} + +static bool ftepp_preprocess(ftepp_t *ftepp); +static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *params, bool resetline) +{ + char *buffer = NULL; + char *old_string = ftepp->output_string; + char *inner_string; + lex_file *old_lexer = ftepp->lex; + size_t vararg_start = vec_size(macro->params); + bool retval = true; + bool has_newlines; + size_t varargs; + + size_t o, pi; + lex_file *inlex; + + bool old_inmacro; + + int nextok; + + if (vararg_start < vec_size(params)) + varargs = vec_size(params) - vararg_start; + else + varargs = 0; + + /* really ... */ + if (!vec_size(macro->output)) + return true; + + ftepp->output_string = NULL; + for (o = 0; o < vec_size(macro->output); ++o) { + pptoken *out = macro->output[o]; + switch (out->token) { + case TOKEN_VA_ARGS: + if (!macro->variadic) { + ftepp_error(ftepp, "internal preprocessor error: TOKEN_VA_ARGS in non-variadic macro"); + vec_free(old_string); + return false; + } + if (!varargs) + break; + + pi = 0; + ftepp_param_out(ftepp, ¶ms[pi + vararg_start]); + for (++pi; pi < varargs; ++pi) { + ftepp_out(ftepp, ", ", false); + ftepp_param_out(ftepp, ¶ms[pi + vararg_start]); + } + break; + + case TOKEN_VA_ARGS_ARRAY: + if ((size_t)out->constval.i >= varargs) { + ftepp_error(ftepp, "subscript of `[%u]` is out of bounds for `__VA_ARGS__`", out->constval.i); + vec_free(old_string); + return false; + } + + ftepp_param_out(ftepp, ¶ms[out->constval.i + vararg_start]); + break; + + case TOKEN_VA_COUNT: + util_asprintf(&buffer, "%d", varargs); + ftepp_out(ftepp, buffer, false); + mem_d(buffer); + break; + + case TOKEN_IDENT: + case TOKEN_TYPENAME: + case TOKEN_KEYWORD: + if (!macro_params_find(macro, out->value, &pi)) { + ftepp_out(ftepp, out->value, false); + break; + } else + ftepp_param_out(ftepp, ¶ms[pi]); + break; + case '#': + if (o + 1 < vec_size(macro->output)) { + nextok = macro->output[o+1]->token; + if (nextok == '#') { + /* raw concatenation */ + ++o; + break; + } + if ( (nextok == TOKEN_IDENT || + nextok == TOKEN_KEYWORD || + nextok == TOKEN_TYPENAME) && + macro_params_find(macro, macro->output[o+1]->value, &pi)) + { + ++o; + ftepp_stringify(ftepp, ¶ms[pi]); + break; + } + } + ftepp_out(ftepp, "#", false); + break; + case TOKEN_EOL: + ftepp_out(ftepp, "\n", false); + break; + default: + ftepp_out(ftepp, out->value, false); + break; + } + } + vec_push(ftepp->output_string, 0); + /* Now run the preprocessor recursively on this string buffer */ + /* + printf("__________\n%s\n=========\n", ftepp->output_string); + */ + inlex = lex_open_string(ftepp->output_string, vec_size(ftepp->output_string)-1, ftepp->lex->name); + if (!inlex) { + ftepp_error(ftepp, "internal error: failed to instantiate lexer"); + retval = false; + goto cleanup; + } + + inlex->line = ftepp->lex->line; + inlex->sline = ftepp->lex->sline; + ftepp->lex = inlex; + + old_inmacro = ftepp->in_macro; + ftepp->in_macro = true; + ftepp->output_string = NULL; + if (!ftepp_preprocess(ftepp)) { + ftepp->in_macro = old_inmacro; + vec_free(ftepp->lex->open_string); + vec_free(ftepp->output_string); + lex_close(ftepp->lex); + retval = false; + goto cleanup; + } + ftepp->in_macro = old_inmacro; + vec_free(ftepp->lex->open_string); + lex_close(ftepp->lex); + + inner_string = ftepp->output_string; + ftepp->output_string = old_string; + + has_newlines = (strchr(inner_string, '\n') != NULL); + + if (has_newlines && !old_inmacro) + ftepp_recursion_header(ftepp); + + vec_append(ftepp->output_string, vec_size(inner_string), inner_string); + vec_free(inner_string); + + if (has_newlines && !old_inmacro) + ftepp_recursion_footer(ftepp); + + if (resetline && !ftepp->in_macro) { + char lineno[128]; + util_snprintf(lineno, 128, "\n#pragma line(%lu)\n", (unsigned long)(old_lexer->sline)); + ftepp_out(ftepp, lineno, false); + } + + old_string = ftepp->output_string; +cleanup: + ftepp->lex = old_lexer; + ftepp->output_string = old_string; + return retval; +} + +static bool ftepp_macro_call(ftepp_t *ftepp, ppmacro *macro) +{ + size_t o; + macroparam *params = NULL; + bool retval = true; + size_t paramline; + + if (!macro->has_params) { + if (!ftepp_macro_expand(ftepp, macro, NULL, false)) + return false; + ftepp_next(ftepp); + return true; + } + ftepp_next(ftepp); + + if (!ftepp_skipallwhite(ftepp)) + return false; + + if (ftepp->token != '(') { + ftepp_error(ftepp, "expected macro parameters in parenthesis"); + return false; + } + + ftepp_next(ftepp); + paramline = ftepp->lex->sline; + if (!ftepp_macro_call_params(ftepp, ¶ms)) + return false; + + if ( vec_size(params) < vec_size(macro->params) || + (vec_size(params) > vec_size(macro->params) && !macro->variadic) ) + { + ftepp_error(ftepp, "macro %s expects%s %u paramteters, %u provided", macro->name, + (macro->variadic ? " at least" : ""), + (unsigned int)vec_size(macro->params), + (unsigned int)vec_size(params)); + retval = false; + goto cleanup; + } + + if (!ftepp_macro_expand(ftepp, macro, params, (paramline != ftepp->lex->sline))) + retval = false; + ftepp_next(ftepp); + +cleanup: + for (o = 0; o < vec_size(params); ++o) + macroparam_clean(¶ms[o]); + vec_free(params); + return retval; +} + +/** + * #if - the FTEQCC way: + * defined(FOO) => true if FOO was #defined regardless of parameters or contents + * => True if the number is not 0 + * ! => True if the factor yields false + * !! => ERROR on 2 or more unary nots + * => becomes the macro's FIRST token regardless of parameters + * && => True if both expressions are true + * || => True if either expression is true + * => False + * => False (remember for macros the rule applies instead) + * Unary + and - are weird and wrong in fteqcc so we don't allow them + * parenthesis in expressions are allowed + * parameter lists on macros are errors + * No mathematical calculations are executed + */ +static bool ftepp_if_expr(ftepp_t *ftepp, bool *out, double *value_out); +static bool ftepp_if_op(ftepp_t *ftepp) +{ + ftepp->lex->flags.noops = false; + ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + ftepp->lex->flags.noops = true; + return true; +} +static bool ftepp_if_value(ftepp_t *ftepp, bool *out, double *value_out) +{ + ppmacro *macro; + bool wasnot = false; + bool wasneg = false; + + if (!ftepp_skipspace(ftepp)) + return false; + + while (ftepp->token == '!') { + wasnot = true; + ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + } + + if (ftepp->token == TOKEN_OPERATOR && !strcmp(ftepp_tokval(ftepp), "-")) + { + wasneg = true; + ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + } + + switch (ftepp->token) { + case TOKEN_IDENT: + case TOKEN_TYPENAME: + case TOKEN_KEYWORD: + if (!strcmp(ftepp_tokval(ftepp), "defined")) { + ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + if (ftepp->token != '(') { + ftepp_error(ftepp, "`defined` keyword in #if requires a macro name in parenthesis"); + return false; + } + ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + if (ftepp->token != TOKEN_IDENT && + ftepp->token != TOKEN_TYPENAME && + ftepp->token != TOKEN_KEYWORD) + { + ftepp_error(ftepp, "defined() used on an unexpected token type"); + return false; + } + macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp)); + *out = !!macro; + ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + if (ftepp->token != ')') { + ftepp_error(ftepp, "expected closing paren"); + return false; + } + break; + } + + macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp)); + if (!macro || !vec_size(macro->output)) { + *out = false; + *value_out = 0; + } else { + /* This does not expand recursively! */ + switch (macro->output[0]->token) { + case TOKEN_INTCONST: + *value_out = macro->output[0]->constval.i; + *out = !!(macro->output[0]->constval.i); + break; + case TOKEN_FLOATCONST: + *value_out = macro->output[0]->constval.f; + *out = !!(macro->output[0]->constval.f); + break; + default: + *out = false; + break; + } + } + break; + case TOKEN_STRINGCONST: + *value_out = 0; + *out = false; + break; + case TOKEN_INTCONST: + *value_out = ftepp->lex->tok.constval.i; + *out = !!(ftepp->lex->tok.constval.i); + break; + case TOKEN_FLOATCONST: + *value_out = ftepp->lex->tok.constval.f; + *out = !!(ftepp->lex->tok.constval.f); + break; + + case '(': + ftepp_next(ftepp); + if (!ftepp_if_expr(ftepp, out, value_out)) + return false; + if (ftepp->token != ')') { + ftepp_error(ftepp, "expected closing paren in #if expression"); + return false; + } + break; + + default: + ftepp_error(ftepp, "junk in #if: `%s` ...", ftepp_tokval(ftepp)); + if (OPTS_OPTION_BOOL(OPTION_DEBUG)) + ftepp_error(ftepp, "internal: token %i\n", ftepp->token); + return false; + } + if (wasneg) + *value_out = -*value_out; + if (wasnot) { + *out = !*out; + *value_out = (*out ? 1 : 0); + } + return true; +} + +/* +static bool ftepp_if_nextvalue(ftepp_t *ftepp, bool *out, double *value_out) +{ + if (!ftepp_next(ftepp)) + return false; + return ftepp_if_value(ftepp, out, value_out); +} +*/ + +static bool ftepp_if_expr(ftepp_t *ftepp, bool *out, double *value_out) +{ + if (!ftepp_if_value(ftepp, out, value_out)) + return false; + + if (!ftepp_if_op(ftepp)) + return false; + + if (ftepp->token == ')' || ftepp->token != TOKEN_OPERATOR) + return true; + + /* FTEQCC is all right-associative and no precedence here */ + if (!strcmp(ftepp_tokval(ftepp), "&&") || + !strcmp(ftepp_tokval(ftepp), "||")) + { + bool next = false; + char opc = ftepp_tokval(ftepp)[0]; + double nextvalue; + + (void)nextvalue; + if (!ftepp_next(ftepp)) + return false; + if (!ftepp_if_expr(ftepp, &next, &nextvalue)) + return false; + + if (opc == '&') + *out = *out && next; + else + *out = *out || next; + + *value_out = (*out ? 1 : 0); + return true; + } + else if (!strcmp(ftepp_tokval(ftepp), "==") || + !strcmp(ftepp_tokval(ftepp), "!=") || + !strcmp(ftepp_tokval(ftepp), ">=") || + !strcmp(ftepp_tokval(ftepp), "<=") || + !strcmp(ftepp_tokval(ftepp), ">") || + !strcmp(ftepp_tokval(ftepp), "<")) + { + bool next = false; + const char opc0 = ftepp_tokval(ftepp)[0]; + const char opc1 = ftepp_tokval(ftepp)[1]; + double other; + + if (!ftepp_next(ftepp)) + return false; + if (!ftepp_if_expr(ftepp, &next, &other)) + return false; + + if (opc0 == '=') + *out = (*value_out == other); + else if (opc0 == '!') + *out = (*value_out != other); + else if (opc0 == '>') { + if (opc1 == '=') *out = (*value_out >= other); + else *out = (*value_out > other); + } + else if (opc0 == '<') { + if (opc1 == '=') *out = (*value_out <= other); + else *out = (*value_out < other); + } + *value_out = (*out ? 1 : 0); + + return true; + } + else { + ftepp_error(ftepp, "junk after #if"); + return false; + } +} + +static bool ftepp_if(ftepp_t *ftepp, ppcondition *cond) +{ + bool result = false; + double dummy = 0; + + memset(cond, 0, sizeof(*cond)); + (void)ftepp_next(ftepp); + + if (!ftepp_skipspace(ftepp)) + return false; + if (ftepp->token == TOKEN_EOL) { + ftepp_error(ftepp, "expected expression for #if-directive"); + return false; + } + + if (!ftepp_if_expr(ftepp, &result, &dummy)) + return false; + + cond->on = result; + return true; +} + +/** + * ifdef is rather simple + */ +static bool ftepp_ifdef(ftepp_t *ftepp, ppcondition *cond) +{ + ppmacro *macro; + memset(cond, 0, sizeof(*cond)); + (void)ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + + switch (ftepp->token) { + case TOKEN_IDENT: + case TOKEN_TYPENAME: + case TOKEN_KEYWORD: + macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp)); + break; + default: + ftepp_error(ftepp, "expected macro name"); + return false; + } + + (void)ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + /* relaxing this condition + if (ftepp->token != TOKEN_EOL && ftepp->token != TOKEN_EOF) { + ftepp_error(ftepp, "stray tokens after #ifdef"); + return false; + } + */ + cond->on = !!macro; + return true; +} + +/** + * undef is also simple + */ +static bool ftepp_undef(ftepp_t *ftepp) +{ + (void)ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + + if (ftepp->output_on) { + switch (ftepp->token) { + case TOKEN_IDENT: + case TOKEN_TYPENAME: + case TOKEN_KEYWORD: + ftepp_macro_delete(ftepp, ftepp_tokval(ftepp)); + break; + default: + ftepp_error(ftepp, "expected macro name"); + return false; + } + } + + (void)ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + /* relaxing this condition + if (ftepp->token != TOKEN_EOL && ftepp->token != TOKEN_EOF) { + ftepp_error(ftepp, "stray tokens after #ifdef"); + return false; + } + */ + return true; +} + +/* Special unescape-string function which skips a leading quote + * and stops at a quote, not just at \0 + */ +static void unescape(const char *str, char *out) { + ++str; + while (*str && *str != '"') { + if (*str == '\\') { + ++str; + switch (*str) { + case '\\': *out++ = *str; break; + case '"': *out++ = *str; break; + case 'a': *out++ = '\a'; break; + case 'b': *out++ = '\b'; break; + case 'r': *out++ = '\r'; break; + case 'n': *out++ = '\n'; break; + case 't': *out++ = '\t'; break; + case 'f': *out++ = '\f'; break; + case 'v': *out++ = '\v'; break; + default: + *out++ = '\\'; + *out++ = *str; + break; + } + ++str; + continue; + } + + *out++ = *str++; + } + *out = 0; +} + +static char *ftepp_include_find_path(const char *file, const char *pathfile) +{ + FILE *fp; + char *filename = NULL; + const char *last_slash; + size_t len; + + if (!pathfile) + return NULL; + + last_slash = strrchr(pathfile, '/'); + + if (last_slash) { + len = last_slash - pathfile; + memcpy(vec_add(filename, len), pathfile, len); + vec_push(filename, '/'); + } + + len = strlen(file); + memcpy(vec_add(filename, len+1), file, len); + vec_last(filename) = 0; + + fp = fs_file_open(filename, "rb"); + if (fp) { + fs_file_close(fp); + return filename; + } + vec_free(filename); + return NULL; +} + +static char *ftepp_include_find(ftepp_t *ftepp, const char *file) +{ + char *filename = NULL; + + filename = ftepp_include_find_path(file, ftepp->includename); + if (!filename) + filename = ftepp_include_find_path(file, ftepp->itemname); + return filename; +} + +static bool ftepp_directive_warning(ftepp_t *ftepp) { + char *message = NULL; + + if (!ftepp_skipspace(ftepp)) + return false; + + /* handle the odd non string constant case so it works like C */ + if (ftepp->token != TOKEN_STRINGCONST) { + bool store = false; + vec_upload(message, "#warning", 8); + ftepp_next(ftepp); + while (ftepp->token != TOKEN_EOL) { + vec_upload(message, ftepp_tokval(ftepp), strlen(ftepp_tokval(ftepp))); + ftepp_next(ftepp); + } + vec_push(message, '\0'); + if (ftepp->output_on) + store = ftepp_warn(ftepp, WARN_CPP, message); + else + store = false; + vec_free(message); + return store; + } + + if (!ftepp->output_on) + return false; + + unescape (ftepp_tokval(ftepp), ftepp_tokval(ftepp)); + return ftepp_warn(ftepp, WARN_CPP, "#warning %s", ftepp_tokval(ftepp)); +} + +static void ftepp_directive_error(ftepp_t *ftepp) { + char *message = NULL; + + if (!ftepp_skipspace(ftepp)) + return; + + /* handle the odd non string constant case so it works like C */ + if (ftepp->token != TOKEN_STRINGCONST) { + vec_upload(message, "#error", 6); + ftepp_next(ftepp); + while (ftepp->token != TOKEN_EOL) { + vec_upload(message, ftepp_tokval(ftepp), strlen(ftepp_tokval(ftepp))); + ftepp_next(ftepp); + } + vec_push(message, '\0'); + if (ftepp->output_on) + ftepp_error(ftepp, message); + vec_free(message); + return; + } + + if (!ftepp->output_on) + return; + + unescape (ftepp_tokval(ftepp), ftepp_tokval(ftepp)); + ftepp_error(ftepp, "#error %s", ftepp_tokval(ftepp)); +} + +static void ftepp_directive_message(ftepp_t *ftepp) { + char *message = NULL; + + if (!ftepp_skipspace(ftepp)) + return; + + /* handle the odd non string constant case so it works like C */ + if (ftepp->token != TOKEN_STRINGCONST) { + vec_upload(message, "#message", 8); + ftepp_next(ftepp); + while (ftepp->token != TOKEN_EOL) { + vec_upload(message, ftepp_tokval(ftepp), strlen(ftepp_tokval(ftepp))); + ftepp_next(ftepp); + } + vec_push(message, '\0'); + if (ftepp->output_on) + con_cprintmsg(&ftepp->lex->tok.ctx, LVL_MSG, "message", message); + vec_free(message); + return; + } + + if (!ftepp->output_on) + return; + + unescape (ftepp_tokval(ftepp), ftepp_tokval(ftepp)); + con_cprintmsg(&ftepp->lex->tok.ctx, LVL_MSG, "message", ftepp_tokval(ftepp)); +} + +/** + * Include a file. + * FIXME: do we need/want a -I option? + * FIXME: what about when dealing with files in subdirectories coming from a progs.src? + */ +static bool ftepp_include(ftepp_t *ftepp) +{ + lex_file *old_lexer = ftepp->lex; + lex_file *inlex; + lex_ctx ctx; + char lineno[128]; + char *filename; + char *old_includename; + + (void)ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + + if (ftepp->token != TOKEN_STRINGCONST) { + ftepp_error(ftepp, "expected filename to include"); + return false; + } + + if (!ftepp->output_on) { + ftepp_next(ftepp); + return true; + } + + ctx = ftepp_ctx(ftepp); + + unescape(ftepp_tokval(ftepp), ftepp_tokval(ftepp)); + + ftepp_out(ftepp, "\n#pragma file(", false); + ftepp_out(ftepp, ftepp_tokval(ftepp), false); + ftepp_out(ftepp, ")\n#pragma line(1)\n", false); + + filename = ftepp_include_find(ftepp, ftepp_tokval(ftepp)); + if (!filename) { + ftepp_error(ftepp, "failed to open include file `%s`", ftepp_tokval(ftepp)); + return false; + } + inlex = lex_open(filename); + if (!inlex) { + ftepp_error(ftepp, "open failed on include file `%s`", filename); + vec_free(filename); + return false; + } + ftepp->lex = inlex; + old_includename = ftepp->includename; + ftepp->includename = filename; + if (!ftepp_preprocess(ftepp)) { + vec_free(ftepp->includename); + ftepp->includename = old_includename; + lex_close(ftepp->lex); + ftepp->lex = old_lexer; + return false; + } + vec_free(ftepp->includename); + ftepp->includename = old_includename; + lex_close(ftepp->lex); + ftepp->lex = old_lexer; + + ftepp_out(ftepp, "\n#pragma file(", false); + ftepp_out(ftepp, ctx.file, false); + util_snprintf(lineno, sizeof(lineno), ")\n#pragma line(%lu)\n", (unsigned long)(ctx.line+1)); + ftepp_out(ftepp, lineno, false); + + /* skip the line */ + (void)ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + if (ftepp->token != TOKEN_EOL) { + ftepp_error(ftepp, "stray tokens after #include"); + return false; + } + (void)ftepp_next(ftepp); + + return true; +} + +/* Basic structure handlers */ +static bool ftepp_else_allowed(ftepp_t *ftepp) +{ + if (!vec_size(ftepp->conditions)) { + ftepp_error(ftepp, "#else without #if"); + return false; + } + if (vec_last(ftepp->conditions).had_else) { + ftepp_error(ftepp, "multiple #else for a single #if"); + return false; + } + return true; +} + +static bool ftepp_hash(ftepp_t *ftepp) +{ + ppcondition cond; + ppcondition *pc; + + lex_ctx ctx = ftepp_ctx(ftepp); + + if (!ftepp_skipspace(ftepp)) + return false; + + switch (ftepp->token) { + case TOKEN_KEYWORD: + case TOKEN_IDENT: + case TOKEN_TYPENAME: + if (!strcmp(ftepp_tokval(ftepp), "define")) { + return ftepp_define(ftepp); + } + else if (!strcmp(ftepp_tokval(ftepp), "undef")) { + return ftepp_undef(ftepp); + } + else if (!strcmp(ftepp_tokval(ftepp), "ifdef")) { + if (!ftepp_ifdef(ftepp, &cond)) + return false; + cond.was_on = cond.on; + vec_push(ftepp->conditions, cond); + ftepp->output_on = ftepp->output_on && cond.on; + break; + } + else if (!strcmp(ftepp_tokval(ftepp), "ifndef")) { + if (!ftepp_ifdef(ftepp, &cond)) + return false; + cond.on = !cond.on; + cond.was_on = cond.on; + vec_push(ftepp->conditions, cond); + ftepp->output_on = ftepp->output_on && cond.on; + break; + } + else if (!strcmp(ftepp_tokval(ftepp), "elifdef")) { + if (!ftepp_else_allowed(ftepp)) + return false; + if (!ftepp_ifdef(ftepp, &cond)) + return false; + pc = &vec_last(ftepp->conditions); + pc->on = !pc->was_on && cond.on; + pc->was_on = pc->was_on || pc->on; + ftepp_update_output_condition(ftepp); + break; + } + else if (!strcmp(ftepp_tokval(ftepp), "elifndef")) { + if (!ftepp_else_allowed(ftepp)) + return false; + if (!ftepp_ifdef(ftepp, &cond)) + return false; + cond.on = !cond.on; + pc = &vec_last(ftepp->conditions); + pc->on = !pc->was_on && cond.on; + pc->was_on = pc->was_on || pc->on; + ftepp_update_output_condition(ftepp); + break; + } + else if (!strcmp(ftepp_tokval(ftepp), "elif")) { + if (!ftepp_else_allowed(ftepp)) + return false; + if (!ftepp_if(ftepp, &cond)) + return false; + pc = &vec_last(ftepp->conditions); + pc->on = !pc->was_on && cond.on; + pc->was_on = pc->was_on || pc->on; + ftepp_update_output_condition(ftepp); + break; + } + else if (!strcmp(ftepp_tokval(ftepp), "if")) { + if (!ftepp_if(ftepp, &cond)) + return false; + cond.was_on = cond.on; + vec_push(ftepp->conditions, cond); + ftepp->output_on = ftepp->output_on && cond.on; + break; + } + else if (!strcmp(ftepp_tokval(ftepp), "else")) { + if (!ftepp_else_allowed(ftepp)) + return false; + pc = &vec_last(ftepp->conditions); + pc->on = !pc->was_on; + pc->had_else = true; + ftepp_next(ftepp); + ftepp_update_output_condition(ftepp); + break; + } + else if (!strcmp(ftepp_tokval(ftepp), "endif")) { + if (!vec_size(ftepp->conditions)) { + ftepp_error(ftepp, "#endif without #if"); + return false; + } + vec_pop(ftepp->conditions); + ftepp_next(ftepp); + ftepp_update_output_condition(ftepp); + break; + } + else if (!strcmp(ftepp_tokval(ftepp), "include")) { + return ftepp_include(ftepp); + } + else if (!strcmp(ftepp_tokval(ftepp), "pragma")) { + ftepp_out(ftepp, "#", false); + break; + } + else if (!strcmp(ftepp_tokval(ftepp), "warning")) { + ftepp_directive_warning(ftepp); + break; + } + else if (!strcmp(ftepp_tokval(ftepp), "error")) { + ftepp_directive_error(ftepp); + break; + } + else if (!strcmp(ftepp_tokval(ftepp), "message")) { + ftepp_directive_message(ftepp); + break; + } + else { + if (ftepp->output_on) { + ftepp_error(ftepp, "unrecognized preprocessor directive: `%s`", ftepp_tokval(ftepp)); + return false; + } else { + ftepp_next(ftepp); + break; + } + } + /* break; never reached */ + default: + ftepp_error(ftepp, "unexpected preprocessor token: `%s`", ftepp_tokval(ftepp)); + return false; + case TOKEN_EOL: + ftepp_errorat(ftepp, ctx, "empty preprocessor directive"); + return false; + case TOKEN_EOF: + ftepp_error(ftepp, "missing newline at end of file", ftepp_tokval(ftepp)); + return false; + + /* Builtins! Don't forget the builtins! */ + case TOKEN_INTCONST: + case TOKEN_FLOATCONST: + ftepp_out(ftepp, "#", false); + return true; + } + if (!ftepp_skipspace(ftepp)) + return false; + return true; +} + +static bool ftepp_preprocess(ftepp_t *ftepp) +{ + ppmacro *macro; + bool newline = true; + + /* predef stuff */ + char *expand = NULL; + + ftepp->lex->flags.preprocessing = true; + ftepp->lex->flags.mergelines = false; + ftepp->lex->flags.noops = true; + + ftepp_next(ftepp); + do + { + if (ftepp->token >= TOKEN_EOF) + break; +#if 0 + newline = true; +#endif + + switch (ftepp->token) { + case TOKEN_KEYWORD: + case TOKEN_IDENT: + case TOKEN_TYPENAME: + /* is it a predef? */ + if (OPTS_FLAG(FTEPP_PREDEFS)) { + char *(*predef)(lex_file*) = ftepp_predef(ftepp_tokval(ftepp)); + if (predef) { + expand = predef(ftepp->lex); + ftepp_out (ftepp, expand, false); + ftepp_next(ftepp); + + mem_d(expand); + break; + } + } + + if (ftepp->output_on) + macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp)); + else + macro = NULL; + + if (!macro) { + ftepp_out(ftepp, ftepp_tokval(ftepp), false); + ftepp_next(ftepp); + break; + } + if (!ftepp_macro_call(ftepp, macro)) + ftepp->token = TOKEN_ERROR; + break; + case '#': + if (!newline) { + ftepp_out(ftepp, ftepp_tokval(ftepp), false); + ftepp_next(ftepp); + break; + } + ftepp->lex->flags.mergelines = true; + if (ftepp_next(ftepp) >= TOKEN_EOF) { + ftepp_error(ftepp, "error in preprocessor directive"); + ftepp->token = TOKEN_ERROR; + break; + } + if (!ftepp_hash(ftepp)) + ftepp->token = TOKEN_ERROR; + ftepp->lex->flags.mergelines = false; + break; + case TOKEN_EOL: + newline = true; + ftepp_out(ftepp, "\n", true); + ftepp_next(ftepp); + break; + case TOKEN_WHITE: + /* same as default but don't set newline=false */ + ftepp_out(ftepp, ftepp_tokval(ftepp), true); + ftepp_next(ftepp); + break; + default: + newline = false; + ftepp_out(ftepp, ftepp_tokval(ftepp), false); + ftepp_next(ftepp); + break; + } + } while (!ftepp->errors && ftepp->token < TOKEN_EOF); + + /* force a 0 at the end but don't count it as added to the output */ + vec_push(ftepp->output_string, 0); + vec_shrinkby(ftepp->output_string, 1); + + return (ftepp->token == TOKEN_EOF); +} + +/* Like in parser.c - files keep the previous state so we have one global + * preprocessor. Except here we will want to warn about dangling #ifs. + */ +static bool ftepp_preprocess_done(ftepp_t *ftepp) +{ + bool retval = true; + if (vec_size(ftepp->conditions)) { + if (ftepp_warn(ftepp, WARN_MULTIFILE_IF, "#if spanning multiple files, is this intended?")) + retval = false; + } + lex_close(ftepp->lex); + ftepp->lex = NULL; + if (ftepp->itemname) { + mem_d(ftepp->itemname); + ftepp->itemname = NULL; + } + return retval; +} + +bool ftepp_preprocess_file(ftepp_t *ftepp, const char *filename) +{ + ftepp->lex = lex_open(filename); + ftepp->itemname = util_strdup(filename); + if (!ftepp->lex) { + con_out("failed to open file \"%s\"\n", filename); + return false; + } + if (!ftepp_preprocess(ftepp)) + return false; + return ftepp_preprocess_done(ftepp); +} + +bool ftepp_preprocess_string(ftepp_t *ftepp, const char *name, const char *str) +{ + ftepp->lex = lex_open_string(str, strlen(str), name); + ftepp->itemname = util_strdup(name); + if (!ftepp->lex) { + con_out("failed to create lexer for string \"%s\"\n", name); + return false; + } + if (!ftepp_preprocess(ftepp)) + return false; + return ftepp_preprocess_done(ftepp); +} + + +void ftepp_add_macro(ftepp_t *ftepp, const char *name, const char *value) { + char *create = NULL; + + /* use saner path for empty macros */ + if (!value) { + ftepp_add_define(ftepp, "__builtin__", name); + return; + } + + vec_upload(create, "#define ", 8); + vec_upload(create, name, strlen(name)); + vec_push (create, ' '); + vec_upload(create, value, strlen(value)); + vec_push (create, 0); + + ftepp_preprocess_string(ftepp, "__builtin__", create); + vec_free (create); +} + +ftepp_t *ftepp_create() +{ + ftepp_t *ftepp; + char minor[32]; + char major[32]; + + ftepp = ftepp_new(); + if (!ftepp) + return NULL; + + memset(minor, 0, sizeof(minor)); + memset(major, 0, sizeof(major)); + + /* set the right macro based on the selected standard */ + ftepp_add_define(ftepp, NULL, "GMQCC"); + if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_FTEQCC) { + ftepp_add_define(ftepp, NULL, "__STD_FTEQCC__"); + /* 1.00 */ + major[0] = '"'; + major[1] = '1'; + major[2] = '"'; + + minor[0] = '"'; + minor[1] = '0'; + minor[2] = '"'; + } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_GMQCC) { + ftepp_add_define(ftepp, NULL, "__STD_GMQCC__"); + util_snprintf(major, 32, "\"%d\"", GMQCC_VERSION_MAJOR); + util_snprintf(minor, 32, "\"%d\"", GMQCC_VERSION_MINOR); + } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCCX) { + ftepp_add_define(ftepp, NULL, "__STD_QCCX__"); + util_snprintf(major, 32, "\"%d\"", GMQCC_VERSION_MAJOR); + util_snprintf(minor, 32, "\"%d\"", GMQCC_VERSION_MINOR); + } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) { + ftepp_add_define(ftepp, NULL, "__STD_QCC__"); + /* 1.0 */ + major[0] = '"'; + major[1] = '1'; + major[2] = '"'; + + minor[0] = '"'; + minor[1] = '0'; + minor[2] = '"'; + } + + ftepp_add_macro(ftepp, "__STD_VERSION_MINOR__", minor); + ftepp_add_macro(ftepp, "__STD_VERSION_MAJOR__", major); + + /* + * We're going to just make __NULL__ nil, which works for 60% of the + * cases of __NULL_ for fteqcc. + */ + ftepp_add_macro(ftepp, "__NULL__", "nil"); + + return ftepp; +} + +void ftepp_add_define(ftepp_t *ftepp, const char *source, const char *name) +{ + ppmacro *macro; + lex_ctx ctx = { "__builtin__", 0, 0 }; + ctx.file = source; + macro = ppmacro_new(ctx, name); + /*vec_push(ftepp->macros, macro);*/ + util_htset(ftepp->macros, name, macro); +} + +const char *ftepp_get(ftepp_t *ftepp) +{ + return ftepp->output_string; +} + +void ftepp_flush(ftepp_t *ftepp) +{ + ftepp_flush_do(ftepp); +} + +void ftepp_finish(ftepp_t *ftepp) +{ + if (!ftepp) + return; + ftepp_delete(ftepp); +} diff --git a/misc/source/gmqcc-src/gmqcc.h b/misc/source/gmqcc-src/gmqcc.h new file mode 100644 index 00000000..d6055dbe --- /dev/null +++ b/misc/source/gmqcc-src/gmqcc.h @@ -0,0 +1,1166 @@ +/* + * Copyright (C) 2012, 2013 + * Dale Weiler + * Wolfgang Bumiller + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef GMQCC_HDR +#define GMQCC_HDR +#include +#include /* TODO: remove this */ + +/* + * Disable some over protective warnings in visual studio because fixing them is a waste + * of my time. + */ +#ifdef _MSC_VER +# pragma warning(disable : 4244 ) /* conversion from 'int' to 'float', possible loss of data */ +#endif /*! _MSC_VER */ + +#define GMQCC_VERSION_MAJOR 0 +#define GMQCC_VERSION_MINOR 3 +#define GMQCC_VERSION_PATCH 0 +#define GMQCC_VERSION_BUILD(J,N,P) (((J)<<16)|((N)<<8)|(P)) +#define GMQCC_VERSION \ + GMQCC_VERSION_BUILD(GMQCC_VERSION_MAJOR, GMQCC_VERSION_MINOR, GMQCC_VERSION_PATCH) +/* Undefine the following on a release-tag: */ +#define GMQCC_VERSION_TYPE_DEVEL + +/* Full version string in case we need it */ +#ifdef GMQCC_VERSION_TYPE_DEVEL +# ifdef GMQCC_GITINFO +# define GMQCC_DEV_VERSION_STRING "git build: " GMQCC_GITINFO "\n" +# elif defined(GMQCC_VERSION_TYPE_DEVEL) +# define GMQCC_DEV_VERSION_STRING "development build\n" +# else +# define GMQCC_DEV_VERSION_STRING +# endif /*! GMQCC_GITINGO */ +#else +# define GMQCC_DEV_VERSION_STRING +#endif + +#define GMQCC_STRINGIFY(x) #x +#define GMQCC_IND_STRING(x) GMQCC_STRINGIFY(x) +#define GMQCC_FULL_VERSION_STRING \ +"GMQCC " \ +GMQCC_IND_STRING(GMQCC_VERSION_MAJOR) "." \ +GMQCC_IND_STRING(GMQCC_VERSION_MINOR) "." \ +GMQCC_IND_STRING(GMQCC_VERSION_PATCH) \ +" Built " __DATE__ " " __TIME__ \ +"\n" GMQCC_DEV_VERSION_STRING + +/* + * We cannot rely on C99 at all, since compilers like MSVC + * simply don't support it. We define our own boolean type + * as a result (since we cannot include ). For + * compilers that are in 1999 mode (C99 compliant) we can use + * the language keyword _Bool which can allow for better code + * on GCC and GCC-like compilers, opposed to `int`. + */ +#ifndef __cplusplus +# ifdef false +# undef false +# endif /*! false */ +# ifdef true +# undef true +# endif /*! true */ +# define false (0) +# define true (1) +# ifdef __STDC_VERSION__ +# if __STDC_VERSION__ < 199901L && __GNUC__ < 3 + typedef int bool; +# else + typedef _Bool bool; +# endif /*! __STDC_VERSION__ < 199901L && __GNUC__ < 3 */ +# else + typedef int bool; +# endif /*! __STDC_VERSION__ */ +#endif /*! __cplusplus */ + +/* + * Of some functions which are generated we want to make sure + * that the result isn't ignored. To find such function calls, + * we use this macro. + */ +#if defined(__GNUC__) || defined(__CLANG__) +# define GMQCC_WARN __attribute__((warn_unused_result)) +# define GMQCC_USED __attribute__((used)) +#else +# define GMQCC_WARN +# define GMQCC_USED +#endif /*! defined(__GNUC__) || defined (__CLANG__) */ +/* + * This is a hack to silent clang regarding empty + * body if statements. + */ +#define GMQCC_SUPPRESS_EMPTY_BODY do { } while (0) + +/* + * Inline is not supported in < C90, however some compilers + * like gcc and clang might have an inline attribute we can + * use if present. + */ +#ifdef __STDC_VERSION__ +# if __STDC_VERSION__ < 199901L +# if defined(__GNUC__) || defined (__CLANG__) +# if __GNUC__ < 2 +# define GMQCC_INLINE +# else +# define GMQCC_INLINE __attribute__ ((always_inline)) +# endif /*! __GNUC__ < 2 */ +# else +# define GMQCC_INLINE +# endif /*! defined(__GNUC__) || defined (__CLANG__) */ +# else +# define GMQCC_INLINE inline +# endif /*! __STDC_VERSION < 199901L */ +/* + * Visual studio has __forcinline we can use. So lets use that + * I suspect it also has just __inline of some sort, but our use + * of inline is correct (not guessed), WE WANT IT TO BE INLINE + */ +#elif defined(_MSC_VER) +# define GMQCC_INLINE __forceinline +#else +# define GMQCC_INLINE +#endif /*! __STDC_VERSION__ */ + +/* + * noreturn is present in GCC and clang + * it's required for _ast_node_destory otherwise -Wmissing-noreturn + * in clang complains about there being no return since abort() is + * called. + */ +#if (defined(__GNUC__) && __GNUC__ >= 2) || defined(__CLANG__) +# define GMQCC_NORETURN __attribute__ ((noreturn)) +#else +# define GMQCC_NORETURN +#endif /*! (defined(__GNUC__) && __GNUC__ >= 2) || defined (__CLANG__) */ + +#ifndef _MSC_VER +# include +#else + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; + typedef unsigned __int64 uint64_t; + + typedef __int16 int16_t; + typedef __int32 int32_t; + typedef __int64 int64_t; +#endif /*! _MSC_VER */ + +/* + * Very roboust way at determining endianess at compile time: this handles + * almost every possible situation. Otherwise a runtime check has to be + * performed. + */ +#define GMQCC_BYTE_ORDER_LITTLE 1234 +#define GMQCC_BYTE_ORDER_BIG 4321 + +#if defined (__GNUC__) || defined (__GNU_LIBRARY__) +# if defined (__FreeBSD__) || defined (__OpenBSD__) +# include +# elif defined (BSD) && (BSD >= 199103) || defined (__DJGPP__) || defined (__CYGWIN32__) +# include +# elif defined (__APPLE__) +# if defined (__BIG_ENDIAN__) && !defined(BIG_ENDIAN) +# define BIG_ENDIAN +# elif defined (__LITTLE_ENDIAN__) && !defined (LITTLE_ENDIAN) +# define LITTLE_ENDIAN +# endif /*! defined (__BIG_ENDIAN__) && !defined(BIG_ENDIAN) */ +# elif !defined (__MINGW32__) +# include +# if !defined (__BEOS__) +# include +# endif /*! !definde (__BEOS__) */ +# endif /*! defined (__FreeBSD__) || defined (__OpenBSD__) */ +#endif /*! defined (__GNUC__) || defined (__GNU_LIBRARY__) */ +#if !defined(PLATFORM_BYTE_ORDER) +# if defined (LITTLE_ENDIAN) || defined (BIG_ENDIAN) +# if defined (LITTLE_ENDIAN) && !defined(BIG_ENDIAN) +# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_LITTLE +# elif !defined (LITTLE_ENDIAN) && defined (BIG_ENDIAN) +# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_BIG +# elif defined (BYTE_ORDER) && (BYTE_ORDER == LITTLE_ENDIAN) +# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_LITTLE +# elif defined (BYTE_ORDER) && (BYTE_ORDER == BIG_ENDIAN) +# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_BIG +# endif /*! defined (LITTLE_ENDIAN) && !defined(BIG_ENDIAN) */ +# elif defined (_LITTLE_ENDIAN) || defined (_BIG_ENDIAN) +# if defined (_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) +# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_LITTLE +# elif !defined (_LITTLE_ENDIAN) && defined (_BIG_ENDIAN) +# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_BIG +# elif defined (_BYTE_ORDER) && (_BYTE_ORDER == _LITTLE_ENDIAN) +# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_LITTLE +# elif defined (_BYTE_ORDER) && (_BYTE_ORDER == _BIG_ENDIAN) +# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_BIG +# endif /*! defined (_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) */ +# elif defined (__LITTLE_ENDIAN__) || defined (__BIG_ENDIAN__) +# if defined (__LITTLE_ENDIAN__) && !defined (__BIG_ENDIAN__) +# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_LITTLE +# elif !defined (__LITTLE_ENDIAN__) && defined (__BIG_ENDIAN__) +# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_BIG +# elif defined (__BYTE_ORDER__) && (__BYTE_ORDER__ == __LITTLE_ENDIAN__) +# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_LITTLE +# elif defined (__BYTE_ORDER__) && (__BYTE_ORDER__ == __BIG_ENDIAN__) +# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_BIG +# endif /*! defined (__LITTLE_ENDIAN__) && !defined (__BIG_ENDIAN__) */ +# endif /*! defined(LITTLE_ENDIAN) || defined (BIG_ENDIAN) */ +#endif /*! !defined(PLATFORM_BYTE_ORDER) */ +#if !defined (PLATFORM_BYTE_ORDER) +# if defined (__alpha__) || defined (__alpha) || defined (i386) || \ + defined (__i386__) || defined (_M_I86) || defined (_M_IX86) || \ + defined (__OS2__) || defined (sun386) || defined (__TURBOC__) || \ + defined (vax) || defined (vms) || defined (VMS) || \ + defined (__VMS) || defined (__x86_64__) || defined (_M_IA64) || \ + defined (_M_X64) || defined (__i386) || defined (__x86_64) +# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_LITTLE +# elif defined (AMIGA) || defined (applec) || defined (__AS400__) || \ + defined (_CRAY) || defined (__hppa) || defined (__hp9000) || \ + defined (ibm370) || defined (mc68000) || defined (m68k) || \ + defined (__MRC__) || defined (__MVS__) || defined (__MWERKS__) || \ + defined (sparc) || defined (__sparc) || defined (SYMANTEC_C) || \ + defined (__TANDEM) || defined (THINK_C) || defined (__VMCMS__) || \ + defined (__PPC__) || defined (__PPC) || defined (PPC) +# define PLATFORM_BYTE_ORDER GMQCC_BYTE_ORDER_BIG +# else +# define PLATFORM_BYTE_ORDER -1 +# endif +#endif /*! !defined (PLATFORM_BYTE_ORDER) */ + +/* + * On windows systems where we're not compiling with MING32 we need a + * little extra help on dependinces for implementing our own dirent.h + * in fs.c. + */ +#if defined(_WIN32) && !defined(__MINGW32__) +# define _WIN32_LEAN_AND_MEAN +# include +# include +# include + + struct dirent { + long d_ino; + unsigned short d_reclen; + unsigned short d_namlen; + char d_name[FILENAME_MAX]; + }; + + typedef struct { + struct _finddata_t dd_dta; + struct dirent dd_dir; + long dd_handle; + int dd_stat; + char dd_name[1]; + } DIR; + /* + * Visual studio also lacks S_ISDIR for sys/stat.h, so we emulate this as well + * which is not hard at all. + */ +# ifdef S_ISDIR +# undef S_ISDIR +# endif /*! S_ISDIR */ +# define S_ISDIR(X) ((X)&_S_IFDIR) +#else +# include +#endif /*! _WIN32 && !defined(__MINGW32__) */ + +/*===================================================================*/ +/*=========================== stat.c ================================*/ +/*===================================================================*/ +void stat_info (void); +char *stat_mem_strdup (const char *, size_t, const char *, bool); +void *stat_mem_reallocate(void *, size_t, size_t, const char *); +void stat_mem_deallocate(void *); +void *stat_mem_allocate (size_t, size_t, const char *); + +#define mem_a(SIZE) stat_mem_allocate ((SIZE), __LINE__, __FILE__) +#define mem_d(PTRN) stat_mem_deallocate((void*)(PTRN)) +#define mem_r(PTRN, SIZE) stat_mem_reallocate((void*)(PTRN), (SIZE), __LINE__, __FILE__) +#define mem_af(SIZE, FILE, LINE) stat_mem_allocate ((SIZE), (LINE), (FILE)) + +/* TODO: rename to mem variations */ +#define util_strdup(SRC) stat_mem_strdup((char*)(SRC), __LINE__, __FILE__, false) +#define util_strdupe(SRC) stat_mem_strdup((char*)(SRC), __LINE__, __FILE__, true) + +/*===================================================================*/ +/*=========================== util.c ================================*/ +/*===================================================================*/ +bool util_filexists (const char *); +bool util_strupper (const char *); +bool util_strdigit (const char *); +void util_debug (const char *, const char *, ...); +void util_endianswap (void *, size_t, unsigned int); + +size_t util_strtocmd (const char *, char *, size_t); +size_t util_strtononcmd (const char *, char *, size_t); + +uint16_t util_crc16(uint16_t crc, const char *data, size_t len); + +void util_seed(uint32_t); +uint32_t util_rand(void); + +/* + * String functions (formatting, copying, concatenating, errors). These are wrapped + * to use the MSVC _safe_ versions when using MSVC, plus some implementations of + * these are non-conformant or don't exist such as asprintf and snprintf, which are + * not supported in C90, but do exist in C99. + */ +int util_vasprintf(char **ret, const char *fmt, va_list); +int util_asprintf (char **ret, const char *fmt, ...); +int util_snprintf (char *src, size_t bytes, const char *format, ...); +char *util_strcat (char *dest, const char *src); +char *util_strncpy (char *dest, const char *src, size_t num); +const char *util_strerror (int num); + +/* + * A flexible vector implementation: all vector pointers contain some + * data about themselfs exactly - sizeof(vector_t) behind the pointer + * this data is represented in the structure below. Doing this allows + * us to use the array [] to access individual elements from the vector + * opposed to using set/get methods. + */ +typedef struct { + size_t allocated; + size_t used; + + /* can be extended now! whoot */ +} vector_t; + +/* hidden interface */ +void _util_vec_grow(void **a, size_t i, size_t s); +#define GMQCC_VEC_WILLGROW(X,Y) ( \ + ((!(X) || vec_meta(X)->used + Y >= vec_meta(X)->allocated)) ? \ + (void)_util_vec_grow(((void**)&(X)), (Y), sizeof(*(X))) : \ + (void)0 \ +) + +/* exposed interface */ +#define vec_meta(A) (((vector_t*)((void*)A)) - 1) +#define vec_free(A) ((void)((A) ? (mem_d((void*)vec_meta(A)), (A) = NULL) : 0)) +#define vec_push(A,V) (GMQCC_VEC_WILLGROW((A),1), (A)[vec_meta(A)->used++] = (V)) +#define vec_size(A) ((A) ? vec_meta(A)->used : 0) +#define vec_add(A,N) (GMQCC_VEC_WILLGROW((A),(N)), vec_meta(A)->used += (N), &(A)[vec_meta(A)->used-(N)]) +#define vec_last(A) ((A)[vec_meta(A)->used - 1]) +#define vec_pop(A) ((void)(vec_meta(A)->used -= 1)) +#define vec_shrinkto(A,N) ((void)(vec_meta(A)->used = (N))) +#define vec_shrinkby(A,N) ((void)(vec_meta(A)->used -= (N))) +#define vec_append(A,N,S) ((void)(memcpy(vec_add((A), (N)), (S), (N) * sizeof(*(S))))) +#define vec_upload(X,Y,S) ((void)(memcpy(vec_add((X), (S) * sizeof(*(Y))), (Y), (S) * sizeof(*(Y))))) +#define vec_remove(A,I,N) ((void)(memmove((A)+(I),(A)+((I)+(N)),sizeof(*(A))*(vec_meta(A)->used-(I)-(N))),vec_meta(A)->used-=(N))) + +typedef struct trie_s { + void *value; + struct trie_s *entries; +} correct_trie_t; + +correct_trie_t* correct_trie_new(void); + +typedef struct hash_table_t { + size_t size; + struct hash_node_t **table; +} hash_table_t, *ht; + +/* + * hashtable implementation: + * + * Note: + * This was designed for pointers: you manage the life of the object yourself + * if you do use this for non-pointers please be warned that the object may not + * be valid if the duration of it exceeds (i.e on stack). So you need to allocate + * yourself, or put those in global scope to ensure duration is for the whole + * runtime. + * + * util_htnew(size) -- to make a new hashtable + * util_htset(table, key, value, sizeof(value)) -- to set something in the table + * util_htget(table, key) -- to get something from the table + * util_htdel(table) -- to delete the table + * + * example of use: + * + * ht foo = util_htnew(1024); + * int data = 100; + * char *test = "hello world\n"; + * util_htset(foo, "foo", (void*)&data); + * util_gtset(foo, "bar", (void*)test); + * + * printf("foo: %d, bar %s", + * *((int *)util_htget(foo, "foo")), + * ((char*)util_htget(foo, "bar")) + * ); + * + * util_htdel(foo); + */ +hash_table_t *util_htnew (size_t size); +void util_htrem (hash_table_t *ht, void (*callback)(void *data)); +void util_htset (hash_table_t *ht, const char *key, void *value); +void util_htdel (hash_table_t *ht); +size_t util_hthash(hash_table_t *ht, const char *key); +void util_htseth(hash_table_t *ht, const char *key, size_t hash, void *value); +void util_htrmh (hash_table_t *ht, const char *key, size_t bin, void (*cb)(void*)); +void util_htrm (hash_table_t *ht, const char *key, void (*cb)(void*)); + +void *util_htget (hash_table_t *ht, const char *key); +void *util_htgeth(hash_table_t *ht, const char *key, size_t hash); + +/*===================================================================*/ +/*============================ file.c ===============================*/ +/*===================================================================*/ +/* file handling */ +void fs_file_close (FILE *); +int fs_file_error (FILE *); +int fs_file_getc (FILE *); +int fs_file_printf (FILE *, const char *, ...); +int fs_file_puts (FILE *, const char *); +int fs_file_seek (FILE *, long int, int); +long int fs_file_tell (FILE *); + +size_t fs_file_read (void *, size_t, size_t, FILE *); +size_t fs_file_write (const void *, size_t, size_t, FILE *); + +FILE *fs_file_open (const char *, const char *); +int fs_file_getline(char **, size_t *, FILE *); + +/* directory handling */ +int fs_dir_make (const char *); +DIR *fs_dir_open (const char *); +int fs_dir_close (DIR *); +struct dirent *fs_dir_read (DIR *); + + +/*===================================================================*/ +/*=========================== correct.c =============================*/ +/*===================================================================*/ +typedef struct { + char ***edits; + size_t **lens; +} correction_t; + +void correct_del (correct_trie_t*, size_t **); +void correct_add (correct_trie_t*, size_t ***, const char *); +char *correct_str (correction_t *, correct_trie_t*, const char *); +void correct_init(correction_t *); +void correct_free(correction_t *); + +/*===================================================================*/ +/*=========================== code.c ================================*/ +/*===================================================================*/ + +/* TODO: cleanup */ +/* Note: if you change the order, fix type_sizeof in ir.c */ +enum { + TYPE_VOID , + TYPE_STRING , + TYPE_FLOAT , + TYPE_VECTOR , + TYPE_ENTITY , + TYPE_FIELD , + TYPE_FUNCTION , + TYPE_POINTER , + TYPE_INTEGER , + TYPE_VARIANT , + TYPE_STRUCT , + TYPE_UNION , + TYPE_ARRAY , + + TYPE_NIL , /* it's its own type / untyped */ + TYPE_NOEXPR , /* simply invalid in expressions */ + + TYPE_COUNT +}; + +/* const/var qualifiers */ +#define CV_NONE 0 +#define CV_CONST 1 +#define CV_VAR -1 +#define CV_WRONG 0x8000 /* magic number to help parsing */ + +extern const char *type_name [TYPE_COUNT]; +extern const uint16_t type_store_instr [TYPE_COUNT]; +extern const uint16_t field_store_instr[TYPE_COUNT]; + +/* + * could use type_store_instr + INSTR_STOREP_F - INSTR_STORE_F + * but this breaks when TYPE_INTEGER is added, since with the enhanced + * instruction set, the old ones are left untouched, thus the _I instructions + * are at a seperate place. + */ +extern const uint16_t type_storep_instr[TYPE_COUNT]; +extern const uint16_t type_eq_instr [TYPE_COUNT]; +extern const uint16_t type_ne_instr [TYPE_COUNT]; +extern const uint16_t type_not_instr [TYPE_COUNT]; + +typedef struct { + uint32_t offset; /* Offset in file of where data begins */ + uint32_t length; /* Length of section (how many of) */ +} prog_section; + +typedef struct { + uint32_t version; /* Program version (6) */ + uint16_t crc16; + uint16_t skip; + + prog_section statements; /* prog_section_statement */ + prog_section defs; /* prog_section_def */ + prog_section fields; /* prog_section_field */ + prog_section functions; /* prog_section_function */ + prog_section strings; + prog_section globals; + uint32_t entfield; /* Number of entity fields */ +} prog_header; + +/* + * Each paramater incerements by 3 since vector types hold + * 3 components (x,y,z). + */ +#define OFS_NULL 0 +#define OFS_RETURN 1 +#define OFS_PARM0 (OFS_RETURN+3) +#define OFS_PARM1 (OFS_PARM0 +3) +#define OFS_PARM2 (OFS_PARM1 +3) +#define OFS_PARM3 (OFS_PARM2 +3) +#define OFS_PARM4 (OFS_PARM3 +3) +#define OFS_PARM5 (OFS_PARM4 +3) +#define OFS_PARM6 (OFS_PARM5 +3) +#define OFS_PARM7 (OFS_PARM6 +3) + +typedef struct { + uint16_t opcode; + + /* operand 1 */ + union { + int16_t s1; /* signed */ + uint16_t u1; /* unsigned */ + } o1; + /* operand 2 */ + union { + int16_t s1; /* signed */ + uint16_t u1; /* unsigned */ + } o2; + /* operand 3 */ + union { + int16_t s1; /* signed */ + uint16_t u1; /* unsigned */ + } o3; + + /* + * This is the same as the structure in darkplaces + * { + * unsigned short op; + * short a,b,c; + * } + * But this one is more sane to work with, and the + * type sizes are guranteed. + */ +} prog_section_statement; + +typedef struct { + /* + * The types: + * 0 = ev_void + * 1 = ev_string + * 2 = ev_float + * 3 = ev_vector + * 4 = ev_entity + * 5 = ev_field + * 6 = ev_function + * 7 = ev_pointer -- engine only + * 8 = ev_bad -- engine only + */ + uint16_t type; + uint16_t offset; + uint32_t name; +} prog_section_both; + +typedef prog_section_both prog_section_def; +typedef prog_section_both prog_section_field; + +/* this is ORed to the type */ +#define DEF_SAVEGLOBAL (1<<15) +#define DEF_TYPEMASK ((1<<15)-1) + +typedef struct { + int32_t entry; /* in statement table for instructions */ + uint32_t firstlocal; /* First local in local table */ + uint32_t locals; /* Total ints of params + locals */ + uint32_t profile; /* Always zero (engine uses this) */ + uint32_t name; /* name of function in string table */ + uint32_t file; /* file of the source file */ + int32_t nargs; /* number of arguments */ + uint8_t argsize[8]; /* size of arguments (keep 8 always?) */ +} prog_section_function; + +/* + * Instructions + * These are the external instructions supported by the interperter + * this is what things compile to (from the C code). + */ +enum { + INSTR_DONE, + INSTR_MUL_F, + INSTR_MUL_V, + INSTR_MUL_FV, /* NOTE: the float operands must NOT be at the same locations: A != C */ + INSTR_MUL_VF, /* and here: B != C */ + INSTR_DIV_F, + INSTR_ADD_F, + INSTR_ADD_V, + INSTR_SUB_F, + INSTR_SUB_V, + INSTR_EQ_F, + INSTR_EQ_V, + INSTR_EQ_S, + INSTR_EQ_E, + INSTR_EQ_FNC, + INSTR_NE_F, + INSTR_NE_V, + INSTR_NE_S, + INSTR_NE_E, + INSTR_NE_FNC, + INSTR_LE, + INSTR_GE, + INSTR_LT, + INSTR_GT, + INSTR_LOAD_F, + INSTR_LOAD_V, + INSTR_LOAD_S, + INSTR_LOAD_ENT, + INSTR_LOAD_FLD, + INSTR_LOAD_FNC, + INSTR_ADDRESS, + INSTR_STORE_F, + INSTR_STORE_V, + INSTR_STORE_S, + INSTR_STORE_ENT, + INSTR_STORE_FLD, + INSTR_STORE_FNC, + INSTR_STOREP_F, + INSTR_STOREP_V, + INSTR_STOREP_S, + INSTR_STOREP_ENT, + INSTR_STOREP_FLD, + INSTR_STOREP_FNC, + INSTR_RETURN, + INSTR_NOT_F, + INSTR_NOT_V, + INSTR_NOT_S, + INSTR_NOT_ENT, + INSTR_NOT_FNC, + INSTR_IF, + INSTR_IFNOT, + INSTR_CALL0, + INSTR_CALL1, + INSTR_CALL2, + INSTR_CALL3, + INSTR_CALL4, + INSTR_CALL5, + INSTR_CALL6, + INSTR_CALL7, + INSTR_CALL8, + INSTR_STATE, + INSTR_GOTO, + INSTR_AND, + INSTR_OR, + INSTR_BITAND, + INSTR_BITOR, + + /* + * Virtual instructions used by the IR + * Keep at the end! + */ + VINSTR_END, + VINSTR_PHI, + VINSTR_JUMP, + VINSTR_COND, + /* A never returning CALL. + * Creating this causes IR blocks to be marked as 'final'. + * No-Return-Call + */ + VINSTR_NRCALL +}; + +/* uhh? */ +typedef float qcfloat; +typedef int32_t qcint; + +typedef struct { + prog_section_statement *statements; + int *linenums; + prog_section_def *defs; + prog_section_field *fields; + prog_section_function *functions; + int *globals; + char *chars; + uint16_t crc; + uint32_t entfields; + ht string_cache; + qcint string_cached_empty; +} code_t; + +/* + * code_write -- writes out the compiled file + * code_init -- prepares the code file + * code_genstrin -- generates string for code + * code_alloc_field -- allocated a field + * code_push_statement -- keeps statements and linenumbers together + * code_pop_statement -- keeps statements and linenumbers together + */ +bool code_write (code_t *, const char *filename, const char *lno); +GMQCC_WARN +code_t *code_init (void); +void code_cleanup (code_t *); +uint32_t code_genstring (code_t *, const char *string); +qcint code_alloc_field (code_t *, size_t qcsize); +void code_push_statement(code_t *, prog_section_statement *stmt, int linenum); +void code_pop_statement (code_t *); + +/* + * A shallow copy of a lex_file to remember where which ast node + * came from. + */ +typedef struct { + const char *file; + size_t line; + size_t column; +} lex_ctx; + +/*===================================================================*/ +/*============================ con.c ================================*/ +/*===================================================================*/ +enum { + CON_BLACK = 30, + CON_RED, + CON_GREEN, + CON_BROWN, + CON_BLUE, + CON_MAGENTA, + CON_CYAN , + CON_WHITE +}; + +/* message level */ +enum { + LVL_MSG, + LVL_WARNING, + LVL_ERROR +}; + +FILE *con_default_out(void); +FILE *con_default_err(void); + +void con_vprintmsg (int level, const char *name, size_t line, size_t column, const char *msgtype, const char *msg, va_list ap); +void con_printmsg (int level, const char *name, size_t line, size_t column, const char *msgtype, const char *msg, ...); +void con_cvprintmsg(void *ctx, int lvl, const char *msgtype, const char *msg, va_list ap); +void con_cprintmsg (void *ctx, int lvl, const char *msgtype, const char *msg, ...); + +void con_close (void); +void con_init (void); +void con_reset (void); +void con_color (int); +int con_change(const char *, const char *); +int con_verr (const char *, va_list); +int con_vout (const char *, va_list); +int con_err (const char *, ...); +int con_out (const char *, ...); + +/* error/warning interface */ +extern size_t compile_errors; +extern size_t compile_Werrors; +extern size_t compile_warnings; + +void /********/ compile_error (lex_ctx ctx, /*LVL_ERROR*/ const char *msg, ...); +void /********/ vcompile_error (lex_ctx ctx, /*LVL_ERROR*/ const char *msg, va_list ap); +bool GMQCC_WARN compile_warning (lex_ctx ctx, int warntype, const char *fmt, ...); +bool GMQCC_WARN vcompile_warning(lex_ctx ctx, int warntype, const char *fmt, va_list ap); +void compile_show_werrors(void); + +/*===================================================================*/ +/*========================= assembler.c =============================*/ +/*===================================================================*/ +/* TODO: remove this ... */ +static const struct { + const char *m; /* menomic */ + const size_t o; /* operands */ + const size_t l; /* menomic len */ +} asm_instr[] = { + { "DONE" , 1, 4 }, + { "MUL_F" , 3, 5 }, + { "MUL_V" , 3, 5 }, + { "MUL_FV" , 3, 6 }, + { "MUL_VF" , 3, 6 }, + { "DIV" , 0, 3 }, + { "ADD_F" , 3, 5 }, + { "ADD_V" , 3, 5 }, + { "SUB_F" , 3, 5 }, + { "SUB_V" , 3, 5 }, + { "EQ_F" , 0, 4 }, + { "EQ_V" , 0, 4 }, + { "EQ_S" , 0, 4 }, + { "EQ_E" , 0, 4 }, + { "EQ_FNC" , 0, 6 }, + { "NE_F" , 0, 4 }, + { "NE_V" , 0, 4 }, + { "NE_S" , 0, 4 }, + { "NE_E" , 0, 4 }, + { "NE_FNC" , 0, 6 }, + { "LE" , 0, 2 }, + { "GE" , 0, 2 }, + { "LT" , 0, 2 }, + { "GT" , 0, 2 }, + { "FIELD_F" , 0, 7 }, + { "FIELD_V" , 0, 7 }, + { "FIELD_S" , 0, 7 }, + { "FIELD_ENT" , 0, 9 }, + { "FIELD_FLD" , 0, 9 }, + { "FIELD_FNC" , 0, 9 }, + { "ADDRESS" , 0, 7 }, + { "STORE_F" , 0, 7 }, + { "STORE_V" , 0, 7 }, + { "STORE_S" , 0, 7 }, + { "STORE_ENT" , 0, 9 }, + { "STORE_FLD" , 0, 9 }, + { "STORE_FNC" , 0, 9 }, + { "STOREP_F" , 0, 8 }, + { "STOREP_V" , 0, 8 }, + { "STOREP_S" , 0, 8 }, + { "STOREP_ENT", 0, 10}, + { "STOREP_FLD", 0, 10}, + { "STOREP_FNC", 0, 10}, + { "RETURN" , 0, 6 }, + { "NOT_F" , 0, 5 }, + { "NOT_V" , 0, 5 }, + { "NOT_S" , 0, 5 }, + { "NOT_ENT" , 0, 7 }, + { "NOT_FNC" , 0, 7 }, + { "IF" , 0, 2 }, + { "IFNOT" , 0, 5 }, + { "CALL0" , 1, 5 }, + { "CALL1" , 2, 5 }, + { "CALL2" , 3, 5 }, + { "CALL3" , 4, 5 }, + { "CALL4" , 5, 5 }, + { "CALL5" , 6, 5 }, + { "CALL6" , 7, 5 }, + { "CALL7" , 8, 5 }, + { "CALL8" , 9, 5 }, + { "STATE" , 0, 5 }, + { "GOTO" , 0, 4 }, + { "AND" , 0, 3 }, + { "OR" , 0, 2 }, + { "BITAND" , 0, 6 }, + { "BITOR" , 0, 5 }, + + { "END" , 0, 3 } /* virtual assembler instruction */ +}; +/*===================================================================*/ +/*============================= ir.c ================================*/ +/*===================================================================*/ + +enum store_types { + store_global, + store_local, /* local, assignable for now, should get promoted later */ + store_param, /* parameters, they are locals with a fixed position */ + store_value, /* unassignable */ + store_return /* unassignable, at OFS_RETURN */ +}; + +typedef struct { + qcfloat x, y, z; +} vector; + +vector vec3_add (vector, vector); +vector vec3_sub (vector, vector); +qcfloat vec3_mulvv(vector, vector); +vector vec3_mulvf(vector, float); + +/*===================================================================*/ +/*============================= exec.c ==============================*/ +/*===================================================================*/ + +/* TODO: cleanup */ +/* + * Darkplaces has (or will have) a 64 bit prog loader + * where the 32 bit qc program is autoconverted on load. + * Since we may want to support that as well, let's redefine + * float and int here. + */ +typedef union { + qcint _int; + qcint string; + qcint function; + qcint edict; + qcfloat _float; + qcfloat vector[3]; + qcint ivector[3]; +} qcany; + +typedef char qcfloat_size_is_correct [sizeof(qcfloat) == 4 ?1:-1]; +typedef char qcint_size_is_correct [sizeof(qcint) == 4 ?1:-1]; + +enum { + VMERR_OK, + VMERR_TEMPSTRING_ALLOC, + + VMERR_END +}; + +#define VM_JUMPS_DEFAULT 1000000 + +/* execute-flags */ +#define VMXF_DEFAULT 0x0000 /* default flags - nothing */ +#define VMXF_TRACE 0x0001 /* trace: print statements before executing */ +#define VMXF_PROFILE 0x0002 /* profile: increment the profile counters */ + +struct qc_program_s; + +typedef int (*prog_builtin)(struct qc_program_s *prog); + +typedef struct { + qcint stmt; + size_t localsp; + prog_section_function *function; +} qc_exec_stack; + +typedef struct qc_program_s { + char *filename; + + prog_section_statement *code; + prog_section_def *defs; + prog_section_def *fields; + prog_section_function *functions; + char *strings; + qcint *globals; + qcint *entitydata; + bool *entitypool; + + const char* *function_stack; + + uint16_t crc16; + + size_t tempstring_start; + size_t tempstring_at; + + qcint vmerror; + + size_t *profile; + + prog_builtin *builtins; + size_t builtins_count; + + /* size_t ip; */ + qcint entities; + size_t entityfields; + bool allowworldwrites; + + qcint *localstack; + qc_exec_stack *stack; + size_t statement; + + size_t xflags; + + int argc; /* current arg count for debugging */ +} qc_program; + +qc_program* prog_load(const char *filename, bool ignoreversion); +void prog_delete(qc_program *prog); + +bool prog_exec(qc_program *prog, prog_section_function *func, size_t flags, long maxjumps); + +const char* prog_getstring (qc_program *prog, qcint str); +prog_section_def* prog_entfield (qc_program *prog, qcint off); +prog_section_def* prog_getdef (qc_program *prog, qcint off); +qcany* prog_getedict (qc_program *prog, qcint e); +qcint prog_tempstring(qc_program *prog, const char *_str); + + +/*===================================================================*/ +/*===================== parser.c commandline ========================*/ +/*===================================================================*/ +struct parser_s; +struct parser_s *parser_create (void); +bool parser_compile_file (struct parser_s *parser, const char *); +bool parser_compile_string(struct parser_s *parser, const char *, const char *, size_t); +bool parser_finish (struct parser_s *parser, const char *); +void parser_cleanup (struct parser_s *parser); + +/*===================================================================*/ +/*====================== ftepp.c commandline ========================*/ +/*===================================================================*/ +struct ftepp_s; +struct ftepp_s *ftepp_create (void); +bool ftepp_preprocess_file (struct ftepp_s *ftepp, const char *filename); +bool ftepp_preprocess_string(struct ftepp_s *ftepp, const char *name, const char *str); +void ftepp_finish (struct ftepp_s *ftepp); +const char *ftepp_get (struct ftepp_s *ftepp); +void ftepp_flush (struct ftepp_s *ftepp); +void ftepp_add_define (struct ftepp_s *ftepp, const char *source, const char *name); +void ftepp_add_macro (struct ftepp_s *ftepp, const char *name, const char *value); + +/*===================================================================*/ +/*======================= main.c commandline ========================*/ +/*===================================================================*/ + +#if 1 +/* Helpers to allow for a whole lot of flags. Otherwise we'd limit + * to 32 or 64 -f options... + */ +typedef struct { + size_t idx; /* index into an array of 32 bit words */ + uint8_t bit; /* bit index for the 8 bit group idx points to */ +} longbit; +#define LONGBIT(bit) { ((bit)/32), ((bit)%32) } +#define LONGBIT_SET(B, I) ((B).idx = (I)/32, (B).bit = ((I)%32)) +#else +typedef uint32_t longbit; +#define LONGBIT(bit) (bit) +#define LONGBIT_SET(B, I) ((B) = (I)) +#endif + +/*===================================================================*/ +/*=========================== utf8lib.c =============================*/ +/*===================================================================*/ +typedef uint32_t uchar_t; + +bool u8_analyze (const char *_s, size_t *_start, size_t *_len, uchar_t *_ch, size_t _maxlen); +size_t u8_strlen (const char*); +size_t u8_strnlen (const char*, size_t); +uchar_t u8_getchar (const char*, const char**); +uchar_t u8_getnchar(const char*, const char**, size_t); +int u8_fromchar(uchar_t w, char *to, size_t maxlen); + +/*===================================================================*/ +/*============================= opts.c ==============================*/ +/*===================================================================*/ +typedef struct { + const char *name; + longbit bit; +} opts_flag_def; + +bool opts_setflag (const char *, bool); +bool opts_setwarn (const char *, bool); +bool opts_setwerror(const char *, bool); +bool opts_setoptim (const char *, bool); + +void opts_init (const char *, int, size_t); +void opts_set (uint32_t *, size_t, bool); +void opts_setoptimlevel(unsigned int); +void opts_ini_init (const char *); + +/* Saner flag handling */ +void opts_backup_non_Wall(void); +void opts_restore_non_Wall(void); +void opts_backup_non_Werror_all(void); +void opts_restore_non_Werror_all(void); + +enum { +# define GMQCC_TYPE_FLAGS +# define GMQCC_DEFINE_FLAG(X) X, +# include "opts.def" + COUNT_FLAGS +}; +static const opts_flag_def opts_flag_list[] = { +# define GMQCC_TYPE_FLAGS +# define GMQCC_DEFINE_FLAG(X) { #X, LONGBIT(X) }, +# include "opts.def" + { NULL, LONGBIT(0) } +}; + +enum { +# define GMQCC_TYPE_WARNS +# define GMQCC_DEFINE_FLAG(X) WARN_##X, +# include "opts.def" + COUNT_WARNINGS +}; +static const opts_flag_def opts_warn_list[] = { +# define GMQCC_TYPE_WARNS +# define GMQCC_DEFINE_FLAG(X) { #X, LONGBIT(WARN_##X) }, +# include "opts.def" + { NULL, LONGBIT(0) } +}; + +enum { +# define GMQCC_TYPE_OPTIMIZATIONS +# define GMQCC_DEFINE_FLAG(NAME, MIN_O) OPTIM_##NAME, +# include "opts.def" + COUNT_OPTIMIZATIONS +}; +static const opts_flag_def opts_opt_list[] = { +# define GMQCC_TYPE_OPTIMIZATIONS +# define GMQCC_DEFINE_FLAG(NAME, MIN_O) { #NAME, LONGBIT(OPTIM_##NAME) }, +# include "opts.def" + { NULL, LONGBIT(0) } +}; +static const unsigned int opts_opt_oflag[] = { +# define GMQCC_TYPE_OPTIMIZATIONS +# define GMQCC_DEFINE_FLAG(NAME, MIN_O) MIN_O, +# include "opts.def" + 0 +}; + +enum { +# define GMQCC_TYPE_OPTIONS +# define GMQCC_DEFINE_FLAG(X) OPTION_##X, +# include "opts.def" + OPTION_COUNT +}; + +extern unsigned int opts_optimizationcount[COUNT_OPTIMIZATIONS]; + +/* other options: */ +typedef enum { + COMPILER_QCC, /* circa QuakeC */ + COMPILER_FTEQCC, /* fteqcc QuakeC */ + COMPILER_QCCX, /* qccx QuakeC */ + COMPILER_GMQCC /* this QuakeC */ +} opts_std_t; + +typedef union { + bool B; + uint16_t U16; + uint32_t U32; + char *STR; +} opt_value_t; + + +typedef struct { + opt_value_t options [OPTION_COUNT]; + uint32_t flags [1 + (COUNT_FLAGS / 32)]; + uint32_t warn [1 + (COUNT_WARNINGS / 32)]; + uint32_t werror [1 + (COUNT_WARNINGS / 32)]; + uint32_t warn_backup [1 + (COUNT_WARNINGS / 32)]; + uint32_t werror_backup[1 + (COUNT_WARNINGS / 32)]; + uint32_t optimization [1 + (COUNT_OPTIMIZATIONS / 32)]; + bool optimizeoff; /* True when -O0 */ +} opts_cmd_t; + +extern opts_cmd_t opts; + +#define OPTS_GENERIC(f,i) (!! (((f)[(i)/32]) & (1<< (unsigned)((i)%32)))) +#define OPTS_FLAG(i) OPTS_GENERIC(opts.flags, (i)) +#define OPTS_WARN(i) OPTS_GENERIC(opts.warn, (i)) +#define OPTS_WERROR(i) OPTS_GENERIC(opts.werror, (i)) +#define OPTS_OPTIMIZATION(i) OPTS_GENERIC(opts.optimization, (i)) +#define OPTS_OPTION_BOOL(X) (opts.options[X].B) +#define OPTS_OPTION_U16(X) (opts.options[X].U16) +#define OPTS_OPTION_U32(X) (opts.options[X].U32) +#define OPTS_OPTION_STR(X) (opts.options[X].STR) + +#endif /*! GMQCC_HDR */ diff --git a/misc/source/gmqcc-src/gmqcc.ini.example b/misc/source/gmqcc-src/gmqcc.ini.example new file mode 100644 index 00000000..42aec66e --- /dev/null +++ b/misc/source/gmqcc-src/gmqcc.ini.example @@ -0,0 +1,267 @@ +# This is an example INI file that can be used to set compiler options +# without the rquirement for supplying them as arguments on the command +# line, this can be coupled with progs.src. To utilize this file there +# are two options availble, if it's named "gmqcc.ini" or "gmqcc.cfg" and +# the file exists in the directory that GMQCC is invoked from, the compiler +# will implicitally find and execute regardless. For more freedom you may +# use -config= from the command line to specify a more explicit +# directory/name.ext for the configuration file. + + +# These are common compiler flags usually represented via the -f prefix +# from the command line. +[flags] + # Enabling this can potentially reduces code size by overlapping + # locals where possible. + OVERLAP_LOCALS = false + + # in some older versions of the Darkplaces engine the string table + # size is computed wrong causing compiled progs.dat to fail to load + # Enabling this works around the bug by writing a few additional + # null bytes to the end of the string table to compensate. + DARKPLACES_STRING_TABLE_BUG = false + + # Enabling this corrects the assignment of vector field pointers via + # subsituting STORE_FLD with STORE_V. + ADJUST_VECTOR_FIELDS = true + + # Enabling this allows the use of the FTEQ preprocessor, as well as + # additional preprocessing directives such as #error and #warning. + FTEPP = true + + # Enabling this relaxes switch statement semantics + RELAXED_SWITCH = false + + # Enabling this allows short-circut evaluation and logic, opposed + # to full evaluation. + SHORT_LOGIC = false + + # Enabling this allows perl-like evaluation/logic. + PERL_LOGIC = true + + # Enabling this allows the use of the "translatable strings" extension + # assisted by .po files. + TRANSLATABLE_STRINGS = false + + # Enabling this prevents initializations from becoming constant unless + # 'const' is specified as a type qualifier. + INITIALIZED_NONCONSTANTS = false + + # Enabling this allows function types to be assignable even if their + # signatures are invariant of each other. + ASSIGN_FUNCTION_TYPES = false + + # Enabling this will allow the generation of .lno files for engine + # virtual machine backtraces (this is enabled with -g as well). + LNO = false + + # Enabling this corrects ternary percedence bugs present in fteqcc. + CORRECT_TERNARY = true + + # Prevent the creation of _x, _y and _z progdefs for vectors + SINGLE_VECTOR_DEFS = false + + # Cast vectors to real booleans when used in logic expressions. + # This is achieved by using NOT_V. + CORRECT_LOGIC = false + + # Always treat empty strings as true. Usuall !"" yields true, because + # the string-NOT instruction considers empty strings to be false, while + # an empty string as condition for 'if' will be considered true, since + # only the numerical value of the global is looked at. + TRUE_EMPTY_STRINGS = false + + # Opposite of the above, empty strings are always false. Similar to + # CORRECT_LOGIC this will always use NOT_S to cast a string to a real + # boolean value. + FALSE_EMPTY_STRINGS = false + + # Recognize utf-8 characters in character constants, and encode + # codepoint escape sequences in strings as utf-8. This essentially allows + # \{1234} escape sequences to be higher than 255. + UTF8 + + # When a warning is printed and it is set to be treated as error via + # a -Werror switch, compilation will be stopped, unless this is false. + # When this is false, the rest of the code will be compiled, and at the end + # the file and line of the first warning will be shown. + BAIL_ON_WERROR = true + + # Allow loops and switches to be labeled and break and continue to take an + # optional label to target a specific loop/switch. + LOOP_LABELS = false + + # Enable the 'nil' null constant, which has no type. It can be used as the + # right hand of any assignment regardless of the required type. + UNTYPED_NIL = false + + # Be "permissive". For instance, when -funtyped-nil is used, this allows local + # variables with the name 'nil' to be declared. + PREMISSIVE = false + + # Allow vararg access from within QC of the form: ...(argnumber, type) + VARIADIC_ARGS = true + + # Most Quake VMs, including the one from FTEQW or up till recently + # Darkplaces, do not cope well with vector instructions with overlapping + # input and output. This option will avoid producing such code. + LEGACY_VECTOR_MATHS = true + + # Builtin-numbers are usually just immediate constants. + # The following allows whole expressions to be used, as long as they + # are compile-time constant. + EXPRESSIONS_FOR_BUILTINS = false + +# These are all the warnings, usually present via the -W prefix from +# the command line. +[warnings] + # ?? Used for debugging ?? + DEBUG = false + + # Enables warnings about unused variables. + UNUSED_VARIABLE = true + + # Enables warnings about uninitialized variables. + USED_UNINITIALIZED = true + + # Enables warnings about the unknown control sequences in the source + # stream. + UNKNOWN_CONTROL_SEQUENCE = true + + # Enables warnings about the use of (an) extension(s). + EXTENSIONS = true + + # Enables warnings about redeclared fields. + FIELD_REDECLARED = true + + # Enables warnings about missing return values. + MISSING_RETURN_VALUES = true + + # Enables warnings about missing parameters for function calls. + INVALID_PARAMETER_COUNT = true + + # Enables warnings about locals shadowing parameters or other locals. + LOCAL_SHADOWS = true + + # Enables warnings about constants specified as locals. + LOCAL_CONSTANTS = true + + # Enables warnings about variables declared as type void. + VOID_VARIABLES = true + + # Enables warnings about implicitally declared function pointers. + IMPLICIT_FUNCTION_POINTER = true + + # Enables warnings for use of varadics for non-builtin functions. + VARIADIC_FUNCTION = true + + # Enables warnings about duplicated frame macros. + FRAME_MACROS = true + + # Enables warnings about effectivless statements. + EFFECTLESS_STATEMENT = true + + # Enables warnings of "end_sys_fields" beiing declared a field. + END_SYS_FIELDS = true + + # Enables warnings for infompatible function pointer signatures used + # in assignment. + ASSIGN_FUNCTION_TYPES = true + + # Enables warnings about redefined macros in the preprocessor + PREPROCESSOR = true + + # Enables warnings about multi-file if statements + MULTIFILE_IF = true + + # Enables warnings about double declarations + DOUBLE_DECLARATION = true + + # Enables warnings about type qualifiers containing both 'var' and + # 'const' + CONST_VAR = true + + # Enables warnings about the use of multibytes characters / constants + MULTIBYTE_CHARACTER = true + + # Enables warnings about ternary expressions whos precedence may be + # not what was initially expected. + TERNARY_PRECEDENCE = true + + # Enables warnings about unknown pragmas. + UNKNOWN_PRAGMAS = true + + # Enables warnings about unreachable code. + UNREACHABLE_CODE = true + + # Enables preprocessor "#warnings" + CPP = true + + # With the [[attribute]] syntax enabled, warn when an unknown + # attribute is encountered. Its first token will be included in the + # message. + UNKNOWN_ATTRIBUTE = true + + # Warn when declaring variables or fields with a reserved name like 'nil' + RESERVED_NAMES = true + + # Warn about 'const'-qualified global variables with no initializing value. + UNINITIALIZED_CONSTANT = true + + # Warn about non-constant global variables with no initializing value. + UNINITIALIZED_GLOBAL = true + + # Redeclaring a 'const' as 'var' or the other way round. + DIFFERENT_QUALIFIERS = true + + # Redeclaring a function with different attributes such as + # [[noreturn]] + DIFFERENT_ATTRIBUTES = true + + # Warn when a function is marked with the attribute + # "[[deprecated]]". This flag enables a warning on calls to functions + # marked as such. + DEPRECATED = true + + # Warn about possible problems from missing parenthesis, like an + # assignment used as truth value without additional parens around. + PARENTHESIS = true + +# Finally these are all the optimizations, usually present via the -O +# prefix from the command line. +[optimizations] + # Enables peephole optimizations. + PEEPHOLE = true + + # Enables localtemp omission optimizations. + LOCALTEMPS = true + + # Enables tail recrusion optimizationd. + TAIL_RECURSION = true + + # Enables tail-call optimizations. (Not implemented) + TAIL_CALLS = true + + # Every function where it is safe to do so will share its local data section + # with the others. The criteria are currently that the function must not have + # any possibly uninitialized locals, or local arrays regardless of how they + # are initialized. + OVERLAP_LOCALS = false + + # Strip out the names of constants to save some space in the progs.dat + STRIP_CONSTANT_NAMES = true + + # Aggressivly reuse strings in the string-section + OVERLAP_STRINGS = true + + # Have expressions which are used as function parameters evaluate directly + # into the parameter-globals if possible. + # This avoids a whole lot of copying. + CALL_STORES = true + + # Do not create a RETURN instruction at the end functions of return-type void. + VOID_RETURN = true + + # Turn extraction-multiplications such as (a_vector * '0 1 0') + # into direct component accesses + VECTOR_COMPONENTS = true diff --git a/misc/source/gmqcc-src/intrin.h b/misc/source/gmqcc-src/intrin.h new file mode 100644 index 00000000..00464aaa --- /dev/null +++ b/misc/source/gmqcc-src/intrin.h @@ -0,0 +1,416 @@ +/* + * Copyright (C) 2012, 2013 + * Dale Weiler + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * Provides all the "intrinsics" / "builtins" for GMQCC. These can do + * a few things, they can provide fall back implementations for math + * functions if the definitions don't exist for some given engine. Or + * then can determine definitions for existing builtins, and simply + * wrap back to them instead. This is like a "portable" intrface that + * is entered when -fintrin is used (causing all existing builtins to + * be ignored by the compiler and instead interface through here. + */ +typedef struct { + ast_expression *(*intrin)(parser_t *); + const char *name; + const char *alias; +} intrin_t; + +static ht intrin_intrinsics(void) { + static ht intrinsics = NULL; + if (!intrinsics) + intrinsics = util_htnew(PARSER_HT_SIZE); + + return intrinsics; +} + +#define INTRIN_VAL(VALUE, NAME, FUNC, STYPE, VTYPE) \ + do { \ + (VALUE) = ast_value_new ( \ + parser_ctx(parser), \ + "__builtin_" NAME, \ + TYPE_FUNCTION \ + ); \ + (VALUE)->expression.next = (ast_expression*)ast_value_new ( \ + parser_ctx(parser), \ + STYPE, \ + VTYPE \ + ); \ + (FUNC) = ast_function_new ( \ + parser_ctx(parser), \ + "__builtin_" NAME, \ + (VALUE) \ + ); \ + } while (0) + +#define INTRIN_REG(FUNC, VALUE) \ + do { \ + vec_push(parser->functions, (FUNC)); \ + vec_push(parser->globals, (ast_expression*)(VALUE)); \ + } while (0) + +#define QC_M_E 2.71828182845905 + +static ast_expression *intrin_func(parser_t *parser, const char *name); +static ast_expression *intrin_pow (parser_t *parser) { + /* + * float pow(float x, float y) { + * float local = 1.0f; + * while (y > 0) { + * while (!(y & 1)) { + * y >>= 2; + * x *= x; + * } + * y--; + * local *= x; + * } + * return local; + * } + */ + static ast_value *value = NULL; + + if (!value) { + ast_value *arg1 = ast_value_new(parser_ctx(parser), "x", TYPE_FLOAT); + ast_value *arg2 = ast_value_new(parser_ctx(parser), "y", TYPE_FLOAT); + ast_value *local = ast_value_new(parser_ctx(parser), "local", TYPE_FLOAT); + ast_block *body = ast_block_new(parser_ctx(parser)); + ast_block *l1b = ast_block_new(parser_ctx(parser)); /* loop 1 body */ + ast_block *l2b = ast_block_new(parser_ctx(parser)); /* looo 2 body */ + ast_loop *loop1 = NULL; + ast_loop *loop2 = NULL; + ast_function *func = NULL; + + INTRIN_VAL(value, "pow", func, "", TYPE_FLOAT); + + /* arguments */ + vec_push(value->expression.params, arg1); + vec_push(value->expression.params, arg2); + + /* local */ + vec_push(body->locals, local); + + /* assignment to local of value 1.0f */ + vec_push(body->exprs, + (ast_expression*)ast_store_new ( + parser_ctx(parser), + INSTR_STORE_F, + (ast_expression*)local, + (ast_expression*)parser_const_float_1(parser) + ) + ); + + /* y >>= 2 */ + vec_push(l2b->exprs, + (ast_expression*)ast_binstore_new ( + parser_ctx(parser), + INSTR_STORE_F, + INSTR_MUL_F, + (ast_expression*)arg2, + (ast_expression*)parser_const_float(parser, 0.25f) + ) + ); + + /* x *= x */ + vec_push(l2b->exprs, + (ast_expression*)ast_binstore_new ( + parser_ctx(parser), + INSTR_STORE_F, + INSTR_MUL_F, + (ast_expression*)arg1, + (ast_expression*)arg1 + ) + ); + + /* while (!(y&1)) */ + loop2 = ast_loop_new ( + parser_ctx(parser), + NULL, + (ast_expression*)ast_binary_new ( + parser_ctx(parser), + INSTR_AND, + (ast_expression*)arg2, + (ast_expression*)parser_const_float_1(parser) + ), + true, /* ! not */ + NULL, + false, + NULL, + (ast_expression*)l2b + ); + + /* push nested loop into loop expressions */ + vec_push(l1b->exprs, (ast_expression*)loop2); + + /* y-- */ + vec_push(l1b->exprs, + (ast_expression*)ast_binstore_new ( + parser_ctx(parser), + INSTR_STORE_F, + INSTR_SUB_F, + (ast_expression*)arg2, + (ast_expression*)parser_const_float_1(parser) + ) + ); + /* local *= x */ + vec_push(l1b->exprs, + (ast_expression*)ast_binstore_new ( + parser_ctx(parser), + INSTR_STORE_F, + INSTR_MUL_F, + (ast_expression*)local, + (ast_expression*)arg1 + ) + ); + + /* while (y > 0) */ + loop1 = ast_loop_new ( + parser_ctx(parser), + NULL, + (ast_expression*)ast_binary_new ( + parser_ctx(parser), + INSTR_GT, + (ast_expression*)arg2, + (ast_expression*)parser_const_float_0(parser) + ), + false, + NULL, + false, + NULL, + (ast_expression*)l1b + ); + + /* push the loop1 into the body for the function */ + vec_push(body->exprs, (ast_expression*)loop1); + + /* return local; */ + vec_push(body->exprs, + (ast_expression*)ast_return_new ( + parser_ctx(parser), + (ast_expression*)local + ) + ); + + /* push block and register intrin for codegen */ + vec_push(func->blocks, body); + + INTRIN_REG(func, value); + } + + return (ast_expression*)value; +} + +static ast_expression *intrin_mod(parser_t *parser) { + /* + * float mod(float x, float y) { + * return x - y * floor(x / y); + * } + */ + static ast_value *value = NULL; + + if (!value) { + ast_call *call = ast_call_new (parser_ctx(parser), intrin_func(parser, "floor")); + ast_value *arg1 = ast_value_new(parser_ctx(parser), "x", TYPE_FLOAT); + ast_value *arg2 = ast_value_new(parser_ctx(parser), "y", TYPE_FLOAT); + ast_block *body = ast_block_new(parser_ctx(parser)); + ast_function *func = NULL; + + INTRIN_VAL(value, "mod", func, "", TYPE_FLOAT); + + /* floor(x/y) */ + vec_push(call->params, + (ast_expression*)ast_binary_new ( + parser_ctx(parser), + INSTR_DIV_F, + (ast_expression*)arg1, + (ast_expression*)arg2 + ) + ); + + vec_push(body->exprs, + (ast_expression*)ast_return_new( + parser_ctx(parser), + (ast_expression*)ast_binary_new( + parser_ctx(parser), + INSTR_SUB_F, + (ast_expression*)arg1, + (ast_expression*)ast_binary_new( + parser_ctx(parser), + INSTR_MUL_F, + (ast_expression*)arg2, + (ast_expression*)call + ) + ) + ) + ); + + vec_push(value->expression.params, arg1); /* float x (for param) */ + vec_push(value->expression.params, arg2); /* float y (for param) */ + + vec_push(func->blocks, body); /* {{{ body }}} */ + + INTRIN_REG(func, value); + } + + return (ast_expression*)value; +} + +static ast_expression *intrin_exp(parser_t *parser) { + /* + * float exp(float x) { + * return pow(QC_M_E, x); + * } + */ + static ast_value *value = NULL; + + if (!value) { + ast_call *call = ast_call_new (parser_ctx(parser), intrin_func(parser, "pow")); + ast_value *arg1 = ast_value_new (parser_ctx(parser), "x", TYPE_FLOAT); + ast_block *body = ast_block_new (parser_ctx(parser)); + ast_function *func = NULL; + + INTRIN_VAL(value, "exp", func, "", TYPE_FLOAT); + + /* push arguments for params to call */ + vec_push(call->params, (ast_expression*)parser_const_float(parser, QC_M_E)); + vec_push(call->params, (ast_expression*)arg1); + + /* return pow(QC_M_E, x) */ + vec_push(body->exprs, + (ast_expression*)ast_return_new( + parser_ctx(parser), + (ast_expression*)call + ) + ); + + vec_push(value->expression.params, arg1); /* float x (for param) */ + + vec_push(func->blocks, body); /* {{{ body }}} */ + + INTRIN_REG(func, value); + } + + return (ast_expression*)value; +} + +static ast_expression *intrin_isnan(parser_t *parser) { + /* + * float isnan(float x) { + * float local; + * local = x; + * + * return (x != local); + * } + */ + static ast_value *value = NULL; + + if (!value) { + ast_value *arg1 = ast_value_new (parser_ctx(parser), "x", TYPE_FLOAT); + ast_value *local = ast_value_new (parser_ctx(parser), "local", TYPE_FLOAT); + ast_block *body = ast_block_new (parser_ctx(parser)); + ast_function *func = NULL; + + INTRIN_VAL(value, "isnan", func, "", TYPE_FLOAT); + + vec_push(body->locals, local); + vec_push(body->exprs, + (ast_expression*)ast_store_new( + parser_ctx(parser), + INSTR_STORE_F, + (ast_expression*)local, + (ast_expression*)arg1 + ) + ); + + vec_push(body->exprs, + (ast_expression*)ast_return_new( + parser_ctx(parser), + (ast_expression*)ast_binary_new( + parser_ctx(parser), + INSTR_NE_F, + (ast_expression*)arg1, + (ast_expression*)local + ) + ) + ); + + vec_push(value->expression.params, arg1); + + vec_push(func->blocks, body); + + INTRIN_REG(func, value); + } + + return (ast_expression*)value; +} + +static intrin_t intrinsics[] = { + {&intrin_exp, "__builtin_exp", "exp"}, + {&intrin_mod, "__builtin_mod", "mod"}, + {&intrin_pow, "__builtin_pow", "pow"}, + {&intrin_isnan, "__builtin_isnan", "isnan"} +}; + +void intrin_intrinsics_destroy(parser_t *parser) { + /*size_t i;*/ + (void)parser; + util_htdel(intrin_intrinsics()); +#if 0 + for (i = 0; i < sizeof(intrinsics)/sizeof(intrin_t); i++) + ast_value_delete( (ast_value*) intrinsics[i].intrin(parser)); +#endif +} + + +static ast_expression *intrin_func(parser_t *parser, const char *name) { + static bool init = false; + size_t i = 0; + void *find; + + /* register the intrinsics in the hashtable for O(1) lookup */ + if (!init) { + for (i = 0; i < sizeof(intrinsics)/sizeof(*intrinsics); i++) + util_htset(intrin_intrinsics(), intrinsics[i].alias, &intrinsics[i]); + + init = true; /* only once */ + } + + /* + * jesus fucking christ, Blub design something less fucking + * impossible to use, like a ast_is_builtin(ast_expression *), also + * use a hashtable :P + */ + if ((find = (void*)parser_find_global(parser, name)) && ((ast_value*)find)->expression.vtype == TYPE_FUNCTION) + for (i = 0; i < vec_size(parser->functions); ++i) + if (((ast_value*)find)->name && !strcmp(parser->functions[i]->name, ((ast_value*)find)->name) && parser->functions[i]->builtin < 0) + return (ast_expression*)find; + + if ((find = util_htget(intrin_intrinsics(), name))) { + /* intrinsic is in table. This will "generate the function" so + * to speak (if it's not already generated). + */ + return ((intrin_t*)find)->intrin(parser); + } + + parseerror(parser, "need function: `%s` compiler depends on it", name); + return NULL; +} diff --git a/misc/source/gmqcc-src/ir.c b/misc/source/gmqcc-src/ir.c new file mode 100644 index 00000000..fcf4caaf --- /dev/null +++ b/misc/source/gmqcc-src/ir.c @@ -0,0 +1,4004 @@ +/* + * Copyright (C) 2012, 2013 + * Wolfgang Bumiller + * Dale Weiler + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include +#include + +#include "gmqcc.h" +#include "ir.h" + +/*********************************************************************** + * Type sizes used at multiple points in the IR codegen + */ + +const char *type_name[TYPE_COUNT] = { + "void", + "string", + "float", + "vector", + "entity", + "field", + "function", + "pointer", + "integer", + "variant", + "struct", + "union", + "array", + + "nil", + "" +}; + +static size_t type_sizeof_[TYPE_COUNT] = { + 1, /* TYPE_VOID */ + 1, /* TYPE_STRING */ + 1, /* TYPE_FLOAT */ + 3, /* TYPE_VECTOR */ + 1, /* TYPE_ENTITY */ + 1, /* TYPE_FIELD */ + 1, /* TYPE_FUNCTION */ + 1, /* TYPE_POINTER */ + 1, /* TYPE_INTEGER */ + 3, /* TYPE_VARIANT */ + 0, /* TYPE_STRUCT */ + 0, /* TYPE_UNION */ + 0, /* TYPE_ARRAY */ + 0, /* TYPE_NIL */ + 0, /* TYPE_NOESPR */ +}; + +const uint16_t type_store_instr[TYPE_COUNT] = { + INSTR_STORE_F, /* should use I when having integer support */ + INSTR_STORE_S, + INSTR_STORE_F, + INSTR_STORE_V, + INSTR_STORE_ENT, + INSTR_STORE_FLD, + INSTR_STORE_FNC, + INSTR_STORE_ENT, /* should use I */ +#if 0 + INSTR_STORE_I, /* integer type */ +#else + INSTR_STORE_F, +#endif + + INSTR_STORE_V, /* variant, should never be accessed */ + + VINSTR_END, /* struct */ + VINSTR_END, /* union */ + VINSTR_END, /* array */ + VINSTR_END, /* nil */ + VINSTR_END, /* noexpr */ +}; + +const uint16_t field_store_instr[TYPE_COUNT] = { + INSTR_STORE_FLD, + INSTR_STORE_FLD, + INSTR_STORE_FLD, + INSTR_STORE_V, + INSTR_STORE_FLD, + INSTR_STORE_FLD, + INSTR_STORE_FLD, + INSTR_STORE_FLD, +#if 0 + INSTR_STORE_FLD, /* integer type */ +#else + INSTR_STORE_FLD, +#endif + + INSTR_STORE_V, /* variant, should never be accessed */ + + VINSTR_END, /* struct */ + VINSTR_END, /* union */ + VINSTR_END, /* array */ + VINSTR_END, /* nil */ + VINSTR_END, /* noexpr */ +}; + +const uint16_t type_storep_instr[TYPE_COUNT] = { + INSTR_STOREP_F, /* should use I when having integer support */ + INSTR_STOREP_S, + INSTR_STOREP_F, + INSTR_STOREP_V, + INSTR_STOREP_ENT, + INSTR_STOREP_FLD, + INSTR_STOREP_FNC, + INSTR_STOREP_ENT, /* should use I */ +#if 0 + INSTR_STOREP_ENT, /* integer type */ +#else + INSTR_STOREP_F, +#endif + + INSTR_STOREP_V, /* variant, should never be accessed */ + + VINSTR_END, /* struct */ + VINSTR_END, /* union */ + VINSTR_END, /* array */ + VINSTR_END, /* nil */ + VINSTR_END, /* noexpr */ +}; + +const uint16_t type_eq_instr[TYPE_COUNT] = { + INSTR_EQ_F, /* should use I when having integer support */ + INSTR_EQ_S, + INSTR_EQ_F, + INSTR_EQ_V, + INSTR_EQ_E, + INSTR_EQ_E, /* FLD has no comparison */ + INSTR_EQ_FNC, + INSTR_EQ_E, /* should use I */ +#if 0 + INSTR_EQ_I, +#else + INSTR_EQ_F, +#endif + + INSTR_EQ_V, /* variant, should never be accessed */ + + VINSTR_END, /* struct */ + VINSTR_END, /* union */ + VINSTR_END, /* array */ + VINSTR_END, /* nil */ + VINSTR_END, /* noexpr */ +}; + +const uint16_t type_ne_instr[TYPE_COUNT] = { + INSTR_NE_F, /* should use I when having integer support */ + INSTR_NE_S, + INSTR_NE_F, + INSTR_NE_V, + INSTR_NE_E, + INSTR_NE_E, /* FLD has no comparison */ + INSTR_NE_FNC, + INSTR_NE_E, /* should use I */ +#if 0 + INSTR_NE_I, +#else + INSTR_NE_F, +#endif + + INSTR_NE_V, /* variant, should never be accessed */ + + VINSTR_END, /* struct */ + VINSTR_END, /* union */ + VINSTR_END, /* array */ + VINSTR_END, /* nil */ + VINSTR_END, /* noexpr */ +}; + +const uint16_t type_not_instr[TYPE_COUNT] = { + INSTR_NOT_F, /* should use I when having integer support */ + VINSTR_END, /* not to be used, depends on string related -f flags */ + INSTR_NOT_F, + INSTR_NOT_V, + INSTR_NOT_ENT, + INSTR_NOT_ENT, + INSTR_NOT_FNC, + INSTR_NOT_ENT, /* should use I */ +#if 0 + INSTR_NOT_I, /* integer type */ +#else + INSTR_NOT_F, +#endif + + INSTR_NOT_V, /* variant, should never be accessed */ + + VINSTR_END, /* struct */ + VINSTR_END, /* union */ + VINSTR_END, /* array */ + VINSTR_END, /* nil */ + VINSTR_END, /* noexpr */ +}; + +/* protos */ +static ir_value* ir_value_var(const char *name, int st, int vtype); +static bool ir_value_set_name(ir_value*, const char *name); +static void ir_value_dump(ir_value*, int (*oprintf)(const char*,...)); + +static ir_value* ir_gen_extparam_proto(ir_builder *ir); +static void ir_gen_extparam (code_t *, ir_builder *ir); + +static bool ir_builder_set_name(ir_builder *self, const char *name); + +static ir_function* ir_function_new(struct ir_builder_s *owner, int returntype); +static bool ir_function_set_name(ir_function*, const char *name); +static void ir_function_delete(ir_function*); +static void ir_function_dump(ir_function*, char *ind, int (*oprintf)(const char*,...)); + +static ir_value* ir_block_create_general_instr(ir_block *self, lex_ctx, const char *label, + int op, ir_value *a, ir_value *b, int outype); +static void ir_block_delete(ir_block*); +static ir_block* ir_block_new(struct ir_function_s *owner, const char *label); +static bool GMQCC_WARN ir_block_create_store(ir_block*, lex_ctx, ir_value *target, ir_value *what); +static bool ir_block_set_label(ir_block*, const char *label); +static void ir_block_dump(ir_block*, char *ind, int (*oprintf)(const char*,...)); + +static bool ir_instr_op(ir_instr*, int op, ir_value *value, bool writing); +static void ir_instr_delete(ir_instr*); +static void ir_instr_dump(ir_instr* in, char *ind, int (*oprintf)(const char*,...)); +/* error functions */ + +static void irerror(lex_ctx ctx, const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); + con_cvprintmsg((void*)&ctx, LVL_ERROR, "internal error", msg, ap); + va_end(ap); +} + +static bool irwarning(lex_ctx ctx, int warntype, const char *fmt, ...) +{ + bool r; + va_list ap; + va_start(ap, fmt); + r = vcompile_warning(ctx, warntype, fmt, ap); + va_end(ap); + return r; +} + +/*********************************************************************** + * Vector utility functions + */ + +static bool GMQCC_WARN vec_ir_value_find(ir_value **vec, const ir_value *what, size_t *idx) +{ + size_t i; + size_t len = vec_size(vec); + for (i = 0; i < len; ++i) { + if (vec[i] == what) { + if (idx) *idx = i; + return true; + } + } + return false; +} + +static bool GMQCC_WARN vec_ir_block_find(ir_block **vec, ir_block *what, size_t *idx) +{ + size_t i; + size_t len = vec_size(vec); + for (i = 0; i < len; ++i) { + if (vec[i] == what) { + if (idx) *idx = i; + return true; + } + } + return false; +} + +static bool GMQCC_WARN vec_ir_instr_find(ir_instr **vec, ir_instr *what, size_t *idx) +{ + size_t i; + size_t len = vec_size(vec); + for (i = 0; i < len; ++i) { + if (vec[i] == what) { + if (idx) *idx = i; + return true; + } + } + return false; +} + +/*********************************************************************** + * IR Builder + */ + +static void ir_block_delete_quick(ir_block* self); +static void ir_instr_delete_quick(ir_instr *self); +static void ir_function_delete_quick(ir_function *self); + +ir_builder* ir_builder_new(const char *modulename) +{ + ir_builder* self; + + self = (ir_builder*)mem_a(sizeof(*self)); + if (!self) + return NULL; + + self->functions = NULL; + self->globals = NULL; + self->fields = NULL; + self->filenames = NULL; + self->filestrings = NULL; + self->htglobals = util_htnew(IR_HT_SIZE); + self->htfields = util_htnew(IR_HT_SIZE); + self->htfunctions = util_htnew(IR_HT_SIZE); + + self->extparams = NULL; + self->extparam_protos = NULL; + + self->first_common_globaltemp = 0; + self->max_globaltemps = 0; + self->first_common_local = 0; + self->max_locals = 0; + + self->str_immediate = 0; + self->name = NULL; + if (!ir_builder_set_name(self, modulename)) { + mem_d(self); + return NULL; + } + + self->nil = ir_value_var("nil", store_value, TYPE_NIL); + self->nil->cvq = CV_CONST; + + self->reserved_va_count = NULL; + + return self; +} + +void ir_builder_delete(ir_builder* self) +{ + size_t i; + util_htdel(self->htglobals); + util_htdel(self->htfields); + util_htdel(self->htfunctions); + mem_d((void*)self->name); + for (i = 0; i != vec_size(self->functions); ++i) { + ir_function_delete_quick(self->functions[i]); + } + vec_free(self->functions); + for (i = 0; i != vec_size(self->extparams); ++i) { + ir_value_delete(self->extparams[i]); + } + vec_free(self->extparams); + vec_free(self->extparam_protos); + for (i = 0; i != vec_size(self->globals); ++i) { + ir_value_delete(self->globals[i]); + } + vec_free(self->globals); + for (i = 0; i != vec_size(self->fields); ++i) { + ir_value_delete(self->fields[i]); + } + ir_value_delete(self->nil); + vec_free(self->fields); + vec_free(self->filenames); + vec_free(self->filestrings); + mem_d(self); +} + +bool ir_builder_set_name(ir_builder *self, const char *name) +{ + if (self->name) + mem_d((void*)self->name); + self->name = util_strdup(name); + return !!self->name; +} + +static ir_function* ir_builder_get_function(ir_builder *self, const char *name) +{ + return (ir_function*)util_htget(self->htfunctions, name); +} + +ir_function* ir_builder_create_function(ir_builder *self, const char *name, int outtype) +{ + ir_function *fn = ir_builder_get_function(self, name); + if (fn) { + return NULL; + } + + fn = ir_function_new(self, outtype); + if (!ir_function_set_name(fn, name)) + { + ir_function_delete(fn); + return NULL; + } + vec_push(self->functions, fn); + util_htset(self->htfunctions, name, fn); + + fn->value = ir_builder_create_global(self, fn->name, TYPE_FUNCTION); + if (!fn->value) { + ir_function_delete(fn); + return NULL; + } + + fn->value->hasvalue = true; + fn->value->outtype = outtype; + fn->value->constval.vfunc = fn; + fn->value->context = fn->context; + + return fn; +} + +static ir_value* ir_builder_get_global(ir_builder *self, const char *name) +{ + return (ir_value*)util_htget(self->htglobals, name); +} + +ir_value* ir_builder_create_global(ir_builder *self, const char *name, int vtype) +{ + ir_value *ve; + + if (name && name[0] != '#') + { + ve = ir_builder_get_global(self, name); + if (ve) { + return NULL; + } + } + + ve = ir_value_var(name, store_global, vtype); + vec_push(self->globals, ve); + util_htset(self->htglobals, name, ve); + return ve; +} + +ir_value* ir_builder_get_va_count(ir_builder *self) +{ + if (self->reserved_va_count) + return self->reserved_va_count; + return (self->reserved_va_count = ir_builder_create_global(self, "reserved:va_count", TYPE_FLOAT)); +} + +static ir_value* ir_builder_get_field(ir_builder *self, const char *name) +{ + return (ir_value*)util_htget(self->htfields, name); +} + + +ir_value* ir_builder_create_field(ir_builder *self, const char *name, int vtype) +{ + ir_value *ve = ir_builder_get_field(self, name); + if (ve) { + return NULL; + } + + ve = ir_value_var(name, store_global, TYPE_FIELD); + ve->fieldtype = vtype; + vec_push(self->fields, ve); + util_htset(self->htfields, name, ve); + return ve; +} + +/*********************************************************************** + *IR Function + */ + +static bool ir_function_naive_phi(ir_function*); +static void ir_function_enumerate(ir_function*); +static bool ir_function_calculate_liferanges(ir_function*); +static bool ir_function_allocate_locals(ir_function*); + +ir_function* ir_function_new(ir_builder* owner, int outtype) +{ + ir_function *self; + self = (ir_function*)mem_a(sizeof(*self)); + + if (!self) + return NULL; + + memset(self, 0, sizeof(*self)); + + self->name = NULL; + if (!ir_function_set_name(self, "<@unnamed>")) { + mem_d(self); + return NULL; + } + self->flags = 0; + + self->owner = owner; + self->context.file = "<@no context>"; + self->context.line = 0; + self->outtype = outtype; + self->value = NULL; + self->builtin = 0; + + self->params = NULL; + self->blocks = NULL; + self->values = NULL; + self->locals = NULL; + + self->max_varargs = 0; + + self->code_function_def = -1; + self->allocated_locals = 0; + self->globaltemps = 0; + + self->run_id = 0; + return self; +} + +bool ir_function_set_name(ir_function *self, const char *name) +{ + if (self->name) + mem_d((void*)self->name); + self->name = util_strdup(name); + return !!self->name; +} + +static void ir_function_delete_quick(ir_function *self) +{ + size_t i; + mem_d((void*)self->name); + + for (i = 0; i != vec_size(self->blocks); ++i) + ir_block_delete_quick(self->blocks[i]); + vec_free(self->blocks); + + vec_free(self->params); + + for (i = 0; i != vec_size(self->values); ++i) + ir_value_delete(self->values[i]); + vec_free(self->values); + + for (i = 0; i != vec_size(self->locals); ++i) + ir_value_delete(self->locals[i]); + vec_free(self->locals); + + /* self->value is deleted by the builder */ + + mem_d(self); +} + +void ir_function_delete(ir_function *self) +{ + size_t i; + mem_d((void*)self->name); + + for (i = 0; i != vec_size(self->blocks); ++i) + ir_block_delete(self->blocks[i]); + vec_free(self->blocks); + + vec_free(self->params); + + for (i = 0; i != vec_size(self->values); ++i) + ir_value_delete(self->values[i]); + vec_free(self->values); + + for (i = 0; i != vec_size(self->locals); ++i) + ir_value_delete(self->locals[i]); + vec_free(self->locals); + + /* self->value is deleted by the builder */ + + mem_d(self); +} + +static void ir_function_collect_value(ir_function *self, ir_value *v) +{ + vec_push(self->values, v); +} + +ir_block* ir_function_create_block(lex_ctx ctx, ir_function *self, const char *label) +{ + ir_block* bn = ir_block_new(self, label); + bn->context = ctx; + vec_push(self->blocks, bn); + return bn; +} + +static bool instr_is_operation(uint16_t op) +{ + return ( (op >= INSTR_MUL_F && op <= INSTR_GT) || + (op >= INSTR_LOAD_F && op <= INSTR_LOAD_FNC) || + (op == INSTR_ADDRESS) || + (op >= INSTR_NOT_F && op <= INSTR_NOT_FNC) || + (op >= INSTR_AND && op <= INSTR_BITOR) || + (op >= INSTR_CALL0 && op <= INSTR_CALL8) ); +} + +static bool ir_function_pass_peephole(ir_function *self) +{ + size_t b; + + for (b = 0; b < vec_size(self->blocks); ++b) { + size_t i; + ir_block *block = self->blocks[b]; + + for (i = 0; i < vec_size(block->instr); ++i) { + ir_instr *inst; + inst = block->instr[i]; + + if (i >= 1 && + (inst->opcode >= INSTR_STORE_F && + inst->opcode <= INSTR_STORE_FNC)) + { + ir_instr *store; + ir_instr *oper; + ir_value *value; + + store = inst; + + oper = block->instr[i-1]; + if (!instr_is_operation(oper->opcode)) + continue; + + if (OPTS_FLAG(LEGACY_VECTOR_MATHS)) { + if (oper->opcode == INSTR_MUL_VF && oper->_ops[2]->memberof == oper->_ops[1]) + continue; + if (oper->opcode == INSTR_MUL_FV && oper->_ops[1]->memberof == oper->_ops[2]) + continue; + } + + value = oper->_ops[0]; + + /* only do it for SSA values */ + if (value->store != store_value) + continue; + + /* don't optimize out the temp if it's used later again */ + if (vec_size(value->reads) != 1) + continue; + + /* The very next store must use this value */ + if (value->reads[0] != store) + continue; + + /* And of course the store must _read_ from it, so it's in + * OP 1 */ + if (store->_ops[1] != value) + continue; + + ++opts_optimizationcount[OPTIM_PEEPHOLE]; + (void)!ir_instr_op(oper, 0, store->_ops[0], true); + + vec_remove(block->instr, i, 1); + ir_instr_delete(store); + } + else if (inst->opcode == VINSTR_COND) + { + /* COND on a value resulting from a NOT could + * remove the NOT and swap its operands + */ + while (true) { + ir_block *tmp; + size_t inotid; + ir_instr *inot; + ir_value *value; + value = inst->_ops[0]; + + if (value->store != store_value || + vec_size(value->reads) != 1 || + value->reads[0] != inst) + { + break; + } + + inot = value->writes[0]; + if (inot->_ops[0] != value || + inot->opcode < INSTR_NOT_F || + inot->opcode > INSTR_NOT_FNC || + inot->opcode == INSTR_NOT_V || /* can't do these */ + inot->opcode == INSTR_NOT_S) + { + break; + } + + /* count */ + ++opts_optimizationcount[OPTIM_PEEPHOLE]; + /* change operand */ + (void)!ir_instr_op(inst, 0, inot->_ops[1], false); + /* remove NOT */ + tmp = inot->owner; + for (inotid = 0; inotid < vec_size(tmp->instr); ++inotid) { + if (tmp->instr[inotid] == inot) + break; + } + if (inotid >= vec_size(tmp->instr)) { + compile_error(inst->context, "sanity-check failed: failed to find instruction to optimize out"); + return false; + } + vec_remove(tmp->instr, inotid, 1); + ir_instr_delete(inot); + /* swap ontrue/onfalse */ + tmp = inst->bops[0]; + inst->bops[0] = inst->bops[1]; + inst->bops[1] = tmp; + } + continue; + } + } + } + + return true; +} + +static bool ir_function_pass_tailrecursion(ir_function *self) +{ + size_t b, p; + + for (b = 0; b < vec_size(self->blocks); ++b) { + ir_value *funcval; + ir_instr *ret, *call, *store = NULL; + ir_block *block = self->blocks[b]; + + if (!block->final || vec_size(block->instr) < 2) + continue; + + ret = block->instr[vec_size(block->instr)-1]; + if (ret->opcode != INSTR_DONE && ret->opcode != INSTR_RETURN) + continue; + + call = block->instr[vec_size(block->instr)-2]; + if (call->opcode >= INSTR_STORE_F && call->opcode <= INSTR_STORE_FNC) { + /* account for the unoptimized + * CALL + * STORE %return, %tmp + * RETURN %tmp + * version + */ + if (vec_size(block->instr) < 3) + continue; + + store = call; + call = block->instr[vec_size(block->instr)-3]; + } + + if (call->opcode < INSTR_CALL0 || call->opcode > INSTR_CALL8) + continue; + + if (store) { + /* optimize out the STORE */ + if (ret->_ops[0] && + ret->_ops[0] == store->_ops[0] && + store->_ops[1] == call->_ops[0]) + { + ++opts_optimizationcount[OPTIM_PEEPHOLE]; + call->_ops[0] = store->_ops[0]; + vec_remove(block->instr, vec_size(block->instr) - 2, 1); + ir_instr_delete(store); + } + else + continue; + } + + if (!call->_ops[0]) + continue; + + funcval = call->_ops[1]; + if (!funcval) + continue; + if (funcval->vtype != TYPE_FUNCTION || funcval->constval.vfunc != self) + continue; + + /* now we have a CALL and a RET, check if it's a tailcall */ + if (ret->_ops[0] && call->_ops[0] != ret->_ops[0]) + continue; + + ++opts_optimizationcount[OPTIM_TAIL_RECURSION]; + vec_shrinkby(block->instr, 2); + + block->final = false; /* open it back up */ + + /* emite parameter-stores */ + for (p = 0; p < vec_size(call->params); ++p) { + /* assert(call->params_count <= self->locals_count); */ + if (!ir_block_create_store(block, call->context, self->locals[p], call->params[p])) { + irerror(call->context, "failed to create tailcall store instruction for parameter %i", (int)p); + return false; + } + } + if (!ir_block_create_jump(block, call->context, self->blocks[0])) { + irerror(call->context, "failed to create tailcall jump"); + return false; + } + + ir_instr_delete(call); + ir_instr_delete(ret); + } + + return true; +} + +bool ir_function_finalize(ir_function *self) +{ + size_t i; + + if (self->builtin) + return true; + + if (OPTS_OPTIMIZATION(OPTIM_PEEPHOLE)) { + if (!ir_function_pass_peephole(self)) { + irerror(self->context, "generic optimization pass broke something in `%s`", self->name); + return false; + } + } + + if (OPTS_OPTIMIZATION(OPTIM_TAIL_RECURSION)) { + if (!ir_function_pass_tailrecursion(self)) { + irerror(self->context, "tail-recursion optimization pass broke something in `%s`", self->name); + return false; + } + } + + if (!ir_function_naive_phi(self)) { + irerror(self->context, "internal error: ir_function_naive_phi failed"); + return false; + } + + for (i = 0; i < vec_size(self->locals); ++i) { + ir_value *v = self->locals[i]; + if (v->vtype == TYPE_VECTOR || + (v->vtype == TYPE_FIELD && v->outtype == TYPE_VECTOR)) + { + ir_value_vector_member(v, 0); + ir_value_vector_member(v, 1); + ir_value_vector_member(v, 2); + } + } + for (i = 0; i < vec_size(self->values); ++i) { + ir_value *v = self->values[i]; + if (v->vtype == TYPE_VECTOR || + (v->vtype == TYPE_FIELD && v->outtype == TYPE_VECTOR)) + { + ir_value_vector_member(v, 0); + ir_value_vector_member(v, 1); + ir_value_vector_member(v, 2); + } + } + + ir_function_enumerate(self); + + if (!ir_function_calculate_liferanges(self)) + return false; + if (!ir_function_allocate_locals(self)) + return false; + return true; +} + +ir_value* ir_function_create_local(ir_function *self, const char *name, int vtype, bool param) +{ + ir_value *ve; + + if (param && + vec_size(self->locals) && + self->locals[vec_size(self->locals)-1]->store != store_param) { + irerror(self->context, "cannot add parameters after adding locals"); + return NULL; + } + + ve = ir_value_var(name, (param ? store_param : store_local), vtype); + if (param) + ve->locked = true; + vec_push(self->locals, ve); + return ve; +} + +/*********************************************************************** + *IR Block + */ + +ir_block* ir_block_new(ir_function* owner, const char *name) +{ + ir_block *self; + self = (ir_block*)mem_a(sizeof(*self)); + if (!self) + return NULL; + + memset(self, 0, sizeof(*self)); + + self->label = NULL; + if (name && !ir_block_set_label(self, name)) { + mem_d(self); + return NULL; + } + self->owner = owner; + self->context.file = "<@no context>"; + self->context.line = 0; + self->final = false; + + self->instr = NULL; + self->entries = NULL; + self->exits = NULL; + + self->eid = 0; + self->is_return = false; + + self->living = NULL; + + self->generated = false; + + return self; +} + +static void ir_block_delete_quick(ir_block* self) +{ + size_t i; + if (self->label) mem_d(self->label); + for (i = 0; i != vec_size(self->instr); ++i) + ir_instr_delete_quick(self->instr[i]); + vec_free(self->instr); + vec_free(self->entries); + vec_free(self->exits); + vec_free(self->living); + mem_d(self); +} + +void ir_block_delete(ir_block* self) +{ + size_t i; + if (self->label) mem_d(self->label); + for (i = 0; i != vec_size(self->instr); ++i) + ir_instr_delete(self->instr[i]); + vec_free(self->instr); + vec_free(self->entries); + vec_free(self->exits); + vec_free(self->living); + mem_d(self); +} + +bool ir_block_set_label(ir_block *self, const char *name) +{ + if (self->label) + mem_d((void*)self->label); + self->label = util_strdup(name); + return !!self->label; +} + +/*********************************************************************** + *IR Instructions + */ + +static ir_instr* ir_instr_new(lex_ctx ctx, ir_block* owner, int op) +{ + ir_instr *self; + self = (ir_instr*)mem_a(sizeof(*self)); + if (!self) + return NULL; + + self->owner = owner; + self->context = ctx; + self->opcode = op; + self->_ops[0] = NULL; + self->_ops[1] = NULL; + self->_ops[2] = NULL; + self->bops[0] = NULL; + self->bops[1] = NULL; + + self->phi = NULL; + self->params = NULL; + + self->eid = 0; + + self->likely = true; + return self; +} + +static void ir_instr_delete_quick(ir_instr *self) +{ + vec_free(self->phi); + vec_free(self->params); + mem_d(self); +} + +static void ir_instr_delete(ir_instr *self) +{ + size_t i; + /* The following calls can only delete from + * vectors, we still want to delete this instruction + * so ignore the return value. Since with the warn_unused_result attribute + * gcc doesn't care about an explicit: (void)foo(); to ignore the result, + * I have to improvise here and use if(foo()); + */ + for (i = 0; i < vec_size(self->phi); ++i) { + size_t idx; + if (vec_ir_instr_find(self->phi[i].value->writes, self, &idx)) + vec_remove(self->phi[i].value->writes, idx, 1); + if (vec_ir_instr_find(self->phi[i].value->reads, self, &idx)) + vec_remove(self->phi[i].value->reads, idx, 1); + } + vec_free(self->phi); + for (i = 0; i < vec_size(self->params); ++i) { + size_t idx; + if (vec_ir_instr_find(self->params[i]->writes, self, &idx)) + vec_remove(self->params[i]->writes, idx, 1); + if (vec_ir_instr_find(self->params[i]->reads, self, &idx)) + vec_remove(self->params[i]->reads, idx, 1); + } + vec_free(self->params); + (void)!ir_instr_op(self, 0, NULL, false); + (void)!ir_instr_op(self, 1, NULL, false); + (void)!ir_instr_op(self, 2, NULL, false); + mem_d(self); +} + +static bool ir_instr_op(ir_instr *self, int op, ir_value *v, bool writing) +{ + if (self->_ops[op]) { + size_t idx; + if (writing && vec_ir_instr_find(self->_ops[op]->writes, self, &idx)) + vec_remove(self->_ops[op]->writes, idx, 1); + else if (vec_ir_instr_find(self->_ops[op]->reads, self, &idx)) + vec_remove(self->_ops[op]->reads, idx, 1); + } + if (v) { + if (writing) + vec_push(v->writes, self); + else + vec_push(v->reads, self); + } + self->_ops[op] = v; + return true; +} + +/*********************************************************************** + *IR Value + */ + +static void ir_value_code_setaddr(ir_value *self, int32_t gaddr) +{ + self->code.globaladdr = gaddr; + if (self->members[0]) self->members[0]->code.globaladdr = gaddr; + if (self->members[1]) self->members[1]->code.globaladdr = gaddr; + if (self->members[2]) self->members[2]->code.globaladdr = gaddr; +} + +static int32_t ir_value_code_addr(const ir_value *self) +{ + if (self->store == store_return) + return OFS_RETURN + self->code.addroffset; + return self->code.globaladdr + self->code.addroffset; +} + +ir_value* ir_value_var(const char *name, int storetype, int vtype) +{ + ir_value *self; + self = (ir_value*)mem_a(sizeof(*self)); + self->vtype = vtype; + self->fieldtype = TYPE_VOID; + self->outtype = TYPE_VOID; + self->store = storetype; + self->flags = 0; + + self->reads = NULL; + self->writes = NULL; + + self->cvq = CV_NONE; + self->hasvalue = false; + self->context.file = "<@no context>"; + self->context.line = 0; + self->name = NULL; + if (name && !ir_value_set_name(self, name)) { + irerror(self->context, "out of memory"); + mem_d(self); + return NULL; + } + + memset(&self->constval, 0, sizeof(self->constval)); + memset(&self->code, 0, sizeof(self->code)); + + self->members[0] = NULL; + self->members[1] = NULL; + self->members[2] = NULL; + self->memberof = NULL; + + self->unique_life = false; + self->locked = false; + self->callparam = false; + + self->life = NULL; + return self; +} + +ir_value* ir_value_vector_member(ir_value *self, unsigned int member) +{ + char *name; + size_t len; + ir_value *m; + if (member >= 3) + return NULL; + + if (self->members[member]) + return self->members[member]; + + if (self->name) { + len = strlen(self->name); + name = (char*)mem_a(len + 3); + memcpy(name, self->name, len); + name[len+0] = '_'; + name[len+1] = 'x' + member; + name[len+2] = '\0'; + } + else + name = NULL; + + if (self->vtype == TYPE_VECTOR) + { + m = ir_value_var(name, self->store, TYPE_FLOAT); + if (name) + mem_d(name); + if (!m) + return NULL; + m->context = self->context; + + self->members[member] = m; + m->code.addroffset = member; + } + else if (self->vtype == TYPE_FIELD) + { + if (self->fieldtype != TYPE_VECTOR) + return NULL; + m = ir_value_var(name, self->store, TYPE_FIELD); + if (name) + mem_d(name); + if (!m) + return NULL; + m->fieldtype = TYPE_FLOAT; + m->context = self->context; + + self->members[member] = m; + m->code.addroffset = member; + } + else + { + irerror(self->context, "invalid member access on %s", self->name); + return NULL; + } + + m->memberof = self; + return m; +} + +static GMQCC_INLINE size_t ir_value_sizeof(const ir_value *self) +{ + if (self->vtype == TYPE_FIELD && self->fieldtype == TYPE_VECTOR) + return type_sizeof_[TYPE_VECTOR]; + return type_sizeof_[self->vtype]; +} + +static ir_value* ir_value_out(ir_function *owner, const char *name, int storetype, int vtype) +{ + ir_value *v = ir_value_var(name, storetype, vtype); + if (!v) + return NULL; + ir_function_collect_value(owner, v); + return v; +} + +void ir_value_delete(ir_value* self) +{ + size_t i; + if (self->name) + mem_d((void*)self->name); + if (self->hasvalue) + { + if (self->vtype == TYPE_STRING) + mem_d((void*)self->constval.vstring); + } + for (i = 0; i < 3; ++i) { + if (self->members[i]) + ir_value_delete(self->members[i]); + } + vec_free(self->reads); + vec_free(self->writes); + vec_free(self->life); + mem_d(self); +} + +bool ir_value_set_name(ir_value *self, const char *name) +{ + if (self->name) + mem_d((void*)self->name); + self->name = util_strdup(name); + return !!self->name; +} + +bool ir_value_set_float(ir_value *self, float f) +{ + if (self->vtype != TYPE_FLOAT) + return false; + self->constval.vfloat = f; + self->hasvalue = true; + return true; +} + +bool ir_value_set_func(ir_value *self, int f) +{ + if (self->vtype != TYPE_FUNCTION) + return false; + self->constval.vint = f; + self->hasvalue = true; + return true; +} + +bool ir_value_set_vector(ir_value *self, vector v) +{ + if (self->vtype != TYPE_VECTOR) + return false; + self->constval.vvec = v; + self->hasvalue = true; + return true; +} + +bool ir_value_set_field(ir_value *self, ir_value *fld) +{ + if (self->vtype != TYPE_FIELD) + return false; + self->constval.vpointer = fld; + self->hasvalue = true; + return true; +} + +bool ir_value_set_string(ir_value *self, const char *str) +{ + if (self->vtype != TYPE_STRING) + return false; + self->constval.vstring = util_strdupe(str); + self->hasvalue = true; + return true; +} + +#if 0 +bool ir_value_set_int(ir_value *self, int i) +{ + if (self->vtype != TYPE_INTEGER) + return false; + self->constval.vint = i; + self->hasvalue = true; + return true; +} +#endif + +bool ir_value_lives(ir_value *self, size_t at) +{ + size_t i; + for (i = 0; i < vec_size(self->life); ++i) + { + ir_life_entry_t *life = &self->life[i]; + if (life->start <= at && at <= life->end) + return true; + if (life->start > at) /* since it's ordered */ + return false; + } + return false; +} + +static bool ir_value_life_insert(ir_value *self, size_t idx, ir_life_entry_t e) +{ + size_t k; + vec_push(self->life, e); + for (k = vec_size(self->life)-1; k > idx; --k) + self->life[k] = self->life[k-1]; + self->life[idx] = e; + return true; +} + +static bool ir_value_life_merge(ir_value *self, size_t s) +{ + size_t i; + const size_t vs = vec_size(self->life); + ir_life_entry_t *life = NULL; + ir_life_entry_t *before = NULL; + ir_life_entry_t new_entry; + + /* Find the first range >= s */ + for (i = 0; i < vs; ++i) + { + before = life; + life = &self->life[i]; + if (life->start > s) + break; + } + /* nothing found? append */ + if (i == vs) { + ir_life_entry_t e; + if (life && life->end+1 == s) + { + /* previous life range can be merged in */ + life->end++; + return true; + } + if (life && life->end >= s) + return false; + e.start = e.end = s; + vec_push(self->life, e); + return true; + } + /* found */ + if (before) + { + if (before->end + 1 == s && + life->start - 1 == s) + { + /* merge */ + before->end = life->end; + vec_remove(self->life, i, 1); + return true; + } + if (before->end + 1 == s) + { + /* extend before */ + before->end++; + return true; + } + /* already contained */ + if (before->end >= s) + return false; + } + /* extend */ + if (life->start - 1 == s) + { + life->start--; + return true; + } + /* insert a new entry */ + new_entry.start = new_entry.end = s; + return ir_value_life_insert(self, i, new_entry); +} + +static bool ir_value_life_merge_into(ir_value *self, const ir_value *other) +{ + size_t i, myi; + + if (!vec_size(other->life)) + return true; + + if (!vec_size(self->life)) { + size_t count = vec_size(other->life); + ir_life_entry_t *life = vec_add(self->life, count); + memcpy(life, other->life, count * sizeof(*life)); + return true; + } + + myi = 0; + for (i = 0; i < vec_size(other->life); ++i) + { + const ir_life_entry_t *life = &other->life[i]; + while (true) + { + ir_life_entry_t *entry = &self->life[myi]; + + if (life->end+1 < entry->start) + { + /* adding an interval before entry */ + if (!ir_value_life_insert(self, myi, *life)) + return false; + ++myi; + break; + } + + if (life->start < entry->start && + life->end+1 >= entry->start) + { + /* starts earlier and overlaps */ + entry->start = life->start; + } + + if (life->end > entry->end && + life->start <= entry->end+1) + { + /* ends later and overlaps */ + entry->end = life->end; + } + + /* see if our change combines it with the next ranges */ + while (myi+1 < vec_size(self->life) && + entry->end+1 >= self->life[1+myi].start) + { + /* overlaps with (myi+1) */ + if (entry->end < self->life[1+myi].end) + entry->end = self->life[1+myi].end; + vec_remove(self->life, myi+1, 1); + entry = &self->life[myi]; + } + + /* see if we're after the entry */ + if (life->start > entry->end) + { + ++myi; + /* append if we're at the end */ + if (myi >= vec_size(self->life)) { + vec_push(self->life, *life); + break; + } + /* otherweise check the next range */ + continue; + } + break; + } + } + return true; +} + +static bool ir_values_overlap(const ir_value *a, const ir_value *b) +{ + /* For any life entry in A see if it overlaps with + * any life entry in B. + * Note that the life entries are orderes, so we can make a + * more efficient algorithm there than naively translating the + * statement above. + */ + + ir_life_entry_t *la, *lb, *enda, *endb; + + /* first of all, if either has no life range, they cannot clash */ + if (!vec_size(a->life) || !vec_size(b->life)) + return false; + + la = a->life; + lb = b->life; + enda = la + vec_size(a->life); + endb = lb + vec_size(b->life); + while (true) + { + /* check if the entries overlap, for that, + * both must start before the other one ends. + */ + if (la->start < lb->end && + lb->start < la->end) + { + return true; + } + + /* entries are ordered + * one entry is earlier than the other + * that earlier entry will be moved forward + */ + if (la->start < lb->start) + { + /* order: A B, move A forward + * check if we hit the end with A + */ + if (++la == enda) + break; + } + else /* if (lb->start < la->start) actually <= */ + { + /* order: B A, move B forward + * check if we hit the end with B + */ + if (++lb == endb) + break; + } + } + return false; +} + +/*********************************************************************** + *IR main operations + */ + +static bool ir_check_unreachable(ir_block *self) +{ + /* The IR should never have to deal with unreachable code */ + if (!self->final/* || OPTS_FLAG(ALLOW_UNREACHABLE_CODE)*/) + return true; + irerror(self->context, "unreachable statement (%s)", self->label); + return false; +} + +bool ir_block_create_store_op(ir_block *self, lex_ctx ctx, int op, ir_value *target, ir_value *what) +{ + ir_instr *in; + if (!ir_check_unreachable(self)) + return false; + + if (target->store == store_value && + (op < INSTR_STOREP_F || op > INSTR_STOREP_FNC)) + { + irerror(self->context, "cannot store to an SSA value"); + irerror(self->context, "trying to store: %s <- %s", target->name, what->name); + irerror(self->context, "instruction: %s", asm_instr[op].m); + return false; + } + + in = ir_instr_new(ctx, self, op); + if (!in) + return false; + + if (!ir_instr_op(in, 0, target, (op < INSTR_STOREP_F || op > INSTR_STOREP_FNC)) || + !ir_instr_op(in, 1, what, false)) + { + ir_instr_delete(in); + return false; + } + vec_push(self->instr, in); + return true; +} + +static bool ir_block_create_store(ir_block *self, lex_ctx ctx, ir_value *target, ir_value *what) +{ + int op = 0; + int vtype; + if (target->vtype == TYPE_VARIANT) + vtype = what->vtype; + else + vtype = target->vtype; + +#if 0 + if (vtype == TYPE_FLOAT && what->vtype == TYPE_INTEGER) + op = INSTR_CONV_ITOF; + else if (vtype == TYPE_INTEGER && what->vtype == TYPE_FLOAT) + op = INSTR_CONV_FTOI; +#endif + op = type_store_instr[vtype]; + + if (OPTS_FLAG(ADJUST_VECTOR_FIELDS)) { + if (op == INSTR_STORE_FLD && what->fieldtype == TYPE_VECTOR) + op = INSTR_STORE_V; + } + + return ir_block_create_store_op(self, ctx, op, target, what); +} + +bool ir_block_create_storep(ir_block *self, lex_ctx ctx, ir_value *target, ir_value *what) +{ + int op = 0; + int vtype; + + if (target->vtype != TYPE_POINTER) + return false; + + /* storing using pointer - target is a pointer, type must be + * inferred from source + */ + vtype = what->vtype; + + op = type_storep_instr[vtype]; + if (OPTS_FLAG(ADJUST_VECTOR_FIELDS)) { + if (op == INSTR_STOREP_FLD && what->fieldtype == TYPE_VECTOR) + op = INSTR_STOREP_V; + } + + return ir_block_create_store_op(self, ctx, op, target, what); +} + +bool ir_block_create_return(ir_block *self, lex_ctx ctx, ir_value *v) +{ + ir_instr *in; + if (!ir_check_unreachable(self)) + return false; + self->final = true; + self->is_return = true; + in = ir_instr_new(ctx, self, INSTR_RETURN); + if (!in) + return false; + + if (v && !ir_instr_op(in, 0, v, false)) { + ir_instr_delete(in); + return false; + } + + vec_push(self->instr, in); + return true; +} + +bool ir_block_create_if(ir_block *self, lex_ctx ctx, ir_value *v, + ir_block *ontrue, ir_block *onfalse) +{ + ir_instr *in; + if (!ir_check_unreachable(self)) + return false; + self->final = true; + /*in = ir_instr_new(ctx, self, (v->vtype == TYPE_STRING ? INSTR_IF_S : INSTR_IF_F));*/ + in = ir_instr_new(ctx, self, VINSTR_COND); + if (!in) + return false; + + if (!ir_instr_op(in, 0, v, false)) { + ir_instr_delete(in); + return false; + } + + in->bops[0] = ontrue; + in->bops[1] = onfalse; + + vec_push(self->instr, in); + + vec_push(self->exits, ontrue); + vec_push(self->exits, onfalse); + vec_push(ontrue->entries, self); + vec_push(onfalse->entries, self); + return true; +} + +bool ir_block_create_jump(ir_block *self, lex_ctx ctx, ir_block *to) +{ + ir_instr *in; + if (!ir_check_unreachable(self)) + return false; + self->final = true; + in = ir_instr_new(ctx, self, VINSTR_JUMP); + if (!in) + return false; + + in->bops[0] = to; + vec_push(self->instr, in); + + vec_push(self->exits, to); + vec_push(to->entries, self); + return true; +} + +bool ir_block_create_goto(ir_block *self, lex_ctx ctx, ir_block *to) +{ + self->owner->flags |= IR_FLAG_HAS_GOTO; + return ir_block_create_jump(self, ctx, to); +} + +ir_instr* ir_block_create_phi(ir_block *self, lex_ctx ctx, const char *label, int ot) +{ + ir_value *out; + ir_instr *in; + if (!ir_check_unreachable(self)) + return NULL; + in = ir_instr_new(ctx, self, VINSTR_PHI); + if (!in) + return NULL; + out = ir_value_out(self->owner, label, store_value, ot); + if (!out) { + ir_instr_delete(in); + return NULL; + } + if (!ir_instr_op(in, 0, out, true)) { + ir_instr_delete(in); + ir_value_delete(out); + return NULL; + } + vec_push(self->instr, in); + return in; +} + +ir_value* ir_phi_value(ir_instr *self) +{ + return self->_ops[0]; +} + +void ir_phi_add(ir_instr* self, ir_block *b, ir_value *v) +{ + ir_phi_entry_t pe; + + if (!vec_ir_block_find(self->owner->entries, b, NULL)) { + /* Must not be possible to cause this, otherwise the AST + * is doing something wrong. + */ + irerror(self->context, "Invalid entry block for PHI"); + exit(EXIT_FAILURE); + } + + pe.value = v; + pe.from = b; + vec_push(v->reads, self); + vec_push(self->phi, pe); +} + +/* call related code */ +ir_instr* ir_block_create_call(ir_block *self, lex_ctx ctx, const char *label, ir_value *func, bool noreturn) +{ + ir_value *out; + ir_instr *in; + if (!ir_check_unreachable(self)) + return NULL; + in = ir_instr_new(ctx, self, (noreturn ? VINSTR_NRCALL : INSTR_CALL0)); + if (!in) + return NULL; + if (noreturn) { + self->final = true; + self->is_return = true; + } + out = ir_value_out(self->owner, label, (func->outtype == TYPE_VOID) ? store_return : store_value, func->outtype); + if (!out) { + ir_instr_delete(in); + return NULL; + } + if (!ir_instr_op(in, 0, out, true) || + !ir_instr_op(in, 1, func, false)) + { + ir_instr_delete(in); + ir_value_delete(out); + return NULL; + } + vec_push(self->instr, in); + /* + if (noreturn) { + if (!ir_block_create_return(self, ctx, NULL)) { + compile_error(ctx, "internal error: failed to generate dummy-return instruction"); + ir_instr_delete(in); + return NULL; + } + } + */ + return in; +} + +ir_value* ir_call_value(ir_instr *self) +{ + return self->_ops[0]; +} + +void ir_call_param(ir_instr* self, ir_value *v) +{ + vec_push(self->params, v); + vec_push(v->reads, self); +} + +/* binary op related code */ + +ir_value* ir_block_create_binop(ir_block *self, lex_ctx ctx, + const char *label, int opcode, + ir_value *left, ir_value *right) +{ + int ot = TYPE_VOID; + switch (opcode) { + case INSTR_ADD_F: + case INSTR_SUB_F: + case INSTR_DIV_F: + case INSTR_MUL_F: + case INSTR_MUL_V: + case INSTR_AND: + case INSTR_OR: +#if 0 + case INSTR_AND_I: + case INSTR_AND_IF: + case INSTR_AND_FI: + case INSTR_OR_I: + case INSTR_OR_IF: + case INSTR_OR_FI: +#endif + case INSTR_BITAND: + case INSTR_BITOR: +#if 0 + case INSTR_SUB_S: /* -- offset of string as float */ + case INSTR_MUL_IF: + case INSTR_MUL_FI: + case INSTR_DIV_IF: + case INSTR_DIV_FI: + case INSTR_BITOR_IF: + case INSTR_BITOR_FI: + case INSTR_BITAND_FI: + case INSTR_BITAND_IF: + case INSTR_EQ_I: + case INSTR_NE_I: +#endif + ot = TYPE_FLOAT; + break; +#if 0 + case INSTR_ADD_I: + case INSTR_ADD_IF: + case INSTR_ADD_FI: + case INSTR_SUB_I: + case INSTR_SUB_FI: + case INSTR_SUB_IF: + case INSTR_MUL_I: + case INSTR_DIV_I: + case INSTR_BITAND_I: + case INSTR_BITOR_I: + case INSTR_XOR_I: + case INSTR_RSHIFT_I: + case INSTR_LSHIFT_I: + ot = TYPE_INTEGER; + break; +#endif + case INSTR_ADD_V: + case INSTR_SUB_V: + case INSTR_MUL_VF: + case INSTR_MUL_FV: +#if 0 + case INSTR_DIV_VF: + case INSTR_MUL_IV: + case INSTR_MUL_VI: +#endif + ot = TYPE_VECTOR; + break; +#if 0 + case INSTR_ADD_SF: + ot = TYPE_POINTER; + break; +#endif + default: + /* ranges: */ + /* boolean operations result in floats */ + if (opcode >= INSTR_EQ_F && opcode <= INSTR_GT) + ot = TYPE_FLOAT; + else if (opcode >= INSTR_LE && opcode <= INSTR_GT) + ot = TYPE_FLOAT; +#if 0 + else if (opcode >= INSTR_LE_I && opcode <= INSTR_EQ_FI) + ot = TYPE_FLOAT; +#endif + break; + }; + if (ot == TYPE_VOID) { + /* The AST or parser were supposed to check this! */ + return NULL; + } + + return ir_block_create_general_instr(self, ctx, label, opcode, left, right, ot); +} + +ir_value* ir_block_create_unary(ir_block *self, lex_ctx ctx, + const char *label, int opcode, + ir_value *operand) +{ + int ot = TYPE_FLOAT; + switch (opcode) { + case INSTR_NOT_F: + case INSTR_NOT_V: + case INSTR_NOT_S: + case INSTR_NOT_ENT: + case INSTR_NOT_FNC: +#if 0 + case INSTR_NOT_I: +#endif + ot = TYPE_FLOAT; + break; + /* QC doesn't have other unary operations. We expect extensions to fill + * the above list, otherwise we assume out-type = in-type, eg for an + * unary minus + */ + default: + ot = operand->vtype; + break; + }; + if (ot == TYPE_VOID) { + /* The AST or parser were supposed to check this! */ + return NULL; + } + + /* let's use the general instruction creator and pass NULL for OPB */ + return ir_block_create_general_instr(self, ctx, label, opcode, operand, NULL, ot); +} + +static ir_value* ir_block_create_general_instr(ir_block *self, lex_ctx ctx, const char *label, + int op, ir_value *a, ir_value *b, int outype) +{ + ir_instr *instr; + ir_value *out; + + out = ir_value_out(self->owner, label, store_value, outype); + if (!out) + return NULL; + + instr = ir_instr_new(ctx, self, op); + if (!instr) { + ir_value_delete(out); + return NULL; + } + + if (!ir_instr_op(instr, 0, out, true) || + !ir_instr_op(instr, 1, a, false) || + !ir_instr_op(instr, 2, b, false) ) + { + goto on_error; + } + + vec_push(self->instr, instr); + + return out; +on_error: + ir_instr_delete(instr); + ir_value_delete(out); + return NULL; +} + +ir_value* ir_block_create_fieldaddress(ir_block *self, lex_ctx ctx, const char *label, ir_value *ent, ir_value *field) +{ + ir_value *v; + + /* Support for various pointer types todo if so desired */ + if (ent->vtype != TYPE_ENTITY) + return NULL; + + if (field->vtype != TYPE_FIELD) + return NULL; + + v = ir_block_create_general_instr(self, ctx, label, INSTR_ADDRESS, ent, field, TYPE_POINTER); + v->fieldtype = field->fieldtype; + return v; +} + +ir_value* ir_block_create_load_from_ent(ir_block *self, lex_ctx ctx, const char *label, ir_value *ent, ir_value *field, int outype) +{ + int op; + if (ent->vtype != TYPE_ENTITY) + return NULL; + + /* at some point we could redirect for TYPE_POINTER... but that could lead to carelessness */ + if (field->vtype != TYPE_FIELD) + return NULL; + + switch (outype) + { + case TYPE_FLOAT: op = INSTR_LOAD_F; break; + case TYPE_VECTOR: op = INSTR_LOAD_V; break; + case TYPE_STRING: op = INSTR_LOAD_S; break; + case TYPE_FIELD: op = INSTR_LOAD_FLD; break; + case TYPE_ENTITY: op = INSTR_LOAD_ENT; break; + case TYPE_FUNCTION: op = INSTR_LOAD_FNC; break; +#if 0 + case TYPE_POINTER: op = INSTR_LOAD_I; break; + case TYPE_INTEGER: op = INSTR_LOAD_I; break; +#endif + default: + irerror(self->context, "invalid type for ir_block_create_load_from_ent: %s", type_name[outype]); + return NULL; + } + + return ir_block_create_general_instr(self, ctx, label, op, ent, field, outype); +} + +/* PHI resolving breaks the SSA, and must thus be the last + * step before life-range calculation. + */ + +static bool ir_block_naive_phi(ir_block *self); +bool ir_function_naive_phi(ir_function *self) +{ + size_t i; + + for (i = 0; i < vec_size(self->blocks); ++i) + { + if (!ir_block_naive_phi(self->blocks[i])) + return false; + } + return true; +} + +static bool ir_block_naive_phi(ir_block *self) +{ + size_t i, p; /*, w;*/ + /* FIXME: optionally, create_phi can add the phis + * to a list so we don't need to loop through blocks + * - anyway: "don't optimize YET" + */ + for (i = 0; i < vec_size(self->instr); ++i) + { + ir_instr *instr = self->instr[i]; + if (instr->opcode != VINSTR_PHI) + continue; + + vec_remove(self->instr, i, 1); + --i; /* NOTE: i+1 below */ + + for (p = 0; p < vec_size(instr->phi); ++p) + { + ir_value *v = instr->phi[p].value; + ir_block *b = instr->phi[p].from; + + if (v->store == store_value && + vec_size(v->reads) == 1 && + vec_size(v->writes) == 1) + { + /* replace the value */ + if (!ir_instr_op(v->writes[0], 0, instr->_ops[0], true)) + return false; + } + else + { + /* force a move instruction */ + ir_instr *prevjump = vec_last(b->instr); + vec_pop(b->instr); + b->final = false; + instr->_ops[0]->store = store_global; + if (!ir_block_create_store(b, instr->context, instr->_ops[0], v)) + return false; + instr->_ops[0]->store = store_value; + vec_push(b->instr, prevjump); + b->final = true; + } + } + ir_instr_delete(instr); + } + return true; +} + +/*********************************************************************** + *IR Temp allocation code + * Propagating value life ranges by walking through the function backwards + * until no more changes are made. + * In theory this should happen once more than once for every nested loop + * level. + * Though this implementation might run an additional time for if nests. + */ + +/* Enumerate instructions used by value's life-ranges + */ +static void ir_block_enumerate(ir_block *self, size_t *_eid) +{ + size_t i; + size_t eid = *_eid; + for (i = 0; i < vec_size(self->instr); ++i) + { + self->instr[i]->eid = eid++; + } + *_eid = eid; +} + +/* Enumerate blocks and instructions. + * The block-enumeration is unordered! + * We do not really use the block enumreation, however + * the instruction enumeration is important for life-ranges. + */ +void ir_function_enumerate(ir_function *self) +{ + size_t i; + size_t instruction_id = 0; + for (i = 0; i < vec_size(self->blocks); ++i) + { + /* each block now gets an additional "entry" instruction id + * we can use to avoid point-life issues + */ + self->blocks[i]->entry_id = instruction_id; + ++instruction_id; + + self->blocks[i]->eid = i; + ir_block_enumerate(self->blocks[i], &instruction_id); + } +} + +/* Local-value allocator + * After finishing creating the liferange of all values used in a function + * we can allocate their global-positions. + * This is the counterpart to register-allocation in register machines. + */ +typedef struct { + ir_value **locals; + size_t *sizes; + size_t *positions; + bool *unique; +} function_allocator; + +static bool function_allocator_alloc(function_allocator *alloc, ir_value *var) +{ + ir_value *slot; + size_t vsize = ir_value_sizeof(var); + + var->code.local = vec_size(alloc->locals); + + slot = ir_value_var("reg", store_global, var->vtype); + if (!slot) + return false; + + if (!ir_value_life_merge_into(slot, var)) + goto localerror; + + vec_push(alloc->locals, slot); + vec_push(alloc->sizes, vsize); + vec_push(alloc->unique, var->unique_life); + + return true; + +localerror: + ir_value_delete(slot); + return false; +} + +static bool ir_function_allocator_assign(ir_function *self, function_allocator *alloc, ir_value *v) +{ + size_t a; + ir_value *slot; + + if (v->unique_life) + return function_allocator_alloc(alloc, v); + + for (a = 0; a < vec_size(alloc->locals); ++a) + { + /* if it's reserved for a unique liferange: skip */ + if (alloc->unique[a]) + continue; + + slot = alloc->locals[a]; + + /* never resize parameters + * will be required later when overlapping temps + locals + */ + if (a < vec_size(self->params) && + alloc->sizes[a] < ir_value_sizeof(v)) + { + continue; + } + + if (ir_values_overlap(v, slot)) + continue; + + if (!ir_value_life_merge_into(slot, v)) + return false; + + /* adjust size for this slot */ + if (alloc->sizes[a] < ir_value_sizeof(v)) + alloc->sizes[a] = ir_value_sizeof(v); + + v->code.local = a; + return true; + } + if (a >= vec_size(alloc->locals)) { + if (!function_allocator_alloc(alloc, v)) + return false; + } + return true; +} + +bool ir_function_allocate_locals(ir_function *self) +{ + size_t i; + bool retval = true; + size_t pos; + bool opt_gt = OPTS_OPTIMIZATION(OPTIM_GLOBAL_TEMPS); + + ir_value *v; + + function_allocator lockalloc, globalloc; + + if (!vec_size(self->locals) && !vec_size(self->values)) + return true; + + globalloc.locals = NULL; + globalloc.sizes = NULL; + globalloc.positions = NULL; + globalloc.unique = NULL; + lockalloc.locals = NULL; + lockalloc.sizes = NULL; + lockalloc.positions = NULL; + lockalloc.unique = NULL; + + for (i = 0; i < vec_size(self->locals); ++i) + { + v = self->locals[i]; + if ((self->flags & IR_FLAG_MASK_NO_LOCAL_TEMPS) || !OPTS_OPTIMIZATION(OPTIM_LOCAL_TEMPS)) { + v->locked = true; + v->unique_life = true; + } + else if (i >= vec_size(self->params)) + break; + else + v->locked = true; /* lock parameters locals */ + if (!function_allocator_alloc((v->locked || !opt_gt ? &lockalloc : &globalloc), v)) + goto error; + } + for (; i < vec_size(self->locals); ++i) + { + v = self->locals[i]; + if (!vec_size(v->life)) + continue; + if (!ir_function_allocator_assign(self, (v->locked || !opt_gt ? &lockalloc : &globalloc), v)) + goto error; + } + + /* Allocate a slot for any value that still exists */ + for (i = 0; i < vec_size(self->values); ++i) + { + v = self->values[i]; + + if (!vec_size(v->life)) + continue; + + /* CALL optimization: + * If the value is a parameter-temp: 1 write, 1 read from a CALL + * and it's not "locked", write it to the OFS_PARM directly. + */ + if (OPTS_OPTIMIZATION(OPTIM_CALL_STORES) && !v->locked && !v->unique_life) { + if (vec_size(v->reads) == 1 && vec_size(v->writes) == 1 && + (v->reads[0]->opcode == VINSTR_NRCALL || + (v->reads[0]->opcode >= INSTR_CALL0 && v->reads[0]->opcode <= INSTR_CALL8) + ) + ) + { + size_t param; + ir_instr *call = v->reads[0]; + if (!vec_ir_value_find(call->params, v, ¶m)) { + irerror(call->context, "internal error: unlocked parameter %s not found", v->name); + goto error; + } + ++opts_optimizationcount[OPTIM_CALL_STORES]; + v->callparam = true; + if (param < 8) + ir_value_code_setaddr(v, OFS_PARM0 + 3*param); + else { + size_t nprotos = vec_size(self->owner->extparam_protos); + ir_value *ep; + param -= 8; + if (nprotos > param) + ep = self->owner->extparam_protos[param]; + else + { + ep = ir_gen_extparam_proto(self->owner); + while (++nprotos <= param) + ep = ir_gen_extparam_proto(self->owner); + } + ir_instr_op(v->writes[0], 0, ep, true); + call->params[param+8] = ep; + } + continue; + } + if (vec_size(v->writes) == 1 && v->writes[0]->opcode == INSTR_CALL0) + { + v->store = store_return; + if (v->members[0]) v->members[0]->store = store_return; + if (v->members[1]) v->members[1]->store = store_return; + if (v->members[2]) v->members[2]->store = store_return; + ++opts_optimizationcount[OPTIM_CALL_STORES]; + continue; + } + } + + if (!ir_function_allocator_assign(self, (v->locked || !opt_gt ? &lockalloc : &globalloc), v)) + goto error; + } + + if (!lockalloc.sizes && !globalloc.sizes) { + goto cleanup; + } + vec_push(lockalloc.positions, 0); + vec_push(globalloc.positions, 0); + + /* Adjust slot positions based on sizes */ + if (lockalloc.sizes) { + pos = (vec_size(lockalloc.sizes) ? lockalloc.positions[0] : 0); + for (i = 1; i < vec_size(lockalloc.sizes); ++i) + { + pos = lockalloc.positions[i-1] + lockalloc.sizes[i-1]; + vec_push(lockalloc.positions, pos); + } + self->allocated_locals = pos + vec_last(lockalloc.sizes); + } + if (globalloc.sizes) { + pos = (vec_size(globalloc.sizes) ? globalloc.positions[0] : 0); + for (i = 1; i < vec_size(globalloc.sizes); ++i) + { + pos = globalloc.positions[i-1] + globalloc.sizes[i-1]; + vec_push(globalloc.positions, pos); + } + self->globaltemps = pos + vec_last(globalloc.sizes); + } + + /* Locals need to know their new position */ + for (i = 0; i < vec_size(self->locals); ++i) { + v = self->locals[i]; + if (v->locked || !opt_gt) + v->code.local = lockalloc.positions[v->code.local]; + else + v->code.local = globalloc.positions[v->code.local]; + } + /* Take over the actual slot positions on values */ + for (i = 0; i < vec_size(self->values); ++i) { + v = self->values[i]; + if (v->locked || !opt_gt) + v->code.local = lockalloc.positions[v->code.local]; + else + v->code.local = globalloc.positions[v->code.local]; + } + + goto cleanup; + +error: + retval = false; +cleanup: + for (i = 0; i < vec_size(lockalloc.locals); ++i) + ir_value_delete(lockalloc.locals[i]); + for (i = 0; i < vec_size(globalloc.locals); ++i) + ir_value_delete(globalloc.locals[i]); + vec_free(globalloc.unique); + vec_free(globalloc.locals); + vec_free(globalloc.sizes); + vec_free(globalloc.positions); + vec_free(lockalloc.unique); + vec_free(lockalloc.locals); + vec_free(lockalloc.sizes); + vec_free(lockalloc.positions); + return retval; +} + +/* Get information about which operand + * is read from, or written to. + */ +static void ir_op_read_write(int op, size_t *read, size_t *write) +{ + switch (op) + { + case VINSTR_JUMP: + case INSTR_GOTO: + *write = 0; + *read = 0; + break; + case INSTR_IF: + case INSTR_IFNOT: +#if 0 + case INSTR_IF_S: + case INSTR_IFNOT_S: +#endif + case INSTR_RETURN: + case VINSTR_COND: + *write = 0; + *read = 1; + break; + case INSTR_STOREP_F: + case INSTR_STOREP_V: + case INSTR_STOREP_S: + case INSTR_STOREP_ENT: + case INSTR_STOREP_FLD: + case INSTR_STOREP_FNC: + *write = 0; + *read = 7; + break; + default: + *write = 1; + *read = 6; + break; + }; +} + +static bool ir_block_living_add_instr(ir_block *self, size_t eid) +{ + size_t i; + const size_t vs = vec_size(self->living); + bool changed = false; + for (i = 0; i != vs; ++i) + { + if (ir_value_life_merge(self->living[i], eid)) + changed = true; + } + return changed; +} + +static bool ir_block_living_lock(ir_block *self) +{ + size_t i; + bool changed = false; + for (i = 0; i != vec_size(self->living); ++i) + { + if (!self->living[i]->locked) { + self->living[i]->locked = true; + changed = true; + } + } + return changed; +} + +static bool ir_block_life_propagate(ir_block *self, bool *changed) +{ + ir_instr *instr; + ir_value *value; + size_t i, o, p, mem, cnt; + /* bitmasks which operands are read from or written to */ + size_t read, write; + char dbg_ind[16]; + dbg_ind[0] = '#'; + dbg_ind[1] = '0'; + (void)dbg_ind; + + vec_free(self->living); + + p = vec_size(self->exits); + for (i = 0; i < p; ++i) { + ir_block *prev = self->exits[i]; + cnt = vec_size(prev->living); + for (o = 0; o < cnt; ++o) { + if (!vec_ir_value_find(self->living, prev->living[o], NULL)) + vec_push(self->living, prev->living[o]); + } + } + + i = vec_size(self->instr); + while (i) + { --i; + instr = self->instr[i]; + + /* See which operands are read and write operands */ + ir_op_read_write(instr->opcode, &read, &write); + + /* Go through the 3 main operands + * writes first, then reads + */ + for (o = 0; o < 3; ++o) + { + if (!instr->_ops[o]) /* no such operand */ + continue; + + value = instr->_ops[o]; + + /* We only care about locals */ + /* we also calculate parameter liferanges so that locals + * can take up parameter slots */ + if (value->store != store_value && + value->store != store_local && + value->store != store_param) + continue; + + /* write operands */ + /* When we write to a local, we consider it "dead" for the + * remaining upper part of the function, since in SSA a value + * can only be written once (== created) + */ + if (write & (1<living, value, &idx); + if (!in_living) + { + /* If the value isn't alive it hasn't been read before... */ + /* TODO: See if the warning can be emitted during parsing or AST processing + * otherwise have warning printed here. + * IF printing a warning here: include filecontext_t, + * and make sure it's only printed once + * since this function is run multiple times. + */ + /* con_err( "Value only written %s\n", value->name); */ + if (ir_value_life_merge(value, instr->eid)) + *changed = true; + } else { + /* since 'living' won't contain it + * anymore, merge the value, since + * (A) doesn't. + */ + if (ir_value_life_merge(value, instr->eid)) + *changed = true; + /* Then remove */ + vec_remove(self->living, idx, 1); + } + /* Removing a vector removes all members */ + for (mem = 0; mem < 3; ++mem) { + if (value->members[mem] && vec_ir_value_find(self->living, value->members[mem], &idx)) { + if (ir_value_life_merge(value->members[mem], instr->eid)) + *changed = true; + vec_remove(self->living, idx, 1); + } + } + /* Removing the last member removes the vector */ + if (value->memberof) { + value = value->memberof; + for (mem = 0; mem < 3; ++mem) { + if (value->members[mem] && vec_ir_value_find(self->living, value->members[mem], NULL)) + break; + } + if (mem == 3 && vec_ir_value_find(self->living, value, &idx)) { + if (ir_value_life_merge(value, instr->eid)) + *changed = true; + vec_remove(self->living, idx, 1); + } + } + } + } + + if (instr->opcode == INSTR_MUL_VF) + { + value = instr->_ops[2]; + /* the float source will get an additional lifetime */ + if (ir_value_life_merge(value, instr->eid+1)) + *changed = true; + if (value->memberof && ir_value_life_merge(value->memberof, instr->eid+1)) + *changed = true; + } + else if (instr->opcode == INSTR_MUL_FV || instr->opcode == INSTR_LOAD_V) + { + value = instr->_ops[1]; + /* the float source will get an additional lifetime */ + if (ir_value_life_merge(value, instr->eid+1)) + *changed = true; + if (value->memberof && ir_value_life_merge(value->memberof, instr->eid+1)) + *changed = true; + } + + for (o = 0; o < 3; ++o) + { + if (!instr->_ops[o]) /* no such operand */ + continue; + + value = instr->_ops[o]; + + /* We only care about locals */ + /* we also calculate parameter liferanges so that locals + * can take up parameter slots */ + if (value->store != store_value && + value->store != store_local && + value->store != store_param) + continue; + + /* read operands */ + if (read & (1<living, value, NULL)) + vec_push(self->living, value); + /* reading adds the full vector */ + if (value->memberof && !vec_ir_value_find(self->living, value->memberof, NULL)) + vec_push(self->living, value->memberof); + for (mem = 0; mem < 3; ++mem) { + if (value->members[mem] && !vec_ir_value_find(self->living, value->members[mem], NULL)) + vec_push(self->living, value->members[mem]); + } + } + } + /* PHI operands are always read operands */ + for (p = 0; p < vec_size(instr->phi); ++p) + { + value = instr->phi[p].value; + if (!vec_ir_value_find(self->living, value, NULL)) + vec_push(self->living, value); + /* reading adds the full vector */ + if (value->memberof && !vec_ir_value_find(self->living, value->memberof, NULL)) + vec_push(self->living, value->memberof); + for (mem = 0; mem < 3; ++mem) { + if (value->members[mem] && !vec_ir_value_find(self->living, value->members[mem], NULL)) + vec_push(self->living, value->members[mem]); + } + } + + /* on a call, all these values must be "locked" */ + if (instr->opcode >= INSTR_CALL0 && instr->opcode <= INSTR_CALL8) { + if (ir_block_living_lock(self)) + *changed = true; + } + /* call params are read operands too */ + for (p = 0; p < vec_size(instr->params); ++p) + { + value = instr->params[p]; + if (!vec_ir_value_find(self->living, value, NULL)) + vec_push(self->living, value); + /* reading adds the full vector */ + if (value->memberof && !vec_ir_value_find(self->living, value->memberof, NULL)) + vec_push(self->living, value->memberof); + for (mem = 0; mem < 3; ++mem) { + if (value->members[mem] && !vec_ir_value_find(self->living, value->members[mem], NULL)) + vec_push(self->living, value->members[mem]); + } + } + + /* (A) */ + if (ir_block_living_add_instr(self, instr->eid)) + *changed = true; + } + /* the "entry" instruction ID */ + if (ir_block_living_add_instr(self, self->entry_id)) + *changed = true; + + return true; +} + +bool ir_function_calculate_liferanges(ir_function *self) +{ + size_t i, s; + bool changed; + + /* parameters live at 0 */ + for (i = 0; i < vec_size(self->params); ++i) + ir_value_life_merge(self->locals[i], 0); + + do { + self->run_id++; + changed = false; + i = vec_size(self->blocks); + while (i--) { + ir_block_life_propagate(self->blocks[i], &changed); + } + } while (changed); + + if (vec_size(self->blocks)) { + ir_block *block = self->blocks[0]; + for (i = 0; i < vec_size(block->living); ++i) { + ir_value *v = block->living[i]; + if (v->store != store_local) + continue; + if (v->vtype == TYPE_VECTOR) + continue; + self->flags |= IR_FLAG_HAS_UNINITIALIZED; + /* find the instruction reading from it */ + for (s = 0; s < vec_size(v->reads); ++s) { + if (v->reads[s]->eid == v->life[0].end) + break; + } + if (s < vec_size(v->reads)) { + if (irwarning(v->context, WARN_USED_UNINITIALIZED, + "variable `%s` may be used uninitialized in this function\n" + " -> %s:%i", + v->name, + v->reads[s]->context.file, v->reads[s]->context.line) + ) + { + return false; + } + continue; + } + if (v->memberof) { + ir_value *vec = v->memberof; + for (s = 0; s < vec_size(vec->reads); ++s) { + if (vec->reads[s]->eid == v->life[0].end) + break; + } + if (s < vec_size(vec->reads)) { + if (irwarning(v->context, WARN_USED_UNINITIALIZED, + "variable `%s` may be used uninitialized in this function\n" + " -> %s:%i", + v->name, + vec->reads[s]->context.file, vec->reads[s]->context.line) + ) + { + return false; + } + continue; + } + } + if (irwarning(v->context, WARN_USED_UNINITIALIZED, + "variable `%s` may be used uninitialized in this function", v->name)) + { + return false; + } + } + } + return true; +} + +/*********************************************************************** + *IR Code-Generation + * + * Since the IR has the convention of putting 'write' operands + * at the beginning, we have to rotate the operands of instructions + * properly in order to generate valid QCVM code. + * + * Having destinations at a fixed position is more convenient. In QC + * this is *mostly* OPC, but FTE adds at least 2 instructions which + * read from from OPA, and store to OPB rather than OPC. Which is + * partially the reason why the implementation of these instructions + * in darkplaces has been delayed for so long. + * + * Breaking conventions is annoying... + */ +static bool ir_builder_gen_global(code_t *, ir_builder *self, ir_value *global, bool islocal); + +static bool gen_global_field(code_t *code, ir_value *global) +{ + if (global->hasvalue) + { + ir_value *fld = global->constval.vpointer; + if (!fld) { + irerror(global->context, "Invalid field constant with no field: %s", global->name); + return false; + } + + /* copy the field's value */ + ir_value_code_setaddr(global, vec_size(code->globals)); + vec_push(code->globals, fld->code.fieldaddr); + if (global->fieldtype == TYPE_VECTOR) { + vec_push(code->globals, fld->code.fieldaddr+1); + vec_push(code->globals, fld->code.fieldaddr+2); + } + } + else + { + ir_value_code_setaddr(global, vec_size(code->globals)); + vec_push(code->globals, 0); + if (global->fieldtype == TYPE_VECTOR) { + vec_push(code->globals, 0); + vec_push(code->globals, 0); + } + } + if (global->code.globaladdr < 0) + return false; + return true; +} + +static bool gen_global_pointer(code_t *code, ir_value *global) +{ + if (global->hasvalue) + { + ir_value *target = global->constval.vpointer; + if (!target) { + irerror(global->context, "Invalid pointer constant: %s", global->name); + /* NULL pointers are pointing to the NULL constant, which also + * sits at address 0, but still has an ir_value for itself. + */ + return false; + } + + /* Here, relocations ARE possible - in fteqcc-enhanced-qc: + * void() foo; <- proto + * void() *fooptr = &foo; + * void() foo = { code } + */ + if (!target->code.globaladdr) { + /* FIXME: Check for the constant nullptr ir_value! + * because then code.globaladdr being 0 is valid. + */ + irerror(global->context, "FIXME: Relocation support"); + return false; + } + + ir_value_code_setaddr(global, vec_size(code->globals)); + vec_push(code->globals, target->code.globaladdr); + } + else + { + ir_value_code_setaddr(global, vec_size(code->globals)); + vec_push(code->globals, 0); + } + if (global->code.globaladdr < 0) + return false; + return true; +} + +static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *block) +{ + prog_section_statement stmt; + ir_instr *instr; + ir_block *target; + ir_block *ontrue; + ir_block *onfalse; + size_t stidx; + size_t i; + + block->generated = true; + block->code_start = vec_size(code->statements); + for (i = 0; i < vec_size(block->instr); ++i) + { + instr = block->instr[i]; + + if (instr->opcode == VINSTR_PHI) { + irerror(block->context, "cannot generate virtual instruction (phi)"); + return false; + } + + if (instr->opcode == VINSTR_JUMP) { + target = instr->bops[0]; + /* for uncoditional jumps, if the target hasn't been generated + * yet, we generate them right here. + */ + if (!target->generated) + return gen_blocks_recursive(code, func, target); + + /* otherwise we generate a jump instruction */ + stmt.opcode = INSTR_GOTO; + stmt.o1.s1 = (target->code_start) - vec_size(code->statements); + stmt.o2.s1 = 0; + stmt.o3.s1 = 0; + if (stmt.o1.s1 != 1) + code_push_statement(code, &stmt, instr->context.line); + + /* no further instructions can be in this block */ + return true; + } + + if (instr->opcode == VINSTR_COND) { + ontrue = instr->bops[0]; + onfalse = instr->bops[1]; + /* TODO: have the AST signal which block should + * come first: eg. optimize IFs without ELSE... + */ + + stmt.o1.u1 = ir_value_code_addr(instr->_ops[0]); + stmt.o2.u1 = 0; + stmt.o3.s1 = 0; + + if (ontrue->generated) { + stmt.opcode = INSTR_IF; + stmt.o2.s1 = (ontrue->code_start) - vec_size(code->statements); + if (stmt.o2.s1 != 1) + code_push_statement(code, &stmt, instr->context.line); + } + if (onfalse->generated) { + stmt.opcode = INSTR_IFNOT; + stmt.o2.s1 = (onfalse->code_start) - vec_size(code->statements); + if (stmt.o2.s1 != 1) + code_push_statement(code, &stmt, instr->context.line); + } + if (!ontrue->generated) { + if (onfalse->generated) + return gen_blocks_recursive(code, func, ontrue); + } + if (!onfalse->generated) { + if (ontrue->generated) + return gen_blocks_recursive(code, func, onfalse); + } + /* neither ontrue nor onfalse exist */ + stmt.opcode = INSTR_IFNOT; + if (!instr->likely) { + /* Honor the likelyhood hint */ + ir_block *tmp = onfalse; + stmt.opcode = INSTR_IF; + onfalse = ontrue; + ontrue = tmp; + } + stidx = vec_size(code->statements); + code_push_statement(code, &stmt, instr->context.line); + /* on false we jump, so add ontrue-path */ + if (!gen_blocks_recursive(code, func, ontrue)) + return false; + /* fixup the jump address */ + code->statements[stidx].o2.s1 = vec_size(code->statements) - stidx; + /* generate onfalse path */ + if (onfalse->generated) { + /* fixup the jump address */ + code->statements[stidx].o2.s1 = (onfalse->code_start) - (stidx); + if (stidx+2 == vec_size(code->statements) && code->statements[stidx].o2.s1 == 1) { + code->statements[stidx] = code->statements[stidx+1]; + if (code->statements[stidx].o1.s1 < 0) + code->statements[stidx].o1.s1++; + code_pop_statement(code); + } + stmt.opcode = vec_last(code->statements).opcode; + if (stmt.opcode == INSTR_GOTO || + stmt.opcode == INSTR_IF || + stmt.opcode == INSTR_IFNOT || + stmt.opcode == INSTR_RETURN || + stmt.opcode == INSTR_DONE) + { + /* no use jumping from here */ + return true; + } + /* may have been generated in the previous recursive call */ + stmt.opcode = INSTR_GOTO; + stmt.o1.s1 = (onfalse->code_start) - vec_size(code->statements); + stmt.o2.s1 = 0; + stmt.o3.s1 = 0; + if (stmt.o1.s1 != 1) + code_push_statement(code, &stmt, instr->context.line); + return true; + } + else if (stidx+2 == vec_size(code->statements) && code->statements[stidx].o2.s1 == 1) { + code->statements[stidx] = code->statements[stidx+1]; + if (code->statements[stidx].o1.s1 < 0) + code->statements[stidx].o1.s1++; + code_pop_statement(code); + } + /* if not, generate now */ + return gen_blocks_recursive(code, func, onfalse); + } + + if ( (instr->opcode >= INSTR_CALL0 && instr->opcode <= INSTR_CALL8) + || instr->opcode == VINSTR_NRCALL) + { + size_t p, first; + ir_value *retvalue; + + first = vec_size(instr->params); + if (first > 8) + first = 8; + for (p = 0; p < first; ++p) + { + ir_value *param = instr->params[p]; + if (param->callparam) + continue; + + stmt.opcode = INSTR_STORE_F; + stmt.o3.u1 = 0; + + if (param->vtype == TYPE_FIELD) + stmt.opcode = field_store_instr[param->fieldtype]; + else if (param->vtype == TYPE_NIL) + stmt.opcode = INSTR_STORE_V; + else + stmt.opcode = type_store_instr[param->vtype]; + stmt.o1.u1 = ir_value_code_addr(param); + stmt.o2.u1 = OFS_PARM0 + 3 * p; + code_push_statement(code, &stmt, instr->context.line); + } + /* Now handle extparams */ + first = vec_size(instr->params); + for (; p < first; ++p) + { + ir_builder *ir = func->owner; + ir_value *param = instr->params[p]; + ir_value *targetparam; + + if (param->callparam) + continue; + + if (p-8 >= vec_size(ir->extparams)) + ir_gen_extparam(code, ir); + + targetparam = ir->extparams[p-8]; + + stmt.opcode = INSTR_STORE_F; + stmt.o3.u1 = 0; + + if (param->vtype == TYPE_FIELD) + stmt.opcode = field_store_instr[param->fieldtype]; + else if (param->vtype == TYPE_NIL) + stmt.opcode = INSTR_STORE_V; + else + stmt.opcode = type_store_instr[param->vtype]; + stmt.o1.u1 = ir_value_code_addr(param); + stmt.o2.u1 = ir_value_code_addr(targetparam); + code_push_statement(code, &stmt, instr->context.line); + } + + stmt.opcode = INSTR_CALL0 + vec_size(instr->params); + if (stmt.opcode > INSTR_CALL8) + stmt.opcode = INSTR_CALL8; + stmt.o1.u1 = ir_value_code_addr(instr->_ops[1]); + stmt.o2.u1 = 0; + stmt.o3.u1 = 0; + code_push_statement(code, &stmt, instr->context.line); + + retvalue = instr->_ops[0]; + if (retvalue && retvalue->store != store_return && + (retvalue->store == store_global || vec_size(retvalue->life))) + { + /* not to be kept in OFS_RETURN */ + if (retvalue->vtype == TYPE_FIELD && OPTS_FLAG(ADJUST_VECTOR_FIELDS)) + stmt.opcode = field_store_instr[retvalue->fieldtype]; + else + stmt.opcode = type_store_instr[retvalue->vtype]; + stmt.o1.u1 = OFS_RETURN; + stmt.o2.u1 = ir_value_code_addr(retvalue); + stmt.o3.u1 = 0; + code_push_statement(code, &stmt, instr->context.line); + } + continue; + } + + if (instr->opcode == INSTR_STATE) { + irerror(block->context, "TODO: state instruction"); + return false; + } + + stmt.opcode = instr->opcode; + stmt.o1.u1 = 0; + stmt.o2.u1 = 0; + stmt.o3.u1 = 0; + + /* This is the general order of operands */ + if (instr->_ops[0]) + stmt.o3.u1 = ir_value_code_addr(instr->_ops[0]); + + if (instr->_ops[1]) + stmt.o1.u1 = ir_value_code_addr(instr->_ops[1]); + + if (instr->_ops[2]) + stmt.o2.u1 = ir_value_code_addr(instr->_ops[2]); + + if (stmt.opcode == INSTR_RETURN || stmt.opcode == INSTR_DONE) + { + stmt.o1.u1 = stmt.o3.u1; + stmt.o3.u1 = 0; + } + else if ((stmt.opcode >= INSTR_STORE_F && + stmt.opcode <= INSTR_STORE_FNC) || + (stmt.opcode >= INSTR_STOREP_F && + stmt.opcode <= INSTR_STOREP_FNC)) + { + /* 2-operand instructions with A -> B */ + stmt.o2.u1 = stmt.o3.u1; + stmt.o3.u1 = 0; + + /* tiny optimization, don't output + * STORE a, a + */ + if (stmt.o2.u1 == stmt.o1.u1 && + OPTS_OPTIMIZATION(OPTIM_PEEPHOLE)) + { + ++opts_optimizationcount[OPTIM_PEEPHOLE]; + continue; + } + } + + code_push_statement(code, &stmt, instr->context.line); + } + return true; +} + +static bool gen_function_code(code_t *code, ir_function *self) +{ + ir_block *block; + prog_section_statement stmt, *retst; + + /* Starting from entry point, we generate blocks "as they come" + * for now. Dead blocks will not be translated obviously. + */ + if (!vec_size(self->blocks)) { + irerror(self->context, "Function '%s' declared without body.", self->name); + return false; + } + + block = self->blocks[0]; + if (block->generated) + return true; + + if (!gen_blocks_recursive(code, self, block)) { + irerror(self->context, "failed to generate blocks for '%s'", self->name); + return false; + } + + /* code_write and qcvm -disasm need to know that the function ends here */ + retst = &vec_last(code->statements); + if (OPTS_OPTIMIZATION(OPTIM_VOID_RETURN) && + self->outtype == TYPE_VOID && + retst->opcode == INSTR_RETURN && + !retst->o1.u1 && !retst->o2.u1 && !retst->o3.u1) + { + retst->opcode = INSTR_DONE; + ++opts_optimizationcount[OPTIM_VOID_RETURN]; + } else { + stmt.opcode = INSTR_DONE; + stmt.o1.u1 = 0; + stmt.o2.u1 = 0; + stmt.o3.u1 = 0; + code_push_statement(code, &stmt, vec_last(code->linenums)); + } + return true; +} + +static qcint ir_builder_filestring(code_t *code, ir_builder *ir, const char *filename) +{ + /* NOTE: filename pointers are copied, we never strdup them, + * thus we can use pointer-comparison to find the string. + */ + size_t i; + qcint str; + + for (i = 0; i < vec_size(ir->filenames); ++i) { + if (ir->filenames[i] == filename) + return ir->filestrings[i]; + } + + str = code_genstring(code, filename); + vec_push(ir->filenames, filename); + vec_push(ir->filestrings, str); + return str; +} + +static bool gen_global_function(code_t *code, ir_builder *ir, ir_value *global) +{ + prog_section_function fun; + ir_function *irfun; + + size_t i; + + if (!global->hasvalue || (!global->constval.vfunc)) + { + irerror(global->context, "Invalid state of function-global: not constant: %s", global->name); + return false; + } + + irfun = global->constval.vfunc; + + fun.name = global->code.name; + fun.file = ir_builder_filestring(code, ir, global->context.file); + fun.profile = 0; /* always 0 */ + fun.nargs = vec_size(irfun->params); + if (fun.nargs > 8) + fun.nargs = 8; + + for (i = 0;i < 8; ++i) { + if ((int32_t)i >= fun.nargs) + fun.argsize[i] = 0; + else + fun.argsize[i] = type_sizeof_[irfun->params[i]]; + } + + fun.firstlocal = 0; + fun.locals = irfun->allocated_locals; + + if (irfun->builtin) + fun.entry = irfun->builtin+1; + else { + irfun->code_function_def = vec_size(code->functions); + fun.entry = vec_size(code->statements); + } + + vec_push(code->functions, fun); + return true; +} + +static ir_value* ir_gen_extparam_proto(ir_builder *ir) +{ + ir_value *global; + char name[128]; + + util_snprintf(name, sizeof(name), "EXTPARM#%i", (int)(vec_size(ir->extparam_protos))); + global = ir_value_var(name, store_global, TYPE_VECTOR); + + vec_push(ir->extparam_protos, global); + return global; +} + +static void ir_gen_extparam(code_t *code, ir_builder *ir) +{ + prog_section_def def; + ir_value *global; + + if (vec_size(ir->extparam_protos) < vec_size(ir->extparams)+1) + global = ir_gen_extparam_proto(ir); + else + global = ir->extparam_protos[vec_size(ir->extparams)]; + + def.name = code_genstring(code, global->name); + def.type = TYPE_VECTOR; + def.offset = vec_size(code->globals); + + vec_push(code->defs, def); + + ir_value_code_setaddr(global, def.offset); + + vec_push(code->globals, 0); + vec_push(code->globals, 0); + vec_push(code->globals, 0); + + vec_push(ir->extparams, global); +} + +static bool gen_function_extparam_copy(code_t *code, ir_function *self) +{ + size_t i, ext, numparams; + + ir_builder *ir = self->owner; + ir_value *ep; + prog_section_statement stmt; + + numparams = vec_size(self->params); + if (!numparams) + return true; + + stmt.opcode = INSTR_STORE_F; + stmt.o3.s1 = 0; + for (i = 8; i < numparams; ++i) { + ext = i - 8; + if (ext >= vec_size(ir->extparams)) + ir_gen_extparam(code, ir); + + ep = ir->extparams[ext]; + + stmt.opcode = type_store_instr[self->locals[i]->vtype]; + if (self->locals[i]->vtype == TYPE_FIELD && + self->locals[i]->fieldtype == TYPE_VECTOR) + { + stmt.opcode = INSTR_STORE_V; + } + stmt.o1.u1 = ir_value_code_addr(ep); + stmt.o2.u1 = ir_value_code_addr(self->locals[i]); + code_push_statement(code, &stmt, self->context.line); + } + + return true; +} + +static bool gen_function_varargs_copy(code_t *code, ir_function *self) +{ + size_t i, ext, numparams, maxparams; + + ir_builder *ir = self->owner; + ir_value *ep; + prog_section_statement stmt; + + numparams = vec_size(self->params); + if (!numparams) + return true; + + stmt.opcode = INSTR_STORE_V; + stmt.o3.s1 = 0; + maxparams = numparams + self->max_varargs; + for (i = numparams; i < maxparams; ++i) { + if (i < 8) { + stmt.o1.u1 = OFS_PARM0 + 3*i; + stmt.o2.u1 = ir_value_code_addr(self->locals[i]); + code_push_statement(code, &stmt, self->context.line); + continue; + } + ext = i - 8; + while (ext >= vec_size(ir->extparams)) + ir_gen_extparam(code, ir); + + ep = ir->extparams[ext]; + + stmt.o1.u1 = ir_value_code_addr(ep); + stmt.o2.u1 = ir_value_code_addr(self->locals[i]); + code_push_statement(code, &stmt, self->context.line); + } + + return true; +} + +static bool gen_function_locals(code_t *code, ir_builder *ir, ir_value *global) +{ + prog_section_function *def; + ir_function *irfun; + size_t i; + uint32_t firstlocal, firstglobal; + + irfun = global->constval.vfunc; + def = code->functions + irfun->code_function_def; + + if (OPTS_OPTION_BOOL(OPTION_G) || + !OPTS_OPTIMIZATION(OPTIM_OVERLAP_LOCALS) || + (irfun->flags & IR_FLAG_MASK_NO_OVERLAP)) + { + firstlocal = def->firstlocal = vec_size(code->globals); + } else { + firstlocal = def->firstlocal = ir->first_common_local; + ++opts_optimizationcount[OPTIM_OVERLAP_LOCALS]; + } + + firstglobal = (OPTS_OPTIMIZATION(OPTIM_GLOBAL_TEMPS) ? ir->first_common_globaltemp : firstlocal); + + for (i = vec_size(code->globals); i < firstlocal + irfun->allocated_locals; ++i) + vec_push(code->globals, 0); + for (i = 0; i < vec_size(irfun->locals); ++i) { + ir_value *v = irfun->locals[i]; + if (v->locked || !OPTS_OPTIMIZATION(OPTIM_GLOBAL_TEMPS)) { + ir_value_code_setaddr(v, firstlocal + v->code.local); + if (!ir_builder_gen_global(code, ir, irfun->locals[i], true)) { + irerror(irfun->locals[i]->context, "failed to generate local %s", irfun->locals[i]->name); + return false; + } + } + else + ir_value_code_setaddr(v, firstglobal + v->code.local); + } + for (i = 0; i < vec_size(irfun->values); ++i) + { + ir_value *v = irfun->values[i]; + if (v->callparam) + continue; + if (v->locked) + ir_value_code_setaddr(v, firstlocal + v->code.local); + else + ir_value_code_setaddr(v, firstglobal + v->code.local); + } + return true; +} + +static bool gen_global_function_code(code_t *code, ir_builder *ir, ir_value *global) +{ + prog_section_function *fundef; + ir_function *irfun; + + (void)ir; + + irfun = global->constval.vfunc; + if (!irfun) { + if (global->cvq == CV_NONE) { + irwarning(global->context, WARN_IMPLICIT_FUNCTION_POINTER, + "function `%s` has no body and in QC implicitly becomes a function-pointer", global->name); + } + /* this was a function pointer, don't generate code for those */ + return true; + } + + if (irfun->builtin) + return true; + + if (irfun->code_function_def < 0) { + irerror(irfun->context, "`%s`: IR global wasn't generated, failed to access function-def", irfun->name); + return false; + } + fundef = &code->functions[irfun->code_function_def]; + + fundef->entry = vec_size(code->statements); + if (!gen_function_locals(code, ir, global)) { + irerror(irfun->context, "Failed to generate locals for function %s", irfun->name); + return false; + } + if (!gen_function_extparam_copy(code, irfun)) { + irerror(irfun->context, "Failed to generate extparam-copy code for function %s", irfun->name); + return false; + } + if (irfun->max_varargs && !gen_function_varargs_copy(code, irfun)) { + irerror(irfun->context, "Failed to generate vararg-copy code for function %s", irfun->name); + return false; + } + if (!gen_function_code(code, irfun)) { + irerror(irfun->context, "Failed to generate code for function %s", irfun->name); + return false; + } + return true; +} + +static void gen_vector_defs(code_t *code, prog_section_def def, const char *name) +{ + char *component; + size_t len, i; + + if (!name || name[0] == '#' || OPTS_FLAG(SINGLE_VECTOR_DEFS)) + return; + + def.type = TYPE_FLOAT; + + len = strlen(name); + + component = (char*)mem_a(len+3); + memcpy(component, name, len); + len += 2; + component[len-0] = 0; + component[len-2] = '_'; + + component[len-1] = 'x'; + + for (i = 0; i < 3; ++i) { + def.name = code_genstring(code, component); + vec_push(code->defs, def); + def.offset++; + component[len-1]++; + } + + mem_d(component); +} + +static void gen_vector_fields(code_t *code, prog_section_field fld, const char *name) +{ + char *component; + size_t len, i; + + if (!name || OPTS_FLAG(SINGLE_VECTOR_DEFS)) + return; + + fld.type = TYPE_FLOAT; + + len = strlen(name); + + component = (char*)mem_a(len+3); + memcpy(component, name, len); + len += 2; + component[len-0] = 0; + component[len-2] = '_'; + + component[len-1] = 'x'; + + for (i = 0; i < 3; ++i) { + fld.name = code_genstring(code, component); + vec_push(code->fields, fld); + fld.offset++; + component[len-1]++; + } + + mem_d(component); +} + +static bool ir_builder_gen_global(code_t *code, ir_builder *self, ir_value *global, bool islocal) +{ + size_t i; + int32_t *iptr; + prog_section_def def; + bool pushdef = opts.optimizeoff; + + def.type = global->vtype; + def.offset = vec_size(code->globals); + def.name = 0; + if (OPTS_OPTION_BOOL(OPTION_G) || !islocal) + { + pushdef = true; + + if (OPTS_OPTIMIZATION(OPTIM_STRIP_CONSTANT_NAMES) && + !(global->flags & IR_FLAG_INCLUDE_DEF) && + (global->name[0] == '#' || global->cvq == CV_CONST)) + { + pushdef = false; + } + + if (pushdef && global->name) { + if (global->name[0] == '#') { + if (!self->str_immediate) + self->str_immediate = code_genstring(code, "IMMEDIATE"); + def.name = global->code.name = self->str_immediate; + } + else + def.name = global->code.name = code_genstring(code, global->name); + } + else + def.name = 0; + if (islocal) { + def.offset = ir_value_code_addr(global); + vec_push(code->defs, def); + if (global->vtype == TYPE_VECTOR) + gen_vector_defs(code, def, global->name); + else if (global->vtype == TYPE_FIELD && global->fieldtype == TYPE_VECTOR) + gen_vector_defs(code, def, global->name); + return true; + } + } + if (islocal) + return true; + + switch (global->vtype) + { + case TYPE_VOID: + if (!strcmp(global->name, "end_sys_globals")) { + /* TODO: remember this point... all the defs before this one + * should be checksummed and added to progdefs.h when we generate it. + */ + } + else if (!strcmp(global->name, "end_sys_fields")) { + /* TODO: same as above but for entity-fields rather than globsl + */ + } + else + irwarning(global->context, WARN_VOID_VARIABLES, "unrecognized variable of type void `%s`", + global->name); + /* I'd argue setting it to 0 is sufficient, but maybe some depend on knowing how far + * the system fields actually go? Though the engine knows this anyway... + * Maybe this could be an -foption + * fteqcc creates data for end_sys_* - of size 1, so let's do the same + */ + ir_value_code_setaddr(global, vec_size(code->globals)); + vec_push(code->globals, 0); + /* Add the def */ + if (pushdef) vec_push(code->defs, def); + return true; + case TYPE_POINTER: + if (pushdef) vec_push(code->defs, def); + return gen_global_pointer(code, global); + case TYPE_FIELD: + if (pushdef) { + vec_push(code->defs, def); + if (global->fieldtype == TYPE_VECTOR) + gen_vector_defs(code, def, global->name); + } + return gen_global_field(code, global); + case TYPE_ENTITY: + /* fall through */ + case TYPE_FLOAT: + { + ir_value_code_setaddr(global, vec_size(code->globals)); + if (global->hasvalue) { + iptr = (int32_t*)&global->constval.ivec[0]; + vec_push(code->globals, *iptr); + } else { + vec_push(code->globals, 0); + } + if (!islocal && global->cvq != CV_CONST) + def.type |= DEF_SAVEGLOBAL; + if (pushdef) vec_push(code->defs, def); + + return global->code.globaladdr >= 0; + } + case TYPE_STRING: + { + ir_value_code_setaddr(global, vec_size(code->globals)); + if (global->hasvalue) { + uint32_t load = code_genstring(code, global->constval.vstring); + vec_push(code->globals, load); + } else { + vec_push(code->globals, 0); + } + if (!islocal && global->cvq != CV_CONST) + def.type |= DEF_SAVEGLOBAL; + if (pushdef) vec_push(code->defs, def); + return global->code.globaladdr >= 0; + } + case TYPE_VECTOR: + { + size_t d; + ir_value_code_setaddr(global, vec_size(code->globals)); + if (global->hasvalue) { + iptr = (int32_t*)&global->constval.ivec[0]; + vec_push(code->globals, iptr[0]); + if (global->code.globaladdr < 0) + return false; + for (d = 1; d < type_sizeof_[global->vtype]; ++d) { + vec_push(code->globals, iptr[d]); + } + } else { + vec_push(code->globals, 0); + if (global->code.globaladdr < 0) + return false; + for (d = 1; d < type_sizeof_[global->vtype]; ++d) { + vec_push(code->globals, 0); + } + } + if (!islocal && global->cvq != CV_CONST) + def.type |= DEF_SAVEGLOBAL; + + if (pushdef) { + vec_push(code->defs, def); + def.type &= ~DEF_SAVEGLOBAL; + gen_vector_defs(code, def, global->name); + } + return global->code.globaladdr >= 0; + } + case TYPE_FUNCTION: + ir_value_code_setaddr(global, vec_size(code->globals)); + if (!global->hasvalue) { + vec_push(code->globals, 0); + if (global->code.globaladdr < 0) + return false; + } else { + vec_push(code->globals, vec_size(code->functions)); + if (!gen_global_function(code, self, global)) + return false; + } + if (!islocal && global->cvq != CV_CONST) + def.type |= DEF_SAVEGLOBAL; + if (pushdef) vec_push(code->defs, def); + return true; + case TYPE_VARIANT: + /* assume biggest type */ + ir_value_code_setaddr(global, vec_size(code->globals)); + vec_push(code->globals, 0); + for (i = 1; i < type_sizeof_[TYPE_VARIANT]; ++i) + vec_push(code->globals, 0); + return true; + default: + /* refuse to create 'void' type or any other fancy business. */ + irerror(global->context, "Invalid type for global variable `%s`: %s", + global->name, type_name[global->vtype]); + return false; + } +} + +static GMQCC_INLINE void ir_builder_prepare_field(code_t *code, ir_value *field) +{ + field->code.fieldaddr = code_alloc_field(code, type_sizeof_[field->fieldtype]); +} + +static bool ir_builder_gen_field(code_t *code, ir_builder *self, ir_value *field) +{ + prog_section_def def; + prog_section_field fld; + + (void)self; + + def.type = (uint16_t)field->vtype; + def.offset = (uint16_t)vec_size(code->globals); + + /* create a global named the same as the field */ + if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_GMQCC) { + /* in our standard, the global gets a dot prefix */ + size_t len = strlen(field->name); + char name[1024]; + + /* we really don't want to have to allocate this, and 1024 + * bytes is more than enough for a variable/field name + */ + if (len+2 >= sizeof(name)) { + irerror(field->context, "invalid field name size: %u", (unsigned int)len); + return false; + } + + name[0] = '.'; + memcpy(name+1, field->name, len); /* no strncpy - we used strlen above */ + name[len+1] = 0; + + def.name = code_genstring(code, name); + fld.name = def.name + 1; /* we reuse that string table entry */ + } else { + /* in plain QC, there cannot be a global with the same name, + * and so we also name the global the same. + * FIXME: fteqcc should create a global as well + * check if it actually uses the same name. Probably does + */ + def.name = code_genstring(code, field->name); + fld.name = def.name; + } + + field->code.name = def.name; + + vec_push(code->defs, def); + + fld.type = field->fieldtype; + + if (fld.type == TYPE_VOID) { + irerror(field->context, "field is missing a type: %s - don't know its size", field->name); + return false; + } + + fld.offset = field->code.fieldaddr; + + vec_push(code->fields, fld); + + ir_value_code_setaddr(field, vec_size(code->globals)); + vec_push(code->globals, fld.offset); + if (fld.type == TYPE_VECTOR) { + vec_push(code->globals, fld.offset+1); + vec_push(code->globals, fld.offset+2); + } + + if (field->fieldtype == TYPE_VECTOR) { + gen_vector_defs (code, def, field->name); + gen_vector_fields(code, fld, field->name); + } + + return field->code.globaladdr >= 0; +} + +bool ir_builder_generate(code_t *code, ir_builder *self, const char *filename) +{ + prog_section_statement stmt; + size_t i; + char *lnofile = NULL; + + for (i = 0; i < vec_size(self->fields); ++i) + { + ir_builder_prepare_field(code, self->fields[i]); + } + + for (i = 0; i < vec_size(self->globals); ++i) + { + if (!ir_builder_gen_global(code, self, self->globals[i], false)) { + return false; + } + if (self->globals[i]->vtype == TYPE_FUNCTION) { + ir_function *func = self->globals[i]->constval.vfunc; + if (func && self->max_locals < func->allocated_locals && + !(func->flags & IR_FLAG_MASK_NO_OVERLAP)) + { + self->max_locals = func->allocated_locals; + } + if (func && self->max_globaltemps < func->globaltemps) + self->max_globaltemps = func->globaltemps; + } + } + + for (i = 0; i < vec_size(self->fields); ++i) + { + if (!ir_builder_gen_field(code, self, self->fields[i])) { + return false; + } + } + + /* generate nil */ + ir_value_code_setaddr(self->nil, vec_size(code->globals)); + vec_push(code->globals, 0); + vec_push(code->globals, 0); + vec_push(code->globals, 0); + + /* generate global temps */ + self->first_common_globaltemp = vec_size(code->globals); + for (i = 0; i < self->max_globaltemps; ++i) { + vec_push(code->globals, 0); + } + /* generate common locals */ + self->first_common_local = vec_size(code->globals); + for (i = 0; i < self->max_locals; ++i) { + vec_push(code->globals, 0); + } + + /* generate function code */ + for (i = 0; i < vec_size(self->globals); ++i) + { + if (self->globals[i]->vtype == TYPE_FUNCTION) { + if (!gen_global_function_code(code, self, self->globals[i])) { + return false; + } + } + } + + if (vec_size(code->globals) >= 65536) { + irerror(vec_last(self->globals)->context, "This progs file would require more globals than the metadata can handle. Bailing out."); + return false; + } + + /* DP errors if the last instruction is not an INSTR_DONE. */ + if (vec_last(code->statements).opcode != INSTR_DONE) + { + stmt.opcode = INSTR_DONE; + stmt.o1.u1 = 0; + stmt.o2.u1 = 0; + stmt.o3.u1 = 0; + code_push_statement(code, &stmt, vec_last(code->linenums)); + } + + if (OPTS_OPTION_BOOL(OPTION_PP_ONLY)) + return true; + + if (vec_size(code->statements) != vec_size(code->linenums)) { + con_err("Linecounter wrong: %lu != %lu\n", + (unsigned long)vec_size(code->statements), + (unsigned long)vec_size(code->linenums)); + } else if (OPTS_FLAG(LNO)) { + char *dot; + size_t filelen = strlen(filename); + + memcpy(vec_add(lnofile, filelen+1), filename, filelen+1); + dot = strrchr(lnofile, '.'); + if (!dot) { + vec_pop(lnofile); + } else { + vec_shrinkto(lnofile, dot - lnofile); + } + memcpy(vec_add(lnofile, 5), ".lno", 5); + } + + if (!OPTS_OPTION_BOOL(OPTION_QUIET)) { + if (lnofile) + con_out("writing '%s' and '%s'...\n", filename, lnofile); + else + con_out("writing '%s'\n", filename); + } + if (!code_write(code, filename, lnofile)) { + vec_free(lnofile); + return false; + } + vec_free(lnofile); + return true; +} + +/*********************************************************************** + *IR DEBUG Dump functions... + */ + +#define IND_BUFSZ 1024 + +#ifdef _MSC_VER +# define strncat(dst, src, sz) strncat_s(dst, sz, src, _TRUNCATE) +#endif + +static const char *qc_opname(int op) +{ + if (op < 0) return ""; + if (op < (int)( sizeof(asm_instr) / sizeof(asm_instr[0]) )) + return asm_instr[op].m; + switch (op) { + case VINSTR_PHI: return "PHI"; + case VINSTR_JUMP: return "JUMP"; + case VINSTR_COND: return "COND"; + default: return ""; + } +} + +void ir_builder_dump(ir_builder *b, int (*oprintf)(const char*, ...)) +{ + size_t i; + char indent[IND_BUFSZ]; + indent[0] = '\t'; + indent[1] = 0; + + oprintf("module %s\n", b->name); + for (i = 0; i < vec_size(b->globals); ++i) + { + oprintf("global "); + if (b->globals[i]->hasvalue) + oprintf("%s = ", b->globals[i]->name); + ir_value_dump(b->globals[i], oprintf); + oprintf("\n"); + } + for (i = 0; i < vec_size(b->functions); ++i) + ir_function_dump(b->functions[i], indent, oprintf); + oprintf("endmodule %s\n", b->name); +} + +static const char *storenames[] = { + "[global]", "[local]", "[param]", "[value]", "[return]" +}; + +void ir_function_dump(ir_function *f, char *ind, + int (*oprintf)(const char*, ...)) +{ + size_t i; + if (f->builtin != 0) { + oprintf("%sfunction %s = builtin %i\n", ind, f->name, -f->builtin); + return; + } + oprintf("%sfunction %s\n", ind, f->name); + strncat(ind, "\t", IND_BUFSZ); + if (vec_size(f->locals)) + { + oprintf("%s%i locals:\n", ind, (int)vec_size(f->locals)); + for (i = 0; i < vec_size(f->locals); ++i) { + oprintf("%s\t", ind); + ir_value_dump(f->locals[i], oprintf); + oprintf("\n"); + } + } + oprintf("%sliferanges:\n", ind); + for (i = 0; i < vec_size(f->locals); ++i) { + const char *attr = ""; + size_t l, m; + ir_value *v = f->locals[i]; + if (v->unique_life && v->locked) + attr = "unique,locked "; + else if (v->unique_life) + attr = "unique "; + else if (v->locked) + attr = "locked "; + oprintf("%s\t%s: %s %s %s%s@%i ", ind, v->name, type_name[v->vtype], + storenames[v->store], + attr, (v->callparam ? "callparam " : ""), + (int)v->code.local); + if (!v->life) + oprintf("[null]"); + for (l = 0; l < vec_size(v->life); ++l) { + oprintf("[%i,%i] ", v->life[l].start, v->life[l].end); + } + oprintf("\n"); + for (m = 0; m < 3; ++m) { + ir_value *vm = v->members[m]; + if (!vm) + continue; + oprintf("%s\t%s: @%i ", ind, vm->name, (int)vm->code.local); + for (l = 0; l < vec_size(vm->life); ++l) { + oprintf("[%i,%i] ", vm->life[l].start, vm->life[l].end); + } + oprintf("\n"); + } + } + for (i = 0; i < vec_size(f->values); ++i) { + const char *attr = ""; + size_t l, m; + ir_value *v = f->values[i]; + if (v->unique_life && v->locked) + attr = "unique,locked "; + else if (v->unique_life) + attr = "unique "; + else if (v->locked) + attr = "locked "; + oprintf("%s\t%s: %s %s %s%s@%i ", ind, v->name, type_name[v->vtype], + storenames[v->store], + attr, (v->callparam ? "callparam " : ""), + (int)v->code.local); + if (!v->life) + oprintf("[null]"); + for (l = 0; l < vec_size(v->life); ++l) { + oprintf("[%i,%i] ", v->life[l].start, v->life[l].end); + } + oprintf("\n"); + for (m = 0; m < 3; ++m) { + ir_value *vm = v->members[m]; + if (!vm) + continue; + if (vm->unique_life && vm->locked) + attr = "unique,locked "; + else if (vm->unique_life) + attr = "unique "; + else if (vm->locked) + attr = "locked "; + oprintf("%s\t%s: %s@%i ", ind, vm->name, attr, (int)vm->code.local); + for (l = 0; l < vec_size(vm->life); ++l) { + oprintf("[%i,%i] ", vm->life[l].start, vm->life[l].end); + } + oprintf("\n"); + } + } + if (vec_size(f->blocks)) + { + oprintf("%slife passes: %i\n", ind, (int)f->run_id); + for (i = 0; i < vec_size(f->blocks); ++i) { + ir_block_dump(f->blocks[i], ind, oprintf); + } + + } + ind[strlen(ind)-1] = 0; + oprintf("%sendfunction %s\n", ind, f->name); +} + +void ir_block_dump(ir_block* b, char *ind, + int (*oprintf)(const char*, ...)) +{ + size_t i; + oprintf("%s:%s\n", ind, b->label); + strncat(ind, "\t", IND_BUFSZ); + + if (b->instr && b->instr[0]) + oprintf("%s (%i) [entry]\n", ind, (int)(b->instr[0]->eid-1)); + for (i = 0; i < vec_size(b->instr); ++i) + ir_instr_dump(b->instr[i], ind, oprintf); + ind[strlen(ind)-1] = 0; +} + +static void dump_phi(ir_instr *in, int (*oprintf)(const char*, ...)) +{ + size_t i; + oprintf("%s <- phi ", in->_ops[0]->name); + for (i = 0; i < vec_size(in->phi); ++i) + { + oprintf("([%s] : %s) ", in->phi[i].from->label, + in->phi[i].value->name); + } + oprintf("\n"); +} + +void ir_instr_dump(ir_instr *in, char *ind, + int (*oprintf)(const char*, ...)) +{ + size_t i; + const char *comma = NULL; + + oprintf("%s (%i) ", ind, (int)in->eid); + + if (in->opcode == VINSTR_PHI) { + dump_phi(in, oprintf); + return; + } + + strncat(ind, "\t", IND_BUFSZ); + + if (in->_ops[0] && (in->_ops[1] || in->_ops[2])) { + ir_value_dump(in->_ops[0], oprintf); + if (in->_ops[1] || in->_ops[2]) + oprintf(" <- "); + } + if (in->opcode == INSTR_CALL0 || in->opcode == VINSTR_NRCALL) { + oprintf("CALL%i\t", vec_size(in->params)); + } else + oprintf("%s\t", qc_opname(in->opcode)); + + if (in->_ops[0] && !(in->_ops[1] || in->_ops[2])) { + ir_value_dump(in->_ops[0], oprintf); + comma = ",\t"; + } + else + { + for (i = 1; i != 3; ++i) { + if (in->_ops[i]) { + if (comma) + oprintf(comma); + ir_value_dump(in->_ops[i], oprintf); + comma = ",\t"; + } + } + } + if (in->bops[0]) { + if (comma) + oprintf(comma); + oprintf("[%s]", in->bops[0]->label); + comma = ",\t"; + } + if (in->bops[1]) + oprintf("%s[%s]", comma, in->bops[1]->label); + if (vec_size(in->params)) { + oprintf("\tparams: "); + for (i = 0; i != vec_size(in->params); ++i) { + oprintf("%s, ", in->params[i]->name); + } + } + oprintf("\n"); + ind[strlen(ind)-1] = 0; +} + +static void ir_value_dump_string(const char *str, int (*oprintf)(const char*, ...)) +{ + oprintf("\""); + for (; *str; ++str) { + switch (*str) { + case '\n': oprintf("\\n"); break; + case '\r': oprintf("\\r"); break; + case '\t': oprintf("\\t"); break; + case '\v': oprintf("\\v"); break; + case '\f': oprintf("\\f"); break; + case '\b': oprintf("\\b"); break; + case '\a': oprintf("\\a"); break; + case '\\': oprintf("\\\\"); break; + case '"': oprintf("\\\""); break; + default: oprintf("%c", *str); break; + } + } + oprintf("\""); +} + +void ir_value_dump(ir_value* v, int (*oprintf)(const char*, ...)) +{ + if (v->hasvalue) { + switch (v->vtype) { + default: + case TYPE_VOID: + oprintf("(void)"); + break; + case TYPE_FUNCTION: + oprintf("fn:%s", v->name); + break; + case TYPE_FLOAT: + oprintf("%g", v->constval.vfloat); + break; + case TYPE_VECTOR: + oprintf("'%g %g %g'", + v->constval.vvec.x, + v->constval.vvec.y, + v->constval.vvec.z); + break; + case TYPE_ENTITY: + oprintf("(entity)"); + break; + case TYPE_STRING: + ir_value_dump_string(v->constval.vstring, oprintf); + break; +#if 0 + case TYPE_INTEGER: + oprintf("%i", v->constval.vint); + break; +#endif + case TYPE_POINTER: + oprintf("&%s", + v->constval.vpointer->name); + break; + } + } else { + oprintf("%s", v->name); + } +} + +void ir_value_dump_life(const ir_value *self, int (*oprintf)(const char*,...)) +{ + size_t i; + oprintf("Life of %12s:", self->name); + for (i = 0; i < vec_size(self->life); ++i) + { + oprintf(" + [%i, %i]\n", self->life[i].start, self->life[i].end); + } +} diff --git a/misc/source/gmqcc-src/ir.h b/misc/source/gmqcc-src/ir.h new file mode 100644 index 00000000..cd382957 --- /dev/null +++ b/misc/source/gmqcc-src/ir.h @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2012, 2013 + * Wolfgang Bumiller + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef GMQCC_IR_HDR +#define GMQCC_IR_HDR +#include "gmqcc.h" + +typedef struct +{ + /* both inclusive */ + size_t start; + size_t end; +} ir_life_entry_t; + +struct ir_function_s; +typedef struct ir_value_s { + char *name; + int vtype; + int store; + lex_ctx context; + /* even the IR knows the subtype of a field */ + int fieldtype; + /* and the output type of a function */ + int outtype; + /* 'const' vs 'var' qualifier */ + int cvq; + uint32_t flags; + + struct ir_instr_s **reads; + struct ir_instr_s **writes; + + /* constantvalues */ + bool hasvalue; + union { + float vfloat; + int vint; + vector vvec; + int32_t ivec[3]; + char *vstring; + struct ir_value_s *vpointer; + struct ir_function_s *vfunc; + } constval; + + struct { + int32_t globaladdr; + int32_t name; + /* filled by the local-allocator */ + int32_t local; + /* added for members */ + int32_t addroffset; + /* to generate field-addresses early */ + int32_t fieldaddr; + } code; + + /* for acessing vectors */ + struct ir_value_s *members[3]; + struct ir_value_s *memberof; + + /* arrays will never overlap with temps */ + bool unique_life; + /* temps living during a CALL must be locked */ + bool locked; + bool callparam; + + /* For the temp allocator */ + ir_life_entry_t *life; +} ir_value; + +/* ir_value can be a variable, or created by an operation */ +/* if a result of an operation: the function should store + * it to remember to delete it / garbage collect it + */ +void ir_value_delete(ir_value*); +ir_value* ir_value_vector_member(ir_value*, unsigned int member); +bool GMQCC_WARN ir_value_set_float(ir_value*, float f); +bool GMQCC_WARN ir_value_set_func(ir_value*, int f); +bool GMQCC_WARN ir_value_set_string(ir_value*, const char *s); +bool GMQCC_WARN ir_value_set_vector(ir_value*, vector v); +bool GMQCC_WARN ir_value_set_field(ir_value*, ir_value *fld); +bool ir_value_lives(ir_value*, size_t); +void ir_value_dump_life(const ir_value *self, int (*oprintf)(const char*,...)); + +/* PHI data */ +typedef struct ir_phi_entry_s +{ + ir_value *value; + struct ir_block_s *from; +} ir_phi_entry_t; + +/* instruction */ +typedef struct ir_instr_s +{ + int opcode; + lex_ctx context; + ir_value* (_ops[3]); + struct ir_block_s* (bops[2]); + + ir_phi_entry_t *phi; + ir_value **params; + + /* For the temp-allocation */ + size_t eid; + + /* For IFs */ + bool likely; + + struct ir_block_s *owner; +} ir_instr; + +/* block */ +typedef struct ir_block_s +{ + char *label; + lex_ctx context; + bool final; /* once a jump is added we're done */ + + ir_instr **instr; + struct ir_block_s **entries; + struct ir_block_s **exits; + ir_value **living; + + /* For the temp-allocation */ + size_t entry_id; + size_t eid; + bool is_return; + + struct ir_function_s *owner; + + bool generated; + size_t code_start; +} ir_block; + +ir_value* ir_block_create_binop(ir_block*, lex_ctx, const char *label, int op, ir_value *left, ir_value *right); +ir_value* ir_block_create_unary(ir_block*, lex_ctx, const char *label, int op, ir_value *operand); +bool GMQCC_WARN ir_block_create_store_op(ir_block*, lex_ctx, int op, ir_value *target, ir_value *what); +bool GMQCC_WARN ir_block_create_storep(ir_block*, lex_ctx, ir_value *target, ir_value *what); +ir_value* ir_block_create_load_from_ent(ir_block*, lex_ctx, const char *label, ir_value *ent, ir_value *field, int outype); +ir_value* ir_block_create_fieldaddress(ir_block*, lex_ctx, const char *label, ir_value *entity, ir_value *field); + +/* This is to create an instruction of the form + * %label := opcode a, b + */ +ir_instr* ir_block_create_phi(ir_block*, lex_ctx, const char *label, int vtype); +ir_value* ir_phi_value(ir_instr*); +void ir_phi_add(ir_instr*, ir_block *b, ir_value *v); +ir_instr* ir_block_create_call(ir_block*, lex_ctx, const char *label, ir_value *func, bool noreturn); +ir_value* ir_call_value(ir_instr*); +void ir_call_param(ir_instr*, ir_value*); + +bool GMQCC_WARN ir_block_create_return(ir_block*, lex_ctx, ir_value *opt_value); + +bool GMQCC_WARN ir_block_create_if(ir_block*, lex_ctx, ir_value *cond, + ir_block *ontrue, ir_block *onfalse); +/* A 'goto' is an actual 'goto' coded in QC, whereas + * a 'jump' is a virtual construct which simply names the + * next block to go to. + * A goto usually becomes an OP_GOTO in the resulting code, + * whereas a 'jump' usually doesn't add any actual instruction. + */ +bool GMQCC_WARN ir_block_create_jump(ir_block*, lex_ctx, ir_block *to); +bool GMQCC_WARN ir_block_create_goto(ir_block*, lex_ctx, ir_block *to); + +/* function */ +typedef struct ir_function_s +{ + char *name; + int outtype; + int *params; + ir_block **blocks; + + uint32_t flags; + + int builtin; + + ir_value *value; + + /* values generated from operations + * which might get optimized away, so anything + * in there needs to be deleted in the dtor. + */ + ir_value **values; + + /* locally defined variables */ + ir_value **locals; + + size_t allocated_locals; + size_t globaltemps; + + ir_block* first; + ir_block* last; + + lex_ctx context; + + /* for prototypes - first we generate all the + * globals, and we remember teh function-defs + * so we can later fill in the entry pos + * + * remember the ID: + */ + qcint code_function_def; + + /* for temp allocation */ + size_t run_id; + + struct ir_builder_s *owner; + + /* vararg support: */ + size_t max_varargs; +} ir_function; + +#define IR_FLAG_HAS_ARRAYS (1<<1) +#define IR_FLAG_HAS_UNINITIALIZED (1<<2) +#define IR_FLAG_HAS_GOTO (1<<3) +#define IR_FLAG_INCLUDE_DEF (1<<4) +#define IR_FLAG_MASK_NO_OVERLAP (IR_FLAG_HAS_ARRAYS | IR_FLAG_HAS_UNINITIALIZED) +#define IR_FLAG_MASK_NO_LOCAL_TEMPS (IR_FLAG_HAS_ARRAYS | IR_FLAG_HAS_UNINITIALIZED) + +ir_value* ir_function_create_local(ir_function *self, const char *name, int vtype, bool param); +bool GMQCC_WARN ir_function_finalize(ir_function*); +ir_block* ir_function_create_block(lex_ctx ctx, ir_function*, const char *label); + +/* builder */ +#define IR_HT_SIZE 1024 +typedef struct ir_builder_s +{ + char *name; + ir_function **functions; + ir_value **globals; + ir_value **fields; + + ht htfunctions; + ht htglobals; + ht htfields; + + ir_value **extparams; + ir_value **extparam_protos; + + /* the highest func->allocated_locals */ + size_t max_locals; + size_t max_globaltemps; + uint32_t first_common_local; + uint32_t first_common_globaltemp; + + const char **filenames; + qcint *filestrings; + /* we cache the #IMMEDIATE string here */ + qcint str_immediate; + /* there should just be this one nil */ + ir_value *nil; + ir_value *reserved_va_count; +} ir_builder; + +ir_builder* ir_builder_new(const char *modulename); +void ir_builder_delete(ir_builder*); +ir_function* ir_builder_create_function(ir_builder*, const char *name, int outtype); +ir_value* ir_builder_create_global(ir_builder*, const char *name, int vtype); +ir_value* ir_builder_create_field(ir_builder*, const char *name, int vtype); +ir_value* ir_builder_get_va_count(ir_builder*); +bool ir_builder_generate(code_t *, ir_builder *self, const char *filename); +void ir_builder_dump(ir_builder*, int (*oprintf)(const char*, ...)); + +/* + * This code assumes 32 bit floats while generating binary + * Blub: don't use extern here, it's annoying and shows up in nm + * for some reason :P + */ +typedef int static_assert_is_32bit_float [(sizeof(int32_t) == 4)?1:-1]; +typedef int static_assert_is_32bit_integer[(sizeof(qcfloat) == 4)?1:-1]; + +#endif diff --git a/misc/source/gmqcc-src/lexer.c b/misc/source/gmqcc-src/lexer.c new file mode 100644 index 00000000..8289057e --- /dev/null +++ b/misc/source/gmqcc-src/lexer.c @@ -0,0 +1,1522 @@ +/* + * Copyright (C) 2012, 2013 + * Wolfgang Bumiller + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include +#include +#include + +#include "gmqcc.h" +#include "lexer.h" +/* + * List of Keywords + */ + +/* original */ +static const char *keywords_qc[] = { + "for", "do", "while", + "if", "else", + "local", + "return", + "const" +}; +static size_t num_keywords_qc = sizeof(keywords_qc) / sizeof(keywords_qc[0]); + +/* For fte/gmgqcc */ +static const char *keywords_fg[] = { + "switch", "case", "default", + "struct", "union", + "break", "continue", + "typedef", + "goto", + + "__builtin_debug_printtype" +}; +static size_t num_keywords_fg = sizeof(keywords_fg) / sizeof(keywords_fg[0]); + +/* + * Lexer code + */ + +static char* *lex_filenames; + +static void lexerror(lex_file *lex, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (lex) + con_vprintmsg(LVL_ERROR, lex->name, lex->sline, lex->column, "parse error", fmt, ap); + else + con_vprintmsg(LVL_ERROR, "", 0, 0, "parse error", fmt, ap); + va_end(ap); +} + +static bool lexwarn(lex_file *lex, int warntype, const char *fmt, ...) +{ + bool r; + lex_ctx ctx; + va_list ap; + + ctx.file = lex->name; + ctx.line = lex->sline; + + va_start(ap, fmt); + r = vcompile_warning(ctx, warntype, fmt, ap); + va_end(ap); + return r; +} + + +#if 0 +token* token_new() +{ + token *tok = (token*)mem_a(sizeof(token)); + if (!tok) + return NULL; + memset(tok, 0, sizeof(*tok)); + return tok; +} + +void token_delete(token *self) +{ + if (self->next && self->next->prev == self) + self->next->prev = self->prev; + if (self->prev && self->prev->next == self) + self->prev->next = self->next; + MEM_VECTOR_CLEAR(self, value); + mem_d(self); +} + +token* token_copy(const token *cp) +{ + token* self = token_new(); + if (!self) + return NULL; + /* copy the value */ + self->value_alloc = cp->value_count + 1; + self->value_count = cp->value_count; + self->value = (char*)mem_a(self->value_alloc); + if (!self->value) { + mem_d(self); + return NULL; + } + memcpy(self->value, cp->value, cp->value_count); + self->value[self->value_alloc-1] = 0; + + /* rest */ + self->ctx = cp->ctx; + self->ttype = cp->ttype; + memcpy(&self->constval, &cp->constval, sizeof(self->constval)); + return self; +} + +void token_delete_all(token *t) +{ + token *n; + + do { + n = t->next; + token_delete(t); + t = n; + } while(t); +} + +token* token_copy_all(const token *cp) +{ + token *cur; + token *out; + + out = cur = token_copy(cp); + if (!out) + return NULL; + + while (cp->next) { + cp = cp->next; + cur->next = token_copy(cp); + if (!cur->next) { + token_delete_all(out); + return NULL; + } + cur->next->prev = cur; + cur = cur->next; + } + + return out; +} +#else +static void lex_token_new(lex_file *lex) +{ +#if 0 + if (lex->tok) + token_delete(lex->tok); + lex->tok = token_new(); +#else + if (lex->tok.value) + vec_shrinkto(lex->tok.value, 0); + + lex->tok.constval.t = 0; + lex->tok.ctx.line = lex->sline; + lex->tok.ctx.file = lex->name; + lex->tok.ctx.column = lex->column; +#endif +} +#endif + +lex_file* lex_open(const char *file) +{ + lex_file *lex; + FILE *in = fs_file_open(file, "rb"); + + if (!in) { + lexerror(NULL, "open failed: '%s'\n", file); + return NULL; + } + + lex = (lex_file*)mem_a(sizeof(*lex)); + if (!lex) { + fs_file_close(in); + lexerror(NULL, "out of memory\n"); + return NULL; + } + + memset(lex, 0, sizeof(*lex)); + + lex->file = in; + lex->name = util_strdup(file); + lex->line = 1; /* we start counting at 1 */ + lex->column = 0; + lex->peekpos = 0; + lex->eof = false; + + vec_push(lex_filenames, lex->name); + return lex; +} + +lex_file* lex_open_string(const char *str, size_t len, const char *name) +{ + lex_file *lex; + + lex = (lex_file*)mem_a(sizeof(*lex)); + if (!lex) { + lexerror(NULL, "out of memory\n"); + return NULL; + } + + memset(lex, 0, sizeof(*lex)); + + lex->file = NULL; + lex->open_string = str; + lex->open_string_length = len; + lex->open_string_pos = 0; + + lex->name = util_strdup(name ? name : ""); + lex->line = 1; /* we start counting at 1 */ + lex->peekpos = 0; + lex->eof = false; + lex->column = 0; + + vec_push(lex_filenames, lex->name); + + return lex; +} + +void lex_cleanup(void) +{ + size_t i; + for (i = 0; i < vec_size(lex_filenames); ++i) + mem_d(lex_filenames[i]); + vec_free(lex_filenames); +} + +void lex_close(lex_file *lex) +{ + size_t i; + for (i = 0; i < vec_size(lex->frames); ++i) + mem_d(lex->frames[i].name); + vec_free(lex->frames); + + if (lex->modelname) + vec_free(lex->modelname); + + if (lex->file) + fs_file_close(lex->file); +#if 0 + if (lex->tok) + token_delete(lex->tok); +#else + vec_free(lex->tok.value); +#endif + /* mem_d(lex->name); collected in lex_filenames */ + mem_d(lex); +} + +static int lex_fgetc(lex_file *lex) +{ + if (lex->file) { + lex->column++; + return fs_file_getc(lex->file); + } + if (lex->open_string) { + if (lex->open_string_pos >= lex->open_string_length) + return EOF; + lex->column++; + return lex->open_string[lex->open_string_pos++]; + } + return EOF; +} + +/* Get or put-back data + * The following to functions do NOT understand what kind of data they + * are working on. + * The are merely wrapping get/put in order to count line numbers. + */ +static void lex_ungetch(lex_file *lex, int ch); +static int lex_try_trigraph(lex_file *lex, int old) +{ + int c2, c3; + c2 = lex_fgetc(lex); + if (!lex->push_line && c2 == '\n') { + lex->line++; + lex->column = 0; + } + + if (c2 != '?') { + lex_ungetch(lex, c2); + return old; + } + + c3 = lex_fgetc(lex); + if (!lex->push_line && c3 == '\n') { + lex->line++; + lex->column = 0; + } + + switch (c3) { + case '=': return '#'; + case '/': return '\\'; + case '\'': return '^'; + case '(': return '['; + case ')': return ']'; + case '!': return '|'; + case '<': return '{'; + case '>': return '}'; + case '-': return '~'; + default: + lex_ungetch(lex, c3); + lex_ungetch(lex, c2); + return old; + } +} + +static int lex_try_digraph(lex_file *lex, int ch) +{ + int c2; + c2 = lex_fgetc(lex); + /* we just used fgetc() so count lines + * need to offset a \n the ungetch would recognize + */ + if (!lex->push_line && c2 == '\n') + lex->line++; + if (ch == '<' && c2 == ':') + return '['; + else if (ch == ':' && c2 == '>') + return ']'; + else if (ch == '<' && c2 == '%') + return '{'; + else if (ch == '%' && c2 == '>') + return '}'; + else if (ch == '%' && c2 == ':') + return '#'; + lex_ungetch(lex, c2); + return ch; +} + +static int lex_getch(lex_file *lex) +{ + int ch; + + if (lex->peekpos) { + lex->peekpos--; + if (!lex->push_line && lex->peek[lex->peekpos] == '\n') + lex->line++; + return lex->peek[lex->peekpos]; + } + + ch = lex_fgetc(lex); + if (!lex->push_line && ch == '\n') + lex->line++; + else if (ch == '?') + return lex_try_trigraph(lex, ch); + else if (!lex->flags.nodigraphs && (ch == '<' || ch == ':' || ch == '%')) + return lex_try_digraph(lex, ch); + return ch; +} + +static void lex_ungetch(lex_file *lex, int ch) +{ + lex->peek[lex->peekpos++] = ch; + lex->column--; + if (!lex->push_line && ch == '\n') { + lex->line--; + lex->column = 0; + } +} + +/* classify characters + * some additions to the is*() functions of ctype.h + */ + +/* Idents are alphanumberic, but they start with alpha or _ */ +static bool isident_start(int ch) +{ + return isalpha(ch) || ch == '_'; +} + +static bool isident(int ch) +{ + return isident_start(ch) || isdigit(ch); +} + +/* isxdigit_only is used when we already know it's not a digit + * and want to see if it's a hex digit anyway. + */ +static bool isxdigit_only(int ch) +{ + return (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F'); +} + +/* Append a character to the token buffer */ +static void lex_tokench(lex_file *lex, int ch) +{ + vec_push(lex->tok.value, ch); +} + +/* Append a trailing null-byte */ +static void lex_endtoken(lex_file *lex) +{ + vec_push(lex->tok.value, 0); + vec_shrinkby(lex->tok.value, 1); +} + +static bool lex_try_pragma(lex_file *lex) +{ + int ch; + char *pragma = NULL; + char *command = NULL; + char *param = NULL; + size_t line; + + if (lex->flags.preprocessing) + return false; + + line = lex->line; + + ch = lex_getch(lex); + if (ch != '#') { + lex_ungetch(lex, ch); + return false; + } + + for (ch = lex_getch(lex); vec_size(pragma) < 8 && ch >= 'a' && ch <= 'z'; ch = lex_getch(lex)) + vec_push(pragma, ch); + vec_push(pragma, 0); + + if (ch != ' ' || strcmp(pragma, "pragma")) { + lex_ungetch(lex, ch); + goto unroll; + } + + for (ch = lex_getch(lex); vec_size(command) < 32 && ch >= 'a' && ch <= 'z'; ch = lex_getch(lex)) + vec_push(command, ch); + vec_push(command, 0); + + if (ch != '(') { + lex_ungetch(lex, ch); + goto unroll; + } + + for (ch = lex_getch(lex); vec_size(param) < 1024 && ch != ')' && ch != '\n'; ch = lex_getch(lex)) + vec_push(param, ch); + vec_push(param, 0); + + if (ch != ')') { + lex_ungetch(lex, ch); + goto unroll; + } + + if (!strcmp(command, "push")) { + if (!strcmp(param, "line")) { + lex->push_line++; + if (lex->push_line == 1) + --line; + } + else + goto unroll; + } + else if (!strcmp(command, "pop")) { + if (!strcmp(param, "line")) { + if (lex->push_line) + lex->push_line--; + if (lex->push_line == 0) + --line; + } + else + goto unroll; + } + else if (!strcmp(command, "file")) { + lex->name = util_strdup(param); + vec_push(lex_filenames, lex->name); + } + else if (!strcmp(command, "line")) { + line = strtol(param, NULL, 0)-1; + } + else + goto unroll; + + lex->line = line; + while (ch != '\n' && ch != EOF) + ch = lex_getch(lex); + vec_free(command); + vec_free(param); + vec_free(pragma); + return true; + +unroll: + if (command) { + vec_pop(command); + while (vec_size(command)) { + lex_ungetch(lex, (unsigned char)vec_last(command)); + vec_pop(command); + } + vec_free(command); + lex_ungetch(lex, ' '); + } + if (param) { + vec_pop(param); + while (vec_size(param)) { + lex_ungetch(lex, (unsigned char)vec_last(param)); + vec_pop(param); + } + vec_free(param); + lex_ungetch(lex, ' '); + } + if (pragma) { + vec_pop(pragma); + while (vec_size(pragma)) { + lex_ungetch(lex, (unsigned char)vec_last(pragma)); + vec_pop(pragma); + } + vec_free(pragma); + } + lex_ungetch(lex, '#'); + + lex->line = line; + return false; +} + +/* Skip whitespace and comments and return the first + * non-white character. + * As this makes use of the above getch() ungetch() functions, + * we don't need to care at all about line numbering anymore. + * + * In theory, this function should only be used at the beginning + * of lexing, or when we *know* the next character is part of the token. + * Otherwise, if the parser throws an error, the linenumber may not be + * the line of the error, but the line of the next token AFTER the error. + * + * This is currently only problematic when using c-like string-continuation, + * since comments and whitespaces are allowed between 2 such strings. + * Example: +printf( "line one\n" +// A comment + "A continuation of the previous string" +// This line is skipped + , foo); + + * In this case, if the parse decides it didn't actually want a string, + * and uses lex->line to print an error, it will show the ', foo);' line's + * linenumber. + * + * On the other hand, the parser is supposed to remember the line of the next + * token's beginning. In this case we would want skipwhite() to be called + * AFTER reading a token, so that the parser, before reading the NEXT token, + * doesn't store teh *comment's* linenumber, but the actual token's linenumber. + * + * THIS SOLUTION + * here is to store the line of the first character after skipping + * the initial whitespace in lex->sline, this happens in lex_do. + */ +static int lex_skipwhite(lex_file *lex, bool hadwhite) +{ + int ch = 0; + bool haswhite = hadwhite; + + do + { + ch = lex_getch(lex); + while (ch != EOF && isspace(ch)) { + if (ch == '\n') { + if (lex_try_pragma(lex)) + continue; + } + if (lex->flags.preprocessing) { + if (ch == '\n') { + /* end-of-line */ + /* see if there was whitespace first */ + if (haswhite) { /* (vec_size(lex->tok.value)) { */ + lex_ungetch(lex, ch); + lex_endtoken(lex); + return TOKEN_WHITE; + } + /* otherwise return EOL */ + return TOKEN_EOL; + } + haswhite = true; + lex_tokench(lex, ch); + } + ch = lex_getch(lex); + } + + if (ch == '/') { + ch = lex_getch(lex); + if (ch == '/') + { + /* one line comment */ + ch = lex_getch(lex); + + if (lex->flags.preprocessing) { + haswhite = true; + /* + lex_tokench(lex, '/'); + lex_tokench(lex, '/'); + */ + lex_tokench(lex, ' '); + lex_tokench(lex, ' '); + } + + while (ch != EOF && ch != '\n') { + if (lex->flags.preprocessing) + lex_tokench(lex, ' '); /* ch); */ + ch = lex_getch(lex); + } + if (lex->flags.preprocessing) { + lex_ungetch(lex, '\n'); + lex_endtoken(lex); + return TOKEN_WHITE; + } + continue; + } + if (ch == '*') + { + /* multiline comment */ + if (lex->flags.preprocessing) { + haswhite = true; + /* + lex_tokench(lex, '/'); + lex_tokench(lex, '*'); + */ + lex_tokench(lex, ' '); + lex_tokench(lex, ' '); + } + + while (ch != EOF) + { + ch = lex_getch(lex); + if (ch == '*') { + ch = lex_getch(lex); + if (ch == '/') { + if (lex->flags.preprocessing) { + /* + lex_tokench(lex, '*'); + lex_tokench(lex, '/'); + */ + lex_tokench(lex, ' '); + lex_tokench(lex, ' '); + } + break; + } + lex_ungetch(lex, ch); + } + if (lex->flags.preprocessing) { + if (ch == '\n') + lex_tokench(lex, '\n'); + else + lex_tokench(lex, ' '); /* ch); */ + } + } + ch = ' '; /* cause TRUE in the isspace check */ + continue; + } + /* Otherwise roll back to the slash and break out of the loop */ + lex_ungetch(lex, ch); + ch = '/'; + break; + } + } while (ch != EOF && isspace(ch)); + + if (haswhite) { + lex_endtoken(lex); + lex_ungetch(lex, ch); + return TOKEN_WHITE; + } + return ch; +} + +/* Get a token */ +static bool GMQCC_WARN lex_finish_ident(lex_file *lex) +{ + int ch; + + ch = lex_getch(lex); + while (ch != EOF && isident(ch)) + { + lex_tokench(lex, ch); + ch = lex_getch(lex); + } + + /* last ch was not an ident ch: */ + lex_ungetch(lex, ch); + + return true; +} + +/* read one ident for the frame list */ +static int lex_parse_frame(lex_file *lex) +{ + int ch; + + lex_token_new(lex); + + ch = lex_getch(lex); + while (ch != EOF && ch != '\n' && isspace(ch)) + ch = lex_getch(lex); + + if (ch == '\n') + return 1; + + if (!isident_start(ch)) { + lexerror(lex, "invalid framename, must start with one of a-z or _, got %c", ch); + return -1; + } + + lex_tokench(lex, ch); + if (!lex_finish_ident(lex)) + return -1; + lex_endtoken(lex); + return 0; +} + +/* read a list of $frames */ +static bool lex_finish_frames(lex_file *lex) +{ + do { + size_t i; + int rc; + frame_macro m; + + rc = lex_parse_frame(lex); + if (rc > 0) /* end of line */ + return true; + if (rc < 0) /* error */ + return false; + + for (i = 0; i < vec_size(lex->frames); ++i) { + if (!strcmp(lex->tok.value, lex->frames[i].name)) { + lex->frames[i].value = lex->framevalue++; + if (lexwarn(lex, WARN_FRAME_MACROS, "duplicate frame macro defined: `%s`", lex->tok.value)) + return false; + break; + } + } + if (i < vec_size(lex->frames)) + continue; + + m.value = lex->framevalue++; + m.name = util_strdup(lex->tok.value); + vec_shrinkto(lex->tok.value, 0); + vec_push(lex->frames, m); + } while (true); + + return false; +} + +static int GMQCC_WARN lex_finish_string(lex_file *lex, int quote) +{ + uchar_t chr; + int ch = 0; + int nextch; + bool hex; + char u8buf[8]; /* way more than enough */ + int u8len, uc; + + while (ch != EOF) + { + ch = lex_getch(lex); + if (ch == quote) + return TOKEN_STRINGCONST; + + if (lex->flags.preprocessing && ch == '\\') { + lex_tokench(lex, ch); + ch = lex_getch(lex); + if (ch == EOF) { + lexerror(lex, "unexpected end of file"); + lex_ungetch(lex, EOF); /* next token to be TOKEN_EOF */ + return (lex->tok.ttype = TOKEN_ERROR); + } + lex_tokench(lex, ch); + } + else if (ch == '\\') { + ch = lex_getch(lex); + if (ch == EOF) { + lexerror(lex, "unexpected end of file"); + lex_ungetch(lex, EOF); /* next token to be TOKEN_EOF */ + return (lex->tok.ttype = TOKEN_ERROR); + } + + switch (ch) { + case '\\': break; + case '\'': break; + case '"': break; + case 'a': ch = '\a'; break; + case 'b': ch = '\b'; break; + case 'r': ch = '\r'; break; + case 'n': ch = '\n'; break; + case 't': ch = '\t'; break; + case 'f': ch = '\f'; break; + case 'v': ch = '\v'; break; + case 'x': + case 'X': + /* same procedure as in fteqcc */ + ch = 0; + nextch = lex_getch(lex); + if (nextch >= '0' && nextch <= '9') + ch += nextch - '0'; + else if (nextch >= 'a' && nextch <= 'f') + ch += nextch - 'a' + 10; + else if (nextch >= 'A' && nextch <= 'F') + ch += nextch - 'A' + 10; + else { + lexerror(lex, "bad character code"); + lex_ungetch(lex, nextch); + return (lex->tok.ttype = TOKEN_ERROR); + } + + ch *= 0x10; + nextch = lex_getch(lex); + if (nextch >= '0' && nextch <= '9') + ch += nextch - '0'; + else if (nextch >= 'a' && nextch <= 'f') + ch += nextch - 'a' + 10; + else if (nextch >= 'A' && nextch <= 'F') + ch += nextch - 'A' + 10; + else { + lexerror(lex, "bad character code"); + lex_ungetch(lex, nextch); + return (lex->tok.ttype = TOKEN_ERROR); + } + break; + + /* fteqcc support */ + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + case '8': case '9': + ch = 18 + ch - '0'; + break; + case '<': ch = 29; break; + case '-': ch = 30; break; + case '>': ch = 31; break; + case '[': ch = 16; break; + case ']': ch = 17; break; + case '{': + chr = 0; + nextch = lex_getch(lex); + hex = (nextch == 'x'); + if (!hex) + lex_ungetch(lex, nextch); + for (nextch = lex_getch(lex); nextch != '}'; nextch = lex_getch(lex)) { + if (!hex) { + if (nextch >= '0' && nextch <= '9') + chr = chr * 10 + nextch - '0'; + else { + lexerror(lex, "bad character code"); + return (lex->tok.ttype = TOKEN_ERROR); + } + } else { + if (nextch >= '0' && nextch <= '9') + chr = chr * 0x10 + nextch - '0'; + else if (nextch >= 'a' && nextch <= 'f') + chr = chr * 0x10 + nextch - 'a' + 10; + else if (nextch >= 'A' && nextch <= 'F') + chr = chr * 0x10 + nextch - 'A' + 10; + else { + lexerror(lex, "bad character code"); + return (lex->tok.ttype = TOKEN_ERROR); + } + } + if (chr > 0x10FFFF || (!OPTS_FLAG(UTF8) && chr > 255)) + { + lexerror(lex, "character code out of range"); + return (lex->tok.ttype = TOKEN_ERROR); + } + } + if (OPTS_FLAG(UTF8) && chr >= 128) { + u8len = u8_fromchar(chr, u8buf, sizeof(u8buf)); + if (!u8len) + ch = 0; + else { + --u8len; + lex->column += u8len; + for (uc = 0; uc < u8len; ++uc) + lex_tokench(lex, u8buf[uc]); + /* the last character will be inserted with the tokench() call + * below the switch + */ + ch = u8buf[uc]; + } + } + else + ch = chr; + break; + case '\n': ch = '\n'; break; + + default: + lexwarn(lex, WARN_UNKNOWN_CONTROL_SEQUENCE, "unrecognized control sequence: \\%c", ch); + /* so we just add the character plus backslash no matter what it actually is */ + lex_tokench(lex, '\\'); + } + /* add the character finally */ + lex_tokench(lex, ch); + } + else + lex_tokench(lex, ch); + } + lexerror(lex, "unexpected end of file within string constant"); + lex_ungetch(lex, EOF); /* next token to be TOKEN_EOF */ + return (lex->tok.ttype = TOKEN_ERROR); +} + +static int GMQCC_WARN lex_finish_digit(lex_file *lex, int lastch) +{ + bool ishex = false; + + int ch = lastch; + + /* parse a number... */ + if (ch == '.') + lex->tok.ttype = TOKEN_FLOATCONST; + else + lex->tok.ttype = TOKEN_INTCONST; + + lex_tokench(lex, ch); + + ch = lex_getch(lex); + if (ch != '.' && !isdigit(ch)) + { + if (lastch != '0' || ch != 'x') + { + /* end of the number or EOF */ + lex_ungetch(lex, ch); + lex_endtoken(lex); + + lex->tok.constval.i = lastch - '0'; + return lex->tok.ttype; + } + + ishex = true; + } + + /* EOF would have been caught above */ + + if (ch != '.') + { + lex_tokench(lex, ch); + ch = lex_getch(lex); + while (isdigit(ch) || (ishex && isxdigit_only(ch))) + { + lex_tokench(lex, ch); + ch = lex_getch(lex); + } + } + /* NOT else, '.' can come from above as well */ + if (lex->tok.ttype != TOKEN_FLOATCONST && ch == '.' && !ishex) + { + /* Allow floating comma in non-hex mode */ + lex->tok.ttype = TOKEN_FLOATCONST; + lex_tokench(lex, ch); + + /* continue digits-only */ + ch = lex_getch(lex); + while (isdigit(ch)) + { + lex_tokench(lex, ch); + ch = lex_getch(lex); + } + } + /* put back the last character */ + /* but do not put back the trailing 'f' or a float */ + if (lex->tok.ttype == TOKEN_FLOATCONST && ch == 'f') + ch = lex_getch(lex); + + /* generally we don't want words to follow numbers: */ + if (isident(ch)) { + lexerror(lex, "unexpected trailing characters after number"); + return (lex->tok.ttype = TOKEN_ERROR); + } + lex_ungetch(lex, ch); + + lex_endtoken(lex); + if (lex->tok.ttype == TOKEN_FLOATCONST) + lex->tok.constval.f = strtod(lex->tok.value, NULL); + else + lex->tok.constval.i = strtol(lex->tok.value, NULL, 0); + return lex->tok.ttype; +} + +int lex_do(lex_file *lex) +{ + int ch, nextch, thirdch; + bool hadwhite = false; + + lex_token_new(lex); +#if 0 + if (!lex->tok) + return TOKEN_FATAL; +#endif + + while (true) { + ch = lex_skipwhite(lex, hadwhite); + hadwhite = true; + if (!lex->flags.mergelines || ch != '\\') + break; + ch = lex_getch(lex); + if (ch == '\r') + ch = lex_getch(lex); + if (ch != '\n') { + lex_ungetch(lex, ch); + ch = '\\'; + break; + } + /* we reached a linemerge */ + lex_tokench(lex, '\n'); + continue; + } + + if (lex->flags.preprocessing && (ch == TOKEN_WHITE || ch == TOKEN_EOL || ch == TOKEN_FATAL)) { + return (lex->tok.ttype = ch); + } + + lex->sline = lex->line; + lex->tok.ctx.line = lex->sline; + lex->tok.ctx.file = lex->name; + + if (lex->eof) + return (lex->tok.ttype = TOKEN_FATAL); + + if (ch == EOF) { + lex->eof = true; + return (lex->tok.ttype = TOKEN_EOF); + } + + /* modelgen / spiritgen commands */ + if (ch == '$' && !lex->flags.preprocessing) { + const char *v; + size_t frame; + + ch = lex_getch(lex); + if (!isident_start(ch)) { + lexerror(lex, "hanging '$' modelgen/spritegen command line"); + return lex_do(lex); + } + lex_tokench(lex, ch); + if (!lex_finish_ident(lex)) + return (lex->tok.ttype = TOKEN_ERROR); + lex_endtoken(lex); + /* skip the known commands */ + v = lex->tok.value; + + if (!strcmp(v, "frame") || !strcmp(v, "framesave")) + { + /* frame/framesave command works like an enum + * similar to fteqcc we handle this in the lexer. + * The reason for this is that it is sensitive to newlines, + * which the parser is unaware of + */ + if (!lex_finish_frames(lex)) + return (lex->tok.ttype = TOKEN_ERROR); + return lex_do(lex); + } + + if (!strcmp(v, "framevalue")) + { + ch = lex_getch(lex); + while (ch != EOF && isspace(ch) && ch != '\n') + ch = lex_getch(lex); + + if (!isdigit(ch)) { + lexerror(lex, "$framevalue requires an integer parameter"); + return lex_do(lex); + } + + lex_token_new(lex); + lex->tok.ttype = lex_finish_digit(lex, ch); + lex_endtoken(lex); + if (lex->tok.ttype != TOKEN_INTCONST) { + lexerror(lex, "$framevalue requires an integer parameter"); + return lex_do(lex); + } + lex->framevalue = lex->tok.constval.i; + return lex_do(lex); + } + + if (!strcmp(v, "framerestore")) + { + int rc; + + lex_token_new(lex); + + rc = lex_parse_frame(lex); + + if (rc > 0) { + lexerror(lex, "$framerestore requires a framename parameter"); + return lex_do(lex); + } + if (rc < 0) + return (lex->tok.ttype = TOKEN_FATAL); + + v = lex->tok.value; + for (frame = 0; frame < vec_size(lex->frames); ++frame) { + if (!strcmp(v, lex->frames[frame].name)) { + lex->framevalue = lex->frames[frame].value; + return lex_do(lex); + } + } + lexerror(lex, "unknown framename `%s`", v); + return lex_do(lex); + } + + if (!strcmp(v, "modelname")) + { + int rc; + + lex_token_new(lex); + + rc = lex_parse_frame(lex); + + if (rc > 0) { + lexerror(lex, "$modelname requires a parameter"); + return lex_do(lex); + } + if (rc < 0) + return (lex->tok.ttype = TOKEN_FATAL); + + if (lex->modelname) { + frame_macro m; + m.value = lex->framevalue; + m.name = lex->modelname; + lex->modelname = NULL; + vec_push(lex->frames, m); + } + lex->modelname = lex->tok.value; + lex->tok.value = NULL; + return lex_do(lex); + } + + if (!strcmp(v, "flush")) + { + size_t fi; + for (fi = 0; fi < vec_size(lex->frames); ++fi) + mem_d(lex->frames[fi].name); + vec_free(lex->frames); + /* skip line (fteqcc does it too) */ + ch = lex_getch(lex); + while (ch != EOF && ch != '\n') + ch = lex_getch(lex); + return lex_do(lex); + } + + if (!strcmp(v, "cd") || + !strcmp(v, "origin") || + !strcmp(v, "base") || + !strcmp(v, "flags") || + !strcmp(v, "scale") || + !strcmp(v, "skin")) + { + /* skip line */ + ch = lex_getch(lex); + while (ch != EOF && ch != '\n') + ch = lex_getch(lex); + return lex_do(lex); + } + + for (frame = 0; frame < vec_size(lex->frames); ++frame) { + if (!strcmp(v, lex->frames[frame].name)) { + lex->tok.constval.i = lex->frames[frame].value; + return (lex->tok.ttype = TOKEN_INTCONST); + } + } + + lexerror(lex, "invalid frame macro"); + return lex_do(lex); + } + + /* single-character tokens */ + switch (ch) + { + case '[': + nextch = lex_getch(lex); + if (nextch == '[') { + lex_tokench(lex, ch); + lex_tokench(lex, nextch); + lex_endtoken(lex); + return (lex->tok.ttype = TOKEN_ATTRIBUTE_OPEN); + } + lex_ungetch(lex, nextch); + /* FALL THROUGH */ + case '(': + case ':': + case '?': + lex_tokench(lex, ch); + lex_endtoken(lex); + if (lex->flags.noops) + return (lex->tok.ttype = ch); + else + return (lex->tok.ttype = TOKEN_OPERATOR); + + case ']': + if (lex->flags.noops) { + nextch = lex_getch(lex); + if (nextch == ']') { + lex_tokench(lex, ch); + lex_tokench(lex, nextch); + lex_endtoken(lex); + return (lex->tok.ttype = TOKEN_ATTRIBUTE_CLOSE); + } + lex_ungetch(lex, nextch); + } + /* FALL THROUGH */ + case ')': + case ';': + case '{': + case '}': + + case '#': + lex_tokench(lex, ch); + lex_endtoken(lex); + return (lex->tok.ttype = ch); + default: + break; + } + + if (ch == '.') { + nextch = lex_getch(lex); + /* digits starting with a dot */ + if (isdigit(nextch)) { + lex_ungetch(lex, nextch); + lex->tok.ttype = lex_finish_digit(lex, ch); + lex_endtoken(lex); + return lex->tok.ttype; + } + lex_ungetch(lex, nextch); + } + + if (lex->flags.noops) + { + /* Detect characters early which are normally + * operators OR PART of an operator. + */ + switch (ch) + { + /* + case '+': + case '-': + */ + case '*': + case '/': + case '<': + case '>': + case '=': + case '&': + case '|': + case '^': + case '~': + case ',': + case '!': + lex_tokench(lex, ch); + lex_endtoken(lex); + return (lex->tok.ttype = ch); + default: + break; + } + } + + if (ch == '.') + { + lex_tokench(lex, ch); + /* peak ahead once */ + nextch = lex_getch(lex); + if (nextch != '.') { + lex_ungetch(lex, nextch); + lex_endtoken(lex); + if (lex->flags.noops) + return (lex->tok.ttype = ch); + else + return (lex->tok.ttype = TOKEN_OPERATOR); + } + /* peak ahead again */ + nextch = lex_getch(lex); + if (nextch != '.') { + lex_ungetch(lex, nextch); + lex_ungetch(lex, '.'); + lex_endtoken(lex); + if (lex->flags.noops) + return (lex->tok.ttype = ch); + else + return (lex->tok.ttype = TOKEN_OPERATOR); + } + /* fill the token to be "..." */ + lex_tokench(lex, ch); + lex_tokench(lex, ch); + lex_endtoken(lex); + return (lex->tok.ttype = TOKEN_DOTS); + } + + if (ch == ',' || ch == '.') { + lex_tokench(lex, ch); + lex_endtoken(lex); + return (lex->tok.ttype = TOKEN_OPERATOR); + } + + if (ch == '+' || ch == '-' || /* ++, --, +=, -= and -> as well! */ + ch == '>' || ch == '<' || /* <<, >>, <=, >= */ + ch == '=' || ch == '!' || /* <=>, ==, != */ + ch == '&' || ch == '|' || /* &&, ||, &=, |= */ + ch == '~' /* ~=, ~ */ + ) { + lex_tokench(lex, ch); + + nextch = lex_getch(lex); + if ((nextch == '=' && ch != '<') || (nextch == ch && ch != '!')) { + lex_tokench(lex, nextch); + } else if (ch == '<' && nextch == '=') { + lex_tokench(lex, nextch); + if ((thirdch = lex_getch(lex)) == '>') + lex_tokench(lex, thirdch); + else + lex_ungetch(lex, thirdch); + + } else if (ch == '-' && nextch == '>') { + lex_tokench(lex, nextch); + } else if (ch == '&' && nextch == '~') { + thirdch = lex_getch(lex); + if (thirdch != '=') { + lex_ungetch(lex, thirdch); + lex_ungetch(lex, nextch); + } + else { + lex_tokench(lex, nextch); + lex_tokench(lex, thirdch); + } + } + else if (lex->flags.preprocessing && + ch == '-' && isdigit(nextch)) + { + lex->tok.ttype = lex_finish_digit(lex, nextch); + if (lex->tok.ttype == TOKEN_INTCONST) + lex->tok.constval.i = -lex->tok.constval.i; + else + lex->tok.constval.f = -lex->tok.constval.f; + lex_endtoken(lex); + return lex->tok.ttype; + } else { + lex_ungetch(lex, nextch); + } + + lex_endtoken(lex); + return (lex->tok.ttype = TOKEN_OPERATOR); + } + + /* + if (ch == '^' || ch == '~' || ch == '!') + { + lex_tokench(lex, ch); + lex_endtoken(lex); + return (lex->tok.ttype = TOKEN_OPERATOR); + } + */ + + if (ch == '*' || ch == '/') /* *=, /= */ + { + lex_tokench(lex, ch); + + nextch = lex_getch(lex); + if (nextch == '=' || nextch == '*') { + lex_tokench(lex, nextch); + } else + lex_ungetch(lex, nextch); + + lex_endtoken(lex); + return (lex->tok.ttype = TOKEN_OPERATOR); + } + + if (ch == '%') { + lex_tokench(lex, ch); + lex_endtoken(lex); + return (lex->tok.ttype = TOKEN_OPERATOR); + } + + if (isident_start(ch)) + { + const char *v; + + lex_tokench(lex, ch); + if (!lex_finish_ident(lex)) { + /* error? */ + return (lex->tok.ttype = TOKEN_ERROR); + } + lex_endtoken(lex); + lex->tok.ttype = TOKEN_IDENT; + + v = lex->tok.value; + if (!strcmp(v, "void")) { + lex->tok.ttype = TOKEN_TYPENAME; + lex->tok.constval.t = TYPE_VOID; + } else if (!strcmp(v, "int")) { + lex->tok.ttype = TOKEN_TYPENAME; + lex->tok.constval.t = TYPE_INTEGER; + } else if (!strcmp(v, "float")) { + lex->tok.ttype = TOKEN_TYPENAME; + lex->tok.constval.t = TYPE_FLOAT; + } else if (!strcmp(v, "string")) { + lex->tok.ttype = TOKEN_TYPENAME; + lex->tok.constval.t = TYPE_STRING; + } else if (!strcmp(v, "entity")) { + lex->tok.ttype = TOKEN_TYPENAME; + lex->tok.constval.t = TYPE_ENTITY; + } else if (!strcmp(v, "vector")) { + lex->tok.ttype = TOKEN_TYPENAME; + lex->tok.constval.t = TYPE_VECTOR; + } else { + size_t kw; + for (kw = 0; kw < num_keywords_qc; ++kw) { + if (!strcmp(v, keywords_qc[kw])) + return (lex->tok.ttype = TOKEN_KEYWORD); + } + if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_QCC) { + for (kw = 0; kw < num_keywords_fg; ++kw) { + if (!strcmp(v, keywords_fg[kw])) + return (lex->tok.ttype = TOKEN_KEYWORD); + } + } + } + + return lex->tok.ttype; + } + + if (ch == '"') + { + lex->flags.nodigraphs = true; + if (lex->flags.preprocessing) + lex_tokench(lex, ch); + lex->tok.ttype = lex_finish_string(lex, '"'); + if (lex->flags.preprocessing) + lex_tokench(lex, ch); + while (!lex->flags.preprocessing && lex->tok.ttype == TOKEN_STRINGCONST) + { + /* Allow c style "string" "continuation" */ + ch = lex_skipwhite(lex, false); + if (ch != '"') { + lex_ungetch(lex, ch); + break; + } + + lex->tok.ttype = lex_finish_string(lex, '"'); + } + lex->flags.nodigraphs = false; + lex_endtoken(lex); + return lex->tok.ttype; + } + + if (ch == '\'') + { + /* we parse character constants like string, + * but return TOKEN_CHARCONST, or a vector type if it fits... + * Likewise actual unescaping has to be done by the parser. + * The difference is we don't allow 'char' 'continuation'. + */ + if (lex->flags.preprocessing) + lex_tokench(lex, ch); + lex->tok.ttype = lex_finish_string(lex, '\''); + if (lex->flags.preprocessing) + lex_tokench(lex, ch); + lex_endtoken(lex); + + lex->tok.ttype = TOKEN_CHARCONST; + /* It's a vector if we can successfully scan 3 floats */ +#ifdef _MSC_VER + if (sscanf_s(lex->tok.value, " %f %f %f ", + &lex->tok.constval.v.x, &lex->tok.constval.v.y, &lex->tok.constval.v.z) == 3) +#else + if (sscanf(lex->tok.value, " %f %f %f ", + &lex->tok.constval.v.x, &lex->tok.constval.v.y, &lex->tok.constval.v.z) == 3) +#endif + + { + lex->tok.ttype = TOKEN_VECTORCONST; + } + else + { + if (!lex->flags.preprocessing && strlen(lex->tok.value) > 1) { + uchar_t u8char; + /* check for a valid utf8 character */ + if (!OPTS_FLAG(UTF8) || !u8_analyze(lex->tok.value, NULL, NULL, &u8char, 8)) { + if (lexwarn(lex, WARN_MULTIBYTE_CHARACTER, + ( OPTS_FLAG(UTF8) ? "invalid multibyte character sequence `%s`" + : "multibyte character: `%s`" ), + lex->tok.value)) + return (lex->tok.ttype = TOKEN_ERROR); + } + else + lex->tok.constval.i = u8char; + } + else + lex->tok.constval.i = lex->tok.value[0]; + } + + return lex->tok.ttype; + } + + if (isdigit(ch)) + { + lex->tok.ttype = lex_finish_digit(lex, ch); + lex_endtoken(lex); + return lex->tok.ttype; + } + + if (lex->flags.preprocessing) { + lex_tokench(lex, ch); + lex_endtoken(lex); + return (lex->tok.ttype = ch); + } + + lexerror(lex, "unknown token: `%s`", lex->tok.value); + return (lex->tok.ttype = TOKEN_ERROR); +} diff --git a/misc/source/gmqcc-src/lexer.h b/misc/source/gmqcc-src/lexer.h new file mode 100644 index 00000000..f51d56e9 --- /dev/null +++ b/misc/source/gmqcc-src/lexer.h @@ -0,0 +1,345 @@ +/* + * Copyright (C) 2012, 2013 + * Wolfgang Bumiller + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef GMQCC_LEXER_HDR +#define GMQCC_LEXER_HDR +typedef struct token_s token; + +struct token_s { + int ttype; + + char *value; + + union { + vector v; + int i; + double f; + int t; /* type */ + } constval; + +#if 0 + struct token_s *next; + struct token_s *prev; +#endif + + lex_ctx ctx; +}; + +#if 0 +token* token_new(); +void token_delete(token*); +token* token_copy(const token *cp); +void token_delete_all(token *t); +token* token_copy_all(const token *cp); +#endif + +/* Lexer + * + */ +enum { + /* Other tokens which we can return: */ + TOKEN_NONE = 0, + TOKEN_START = 128, + + TOKEN_IDENT, + + TOKEN_TYPENAME, + + TOKEN_OPERATOR, + + TOKEN_KEYWORD, /* loop */ + + TOKEN_DOTS, /* 3 dots, ... */ + + TOKEN_ATTRIBUTE_OPEN, /* [[ */ + TOKEN_ATTRIBUTE_CLOSE, /* ]] */ + + TOKEN_VA_ARGS, /* for the ftepp only */ + TOKEN_VA_ARGS_ARRAY, /* for the ftepp only */ + TOKEN_VA_COUNT, /* to get the count of vaargs */ + + TOKEN_STRINGCONST, /* not the typename but an actual "string" */ + TOKEN_CHARCONST, + TOKEN_VECTORCONST, + TOKEN_INTCONST, + TOKEN_FLOATCONST, + + TOKEN_WHITE, + TOKEN_EOL, + + /* if we add additional tokens before this, the exposed API + * should not be broken anyway, but EOF/ERROR/... should + * still be at the bottom + */ + TOKEN_EOF = 1024, + + /* We use '< TOKEN_ERROR', so TOKEN_FATAL must come after it and any + * other error related tokens as well + */ + TOKEN_ERROR, + TOKEN_FATAL /* internal error, eg out of memory */ +}; + +typedef struct { + char *name; + int value; +} frame_macro; + +typedef struct lex_file_s { + FILE *file; + const char *open_string; + size_t open_string_length; + size_t open_string_pos; + + char *name; + size_t line; + size_t sline; /* line at the start of a token */ + size_t column; + + int peek[256]; + size_t peekpos; + + bool eof; + + token tok; /* not a pointer anymore */ + + struct { + bool noops; + bool nodigraphs; /* used when lexing string constants */ + bool preprocessing; /* whitespace and EOLs become actual tokens */ + bool mergelines; /* backslash at the end of a line escapes the newline */ + } flags; + + int framevalue; + frame_macro *frames; + char *modelname; + + size_t push_line; +} lex_file; + +lex_file* lex_open (const char *file); +lex_file* lex_open_string(const char *str, size_t len, const char *name); +void lex_close(lex_file *lex); +int lex_do (lex_file *lex); +void lex_cleanup(void); + +/* Parser + * + */ + +enum { + ASSOC_LEFT, + ASSOC_RIGHT +}; + +#define OP_SUFFIX 1 +#define OP_PREFIX 2 + +typedef struct { + const char *op; + unsigned int operands; + unsigned int id; + unsigned int assoc; + signed int prec; + unsigned int flags; +} oper_info; + +/* + * Explicit uint8_t casts since the left operand of shift operator cannot + * be negative, even though it won't happen, this supresses the future + * possibility. + */ +#define opid1(a) ((uint8_t)a) +#define opid2(a,b) (((uint8_t)a<<8) |(uint8_t)b) +#define opid3(a,b,c) (((uint8_t)a<<16)|((uint8_t)b<<8)|(uint8_t)c) + +static const oper_info c_operators[] = { + { "(", 0, opid1('('), ASSOC_LEFT, 99, OP_PREFIX}, /* paren expression - non function call */ + + { "++", 1, opid3('S','+','+'), ASSOC_LEFT, 17, OP_SUFFIX}, + { "--", 1, opid3('S','-','-'), ASSOC_LEFT, 17, OP_SUFFIX}, + { ".", 2, opid1('.'), ASSOC_LEFT, 17, 0 }, + { "(", 0, opid1('('), ASSOC_LEFT, 17, 0 }, /* function call */ + { "[", 2, opid1('['), ASSOC_LEFT, 17, 0 }, /* array subscript */ + + { "++", 1, opid3('+','+','P'), ASSOC_RIGHT, 16, OP_PREFIX }, + { "--", 1, opid3('-','-','P'), ASSOC_RIGHT, 16, OP_PREFIX }, + + { "**", 2, opid2('*', '*'), ASSOC_RIGHT, 15, 0 }, + + { "!", 1, opid2('!', 'P'), ASSOC_RIGHT, 14, OP_PREFIX }, + { "~", 1, opid2('~', 'P'), ASSOC_RIGHT, 14, OP_PREFIX }, + { "+", 1, opid2('+','P'), ASSOC_RIGHT, 14, OP_PREFIX }, + { "-", 1, opid2('-','P'), ASSOC_RIGHT, 14, OP_PREFIX }, +/* { "&", 1, opid2('&','P'), ASSOC_RIGHT, 14, OP_PREFIX }, */ + + { "*", 2, opid1('*'), ASSOC_LEFT, 13, 0 }, + { "/", 2, opid1('/'), ASSOC_LEFT, 13, 0 }, + { "%", 2, opid1('%'), ASSOC_LEFT, 13, 0 }, + + { "+", 2, opid1('+'), ASSOC_LEFT, 12, 0 }, + { "-", 2, opid1('-'), ASSOC_LEFT, 12, 0 }, + + { "<<", 2, opid2('<','<'), ASSOC_LEFT, 11, 0 }, + { ">>", 2, opid2('>','>'), ASSOC_LEFT, 11, 0 }, + + { "<", 2, opid1('<'), ASSOC_LEFT, 10, 0 }, + { ">", 2, opid1('>'), ASSOC_LEFT, 10, 0 }, + { "<=>", 2, opid3('<','=','>'), ASSOC_LEFT, 10, 0 }, + { "<=", 2, opid2('<','='), ASSOC_LEFT, 10, 0 }, + { ">=", 2, opid2('>','='), ASSOC_LEFT, 10, 0 }, + + { "==", 2, opid2('=','='), ASSOC_LEFT, 9, 0 }, + { "!=", 2, opid2('!','='), ASSOC_LEFT, 9, 0 }, + + { "&", 2, opid1('&'), ASSOC_LEFT, 8, 0 }, + + { "^", 2, opid1('^'), ASSOC_LEFT, 7, 0 }, + + { "|", 2, opid1('|'), ASSOC_LEFT, 6, 0 }, + + { "&&", 2, opid2('&','&'), ASSOC_LEFT, 5, 0 }, + + { "||", 2, opid2('|','|'), ASSOC_LEFT, 4, 0 }, + + { "?", 3, opid2('?',':'), ASSOC_RIGHT, 3, 0 }, + + { "=", 2, opid1('='), ASSOC_RIGHT, 2, 0 }, + { "+=", 2, opid2('+','='), ASSOC_RIGHT, 2, 0 }, + { "-=", 2, opid2('-','='), ASSOC_RIGHT, 2, 0 }, + { "*=", 2, opid2('*','='), ASSOC_RIGHT, 2, 0 }, + { "/=", 2, opid2('/','='), ASSOC_RIGHT, 2, 0 }, + { "%=", 2, opid2('%','='), ASSOC_RIGHT, 2, 0 }, + { ">>=", 2, opid3('>','>','='), ASSOC_RIGHT, 2, 0 }, + { "<<=", 2, opid3('<','<','='), ASSOC_RIGHT, 2, 0 }, + { "&=", 2, opid2('&','='), ASSOC_RIGHT, 2, 0 }, + { "^=", 2, opid2('^','='), ASSOC_RIGHT, 2, 0 }, + { "|=", 2, opid2('|','='), ASSOC_RIGHT, 2, 0 }, + { "&~=", 2, opid3('&','~','='), ASSOC_RIGHT, 2, 0 }, + + { ":", 0, opid2(':','?'), ASSOC_RIGHT, 1, 0 }, + + { ",", 2, opid1(','), ASSOC_LEFT, 0, 0 } +}; +static const size_t c_operator_count = (sizeof(c_operators) / sizeof(c_operators[0])); + +static const oper_info fte_operators[] = { + { "(", 0, opid1('('), ASSOC_LEFT, 99, OP_PREFIX}, /* paren expression - non function call */ + + { "++", 1, opid3('S','+','+'), ASSOC_LEFT, 15, OP_SUFFIX}, + { "--", 1, opid3('S','-','-'), ASSOC_LEFT, 15, OP_SUFFIX}, + { ".", 2, opid1('.'), ASSOC_LEFT, 15, 0 }, + { "(", 0, opid1('('), ASSOC_LEFT, 15, 0 }, /* function call */ + { "[", 2, opid1('['), ASSOC_LEFT, 15, 0 }, /* array subscript */ + + { "!", 1, opid2('!', 'P'), ASSOC_RIGHT, 14, OP_PREFIX }, + { "+", 1, opid2('+','P'), ASSOC_RIGHT, 14, OP_PREFIX }, + { "-", 1, opid2('-','P'), ASSOC_RIGHT, 14, OP_PREFIX }, + { "++", 1, opid3('+','+','P'), ASSOC_RIGHT, 14, OP_PREFIX }, + { "--", 1, opid3('-','-','P'), ASSOC_RIGHT, 14, OP_PREFIX }, + + { "*", 2, opid1('*'), ASSOC_LEFT, 13, 0 }, + { "/", 2, opid1('/'), ASSOC_LEFT, 13, 0 }, + { "&", 2, opid1('&'), ASSOC_LEFT, 13, 0 }, + { "|", 2, opid1('|'), ASSOC_LEFT, 13, 0 }, + + { "+", 2, opid1('+'), ASSOC_LEFT, 12, 0 }, + { "-", 2, opid1('-'), ASSOC_LEFT, 12, 0 }, + + { "<<", 2, opid2('<','<'), ASSOC_LEFT, 11, 0 }, + { ">>", 2, opid2('>','>'), ASSOC_LEFT, 11, 0 }, + + { "<", 2, opid1('<'), ASSOC_LEFT, 10, 0 }, + { ">", 2, opid1('>'), ASSOC_LEFT, 10, 0 }, + { "<=", 2, opid2('<','='), ASSOC_LEFT, 10, 0 }, + { ">=", 2, opid2('>','='), ASSOC_LEFT, 10, 0 }, + { "==", 2, opid2('=','='), ASSOC_LEFT, 10, 0 }, + { "!=", 2, opid2('!','='), ASSOC_LEFT, 10, 0 }, + + { "?", 3, opid2('?',':'), ASSOC_RIGHT, 9, 0 }, + + { "=", 2, opid1('='), ASSOC_RIGHT, 8, 0 }, + { "+=", 2, opid2('+','='), ASSOC_RIGHT, 8, 0 }, + { "-=", 2, opid2('-','='), ASSOC_RIGHT, 8, 0 }, + { "*=", 2, opid2('*','='), ASSOC_RIGHT, 8, 0 }, + { "/=", 2, opid2('/','='), ASSOC_RIGHT, 8, 0 }, + { "%=", 2, opid2('%','='), ASSOC_RIGHT, 8, 0 }, + { "&=", 2, opid2('&','='), ASSOC_RIGHT, 8, 0 }, + { "|=", 2, opid2('|','='), ASSOC_RIGHT, 8, 0 }, + { "&~=", 2, opid3('&','~','='), ASSOC_RIGHT, 8, 0 }, + + { "&&", 2, opid2('&','&'), ASSOC_LEFT, 5, 0 }, + { "||", 2, opid2('|','|'), ASSOC_LEFT, 5, 0 }, + + /* Leave precedence 3 for : with -fcorrect-ternary */ + { ",", 2, opid1(','), ASSOC_LEFT, 2, 0 }, + { ":", 0, opid2(':','?'), ASSOC_RIGHT, 1, 0 } +}; +static const size_t fte_operator_count = (sizeof(fte_operators) / sizeof(fte_operators[0])); + +static const oper_info qcc_operators[] = { + { "(", 0, opid1('('), ASSOC_LEFT, 99, OP_PREFIX}, /* paren expression - non function call */ + + { ".", 2, opid1('.'), ASSOC_LEFT, 15, 0 }, + { "(", 0, opid1('('), ASSOC_LEFT, 15, 0 }, /* function call */ + { "[", 2, opid1('['), ASSOC_LEFT, 15, 0 }, /* array subscript */ + + { "!", 1, opid2('!', 'P'), ASSOC_RIGHT, 14, OP_PREFIX }, + { "+", 1, opid2('+','P'), ASSOC_RIGHT, 14, OP_PREFIX }, + { "-", 1, opid2('-','P'), ASSOC_RIGHT, 14, OP_PREFIX }, + + { "*", 2, opid1('*'), ASSOC_LEFT, 13, 0 }, + { "/", 2, opid1('/'), ASSOC_LEFT, 13, 0 }, + { "&", 2, opid1('&'), ASSOC_LEFT, 13, 0 }, + { "|", 2, opid1('|'), ASSOC_LEFT, 13, 0 }, + + { "+", 2, opid1('+'), ASSOC_LEFT, 12, 0 }, + { "-", 2, opid1('-'), ASSOC_LEFT, 12, 0 }, + + { "<", 2, opid1('<'), ASSOC_LEFT, 10, 0 }, + { ">", 2, opid1('>'), ASSOC_LEFT, 10, 0 }, + { "<=", 2, opid2('<','='), ASSOC_LEFT, 10, 0 }, + { ">=", 2, opid2('>','='), ASSOC_LEFT, 10, 0 }, + { "==", 2, opid2('=','='), ASSOC_LEFT, 10, 0 }, + { "!=", 2, opid2('!','='), ASSOC_LEFT, 10, 0 }, + + { "=", 2, opid1('='), ASSOC_RIGHT, 8, 0 }, + { "+=", 2, opid2('+','='), ASSOC_RIGHT, 8, 0 }, + { "-=", 2, opid2('-','='), ASSOC_RIGHT, 8, 0 }, + { "*=", 2, opid2('*','='), ASSOC_RIGHT, 8, 0 }, + { "/=", 2, opid2('/','='), ASSOC_RIGHT, 8, 0 }, + { "%=", 2, opid2('%','='), ASSOC_RIGHT, 8, 0 }, + { "&=", 2, opid2('&','='), ASSOC_RIGHT, 8, 0 }, + { "|=", 2, opid2('|','='), ASSOC_RIGHT, 8, 0 }, + + { "&&", 2, opid2('&','&'), ASSOC_LEFT, 5, 0 }, + { "||", 2, opid2('|','|'), ASSOC_LEFT, 5, 0 }, + + { ",", 2, opid1(','), ASSOC_LEFT, 2, 0 }, +}; +static const size_t qcc_operator_count = (sizeof(qcc_operators) / sizeof(qcc_operators[0])); + +extern const oper_info *operators; +extern size_t operator_count; +/*void lexerror(lex_file*, const char *fmt, ...);*/ + +#endif diff --git a/misc/source/gmqcc-src/main.c b/misc/source/gmqcc-src/main.c new file mode 100644 index 00000000..37638e92 --- /dev/null +++ b/misc/source/gmqcc-src/main.c @@ -0,0 +1,808 @@ +/* + * Copyright (C) 2012, 2013 + * Dale Weiler + * Wolfgang Bumiller + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include +#include +#include +#include +#include + + +#include "gmqcc.h" +#include "lexer.h" + +/* TODO: cleanup this whole file .. it's a fuckign mess */ + +/* set by the standard */ +const oper_info *operators = NULL; +size_t operator_count = 0; +static bool opts_output_wasset = false; + +typedef struct { char *filename; int type; } argitem; +typedef struct { char *name; char *value; } ppitem; +static argitem *items = NULL; +static ppitem *ppems = NULL; + +#define TYPE_QC 0 +#define TYPE_ASM 1 +#define TYPE_SRC 2 + + +static const char *app_name; + +static void version(void) { + con_out("GMQCC %d.%d.%d Built %s %s\n" GMQCC_DEV_VERSION_STRING, + GMQCC_VERSION_MAJOR, + GMQCC_VERSION_MINOR, + GMQCC_VERSION_PATCH, + __DATE__, + __TIME__ + ); +} + +static int usage(void) { + con_out("usage: %s [options] [files...]", app_name); + con_out("options:\n" + " -h, --help show this help message\n" + " -debug turns on compiler debug messages\n" + " -memchk turns on compiler memory leak check\n"); + con_out(" -o, --output=file output file, defaults to progs.dat\n" + " -s filename add a progs.src file to be used\n"); + con_out(" -E stop after preprocessing\n"); + con_out(" -q, --quiet be less verbose\n"); + con_out(" -config file use the specified ini file\n"); + con_out(" -std=standard select one of the following standards\n" + " -std=qcc original QuakeC\n" + " -std=fteqcc fteqcc QuakeC\n" + " -std=gmqcc this compiler (default)\n"); + con_out(" -f enable a flag\n" + " -fno- disable a flag\n" + " -fhelp list possible flags\n"); + con_out(" -W enable a warning\n" + " -Wno- disable a warning\n" + " -Wall enable all warnings\n"); + con_out(" -Werror treat warnings as errors\n" + " -Werror- treat a warning as error\n" + " -Wno-error- opposite of the above\n"); + con_out(" -Whelp list possible warnings\n"); + con_out(" -O optimization level\n" + " -O enable specific optimization\n" + " -Ono- disable specific optimization\n" + " -Ohelp list optimizations\n"); + con_out(" -force-crc=num force a specific checksum into the header\n"); + return -1; +} + +/* command line parsing */ +static bool options_witharg(int *argc_, char ***argv_, char **out) { + int argc = *argc_; + char **argv = *argv_; + + if (argv[0][2]) { + *out = argv[0]+2; + return true; + } + /* eat up the next */ + if (argc < 2) /* no parameter was provided */ + return false; + + *out = argv[1]; + --*argc_; + ++*argv_; + return true; +} + +static bool options_long_witharg_all(const char *optname, int *argc_, char ***argv_, char **out, int ds, bool split) { + int argc = *argc_; + char **argv = *argv_; + + size_t len = strlen(optname); + + if (strncmp(argv[0]+ds, optname, len)) + return false; + + /* it's --optname, check how the parameter is supplied */ + if (argv[0][ds+len] == '=') { + /* using --opt=param */ + *out = argv[0]+ds+len+1; + return true; + } + + if (!split || argc < ds) /* no parameter was provided, or only single-arg form accepted */ + return false; + + /* using --opt param */ + *out = argv[1]; + --*argc_; + ++*argv_; + return true; +} +static bool options_long_witharg(const char *optname, int *argc_, char ***argv_, char **out) { + return options_long_witharg_all(optname, argc_, argv_, out, 2, true); +} +static bool options_long_gcc(const char *optname, int *argc_, char ***argv_, char **out) { + return options_long_witharg_all(optname, argc_, argv_, out, 1, false); +} + +static bool options_parse(int argc, char **argv) { + bool argend = false; + size_t itr; + char buffer[1024]; + char *redirout = NULL; + char *redirerr = NULL; + char *config = NULL; + char *memdumpcols = NULL; + + while (!argend && argc > 1) { + char *argarg; + argitem item; + ppitem macro; + + ++argv; + --argc; + + if (argv[0][0] == '-') { + /* All gcc-type long options */ + if (options_long_gcc("std", &argc, &argv, &argarg)) { + if (!strcmp(argarg, "gmqcc") || !strcmp(argarg, "default")) { + + opts_set(opts.flags, ADJUST_VECTOR_FIELDS, true); + opts_set(opts.flags, CORRECT_LOGIC, true); + opts_set(opts.flags, FALSE_EMPTY_STRINGS, false); + opts_set(opts.flags, TRUE_EMPTY_STRINGS, true); + opts_set(opts.flags, LOOP_LABELS, true); + opts_set(opts.flags, TRANSLATABLE_STRINGS, true); + opts_set(opts.flags, INITIALIZED_NONCONSTANTS, true); + opts_set(opts.werror, WARN_INVALID_PARAMETER_COUNT, true); + opts_set(opts.werror, WARN_MISSING_RETURN_VALUES, true); + opts_set(opts.flags, EXPRESSIONS_FOR_BUILTINS, true); + + + OPTS_OPTION_U32(OPTION_STANDARD) = COMPILER_GMQCC; + OPTS_OPTION_BOOL(OPTION_STATISTICS) = true; + + } else if (!strcmp(argarg, "qcc")) { + + opts_set(opts.flags, ADJUST_VECTOR_FIELDS, false); + opts_set(opts.flags, ASSIGN_FUNCTION_TYPES, true); + + OPTS_OPTION_U32(OPTION_STANDARD) = COMPILER_QCC; + + } else if (!strcmp(argarg, "fte") || !strcmp(argarg, "fteqcc")) { + + opts_set(opts.flags, FTEPP, true); + opts_set(opts.flags, TRANSLATABLE_STRINGS, true); + opts_set(opts.flags, ADJUST_VECTOR_FIELDS, false); + opts_set(opts.flags, ASSIGN_FUNCTION_TYPES, true); + opts_set(opts.flags, CORRECT_TERNARY, false); + opts_set(opts.warn, WARN_TERNARY_PRECEDENCE, true); + + OPTS_OPTION_U32(OPTION_STANDARD) = COMPILER_FTEQCC; + + } else if (!strcmp(argarg, "qccx")) { + + opts_set(opts.flags, ADJUST_VECTOR_FIELDS, false); + OPTS_OPTION_U32(OPTION_STANDARD) = COMPILER_QCCX; + + } else { + con_out("Unknown standard: %s\n", argarg); + return false; + } + continue; + } + if (options_long_gcc("force-crc", &argc, &argv, &argarg)) { + + OPTS_OPTION_BOOL(OPTION_FORCECRC) = true; + OPTS_OPTION_U16 (OPTION_FORCED_CRC) = strtol(argarg, NULL, 0); + continue; + } + if (options_long_gcc("redirout", &argc, &argv, &redirout)) { + con_change(redirout, redirerr); + continue; + } + if (options_long_gcc("redirerr", &argc, &argv, &redirerr)) { + con_change(redirout, redirerr); + continue; + } + if (options_long_gcc("config", &argc, &argv, &argarg)) { + config = argarg; + continue; + } + if (options_long_gcc("memdumpcols", &argc, &argv, &memdumpcols)) { + OPTS_OPTION_U16(OPTION_MEMDUMPCOLS) = (uint16_t)strtol(memdumpcols, NULL, 10); + continue; + } + + /* show defaults (like pathscale) */ + if (!strcmp(argv[0]+1, "show-defaults")) { + for (itr = 0; itr < COUNT_FLAGS; ++itr) { + if (!OPTS_FLAG(itr)) + continue; + + memset(buffer, 0, sizeof(buffer)); + util_strtononcmd(opts_flag_list[itr].name, buffer, strlen(opts_flag_list[itr].name) + 1); + + con_out("-f%s ", buffer); + } + for (itr = 0; itr < COUNT_WARNINGS; ++itr) { + if (!OPTS_WARN(itr)) + continue; + + memset(buffer, 0, sizeof(buffer)); + util_strtononcmd(opts_warn_list[itr].name, buffer, strlen(opts_warn_list[itr].name) + 1); + con_out("-W%s ", buffer); + } + con_out("\n"); + exit(0); + } + + if (!strcmp(argv[0]+1, "debug")) { + OPTS_OPTION_BOOL(OPTION_DEBUG) = true; + continue; + } + if (!strcmp(argv[0]+1, "dump")) { + OPTS_OPTION_BOOL(OPTION_DUMP) = true; + continue; + } + if (!strcmp(argv[0]+1, "dumpfin")) { + OPTS_OPTION_BOOL(OPTION_DUMPFIN) = true; + continue; + } + if (!strcmp(argv[0]+1, "memchk")) { + OPTS_OPTION_BOOL(OPTION_MEMCHK) = true; + continue; + } + if (!strcmp(argv[0]+1, "nocolor")) { + con_color(0); + continue; + } + + switch (argv[0][1]) { + /* -h, show usage but exit with 0 */ + case 'h': + usage(); + exit(0); + /* break; never reached because of exit(0) */ + + case 'v': + version(); + exit(0); + + case 'E': + OPTS_OPTION_BOOL(OPTION_PP_ONLY) = true; + opts_set(opts.flags, FTEPP_PREDEFS, true); /* predefs on for -E */ + break; + + /* debug turns on -flno */ + case 'g': + opts_setflag("LNO", true); + OPTS_OPTION_BOOL(OPTION_G) = true; + break; + + case 'q': + OPTS_OPTION_BOOL(OPTION_QUIET) = true; + break; + + case 'D': + if (!strlen(argv[0]+2)) { + con_err("expected name after -D\n"); + exit(0); + } + + if (!(argarg = strchr(argv[0] + 2, '='))) { + macro.name = util_strdup(argv[0]+2); + macro.value = NULL; + } else { + *argarg='\0'; /* terminate for name */ + macro.name = util_strdup(argv[0]+2); + macro.value = util_strdup(argarg+1); + } + vec_push(ppems, macro); + break; + + /* handle all -fflags */ + case 'f': + util_strtocmd(argv[0]+2, argv[0]+2, strlen(argv[0]+2)+1); + if (!strcmp(argv[0]+2, "HELP") || *(argv[0]+2) == '?') { + con_out("Possible flags:\n\n"); + for (itr = 0; itr < COUNT_FLAGS; ++itr) { + util_strtononcmd(opts_flag_list[itr].name, buffer, sizeof(buffer)); + con_out(" -f%s\n", buffer); + } + exit(0); + } + else if (!strncmp(argv[0]+2, "NO_", 3)) { + if (!opts_setflag(argv[0]+5, false)) { + con_out("unknown flag: %s\n", argv[0]+2); + return false; + } + } + else if (!opts_setflag(argv[0]+2, true)) { + con_out("unknown flag: %s\n", argv[0]+2); + return false; + } + break; + case 'W': + util_strtocmd(argv[0]+2, argv[0]+2, strlen(argv[0]+2)+1); + if (!strcmp(argv[0]+2, "HELP") || *(argv[0]+2) == '?') { + con_out("Possible warnings:\n"); + for (itr = 0; itr < COUNT_WARNINGS; ++itr) { + util_strtononcmd(opts_warn_list[itr].name, buffer, sizeof(buffer)); + con_out(" -W%s\n", buffer); + if (itr == WARN_DEBUG) + con_out(" Warnings included by -Wall:\n"); + } + exit(0); + } + else if (!strcmp(argv[0]+2, "NO_ERROR") || + !strcmp(argv[0]+2, "NO_ERROR_ALL")) + { + for (itr = 0; itr < sizeof(opts.werror)/sizeof(opts.werror[0]); ++itr) + opts.werror[itr] = 0; + break; + } + else if (!strcmp(argv[0]+2, "ERROR") || + !strcmp(argv[0]+2, "ERROR_ALL")) + { + opts_backup_non_Werror_all(); + for (itr = 0; itr < sizeof(opts.werror)/sizeof(opts.werror[0]); ++itr) + opts.werror[itr] = 0xFFFFFFFFL; + opts_restore_non_Werror_all(); + break; + } + else if (!strcmp(argv[0]+2, "NONE")) { + for (itr = 0; itr < sizeof(opts.warn)/sizeof(opts.warn[0]); ++itr) + opts.warn[itr] = 0; + break; + } + else if (!strcmp(argv[0]+2, "ALL")) { + opts_backup_non_Wall(); + for (itr = 0; itr < sizeof(opts.warn)/sizeof(opts.warn[0]); ++itr) + opts.warn[itr] = 0xFFFFFFFFL; + opts_restore_non_Wall(); + break; + } + else if (!strncmp(argv[0]+2, "ERROR_", 6)) { + if (!opts_setwerror(argv[0]+8, true)) { + con_out("unknown warning: %s\n", argv[0]+2); + return false; + } + } + else if (!strncmp(argv[0]+2, "NO_ERROR_", 9)) { + if (!opts_setwerror(argv[0]+11, false)) { + con_out("unknown warning: %s\n", argv[0]+2); + return false; + } + } + else if (!strncmp(argv[0]+2, "NO_", 3)) { + if (!opts_setwarn(argv[0]+5, false)) { + con_out("unknown warning: %s\n", argv[0]+2); + return false; + } + } + else if (!opts_setwarn(argv[0]+2, true)) { + con_out("unknown warning: %s\n", argv[0]+2); + return false; + } + break; + + case 'O': + if (!options_witharg(&argc, &argv, &argarg)) { + con_out("option -O requires a numerical argument, or optimization name with an optional 'no-' prefix\n"); + return false; + } + if (isdigit(argarg[0])) { + uint32_t val = (uint32_t)strtol(argarg, NULL, 10); + OPTS_OPTION_U32(OPTION_O) = val; + opts_setoptimlevel(val); + } else { + util_strtocmd(argarg, argarg, strlen(argarg)+1); + if (!strcmp(argarg, "HELP")) { + con_out("Possible optimizations:\n"); + for (itr = 0; itr < COUNT_OPTIMIZATIONS; ++itr) { + util_strtononcmd(opts_opt_list[itr].name, buffer, sizeof(buffer)); + con_out(" -O%-20s (-O%u)\n", buffer, opts_opt_oflag[itr]); + } + exit(0); + } + else if (!strcmp(argarg, "ALL")) + opts_setoptimlevel(OPTS_OPTION_U32(OPTION_O) = 9999); + else if (!strncmp(argarg, "NO_", 3)) { + if (!opts_setoptim(argarg+3, false)) { + con_out("unknown optimization: %s\n", argarg+3); + return false; + } + } + else { + if (!opts_setoptim(argarg, true)) { + con_out("unknown optimization: %s\n", argarg); + return false; + } + } + } + break; + + case 'o': + if (!options_witharg(&argc, &argv, &argarg)) { + con_out("option -o requires an argument: the output file name\n"); + return false; + } + OPTS_OPTION_STR(OPTION_OUTPUT) = argarg; + opts_output_wasset = true; + break; + + case 'a': + case 's': + item.type = argv[0][1] == 'a' ? TYPE_ASM : TYPE_SRC; + if (!options_witharg(&argc, &argv, &argarg)) { + con_out("option -a requires a filename %s\n", + (argv[0][1] == 'a' ? "containing QC-asm" : "containing a progs.src formatted list")); + return false; + } + item.filename = argarg; + vec_push(items, item); + break; + + case '-': + if (!argv[0][2]) { + /* anything following -- is considered a non-option argument */ + argend = true; + break; + } + /* All long options without arguments */ + else if (!strcmp(argv[0]+2, "help")) { + usage(); + exit(0); + } + else if (!strcmp(argv[0]+2, "version")) { + version(); + exit(0); + } + else if (!strcmp(argv[0]+2, "quiet")) { + OPTS_OPTION_BOOL(OPTION_QUIET) = true; + break; + } + else if (!strcmp(argv[0]+2, "correct")) { + OPTS_OPTION_BOOL(OPTION_CORRECTION) = true; + break; + } + else if (!strcmp(argv[0]+2, "no-correct")) { + OPTS_OPTION_BOOL(OPTION_CORRECTION) = false; + break; + } + else if (!strcmp(argv[0]+2, "add-info")) { + OPTS_OPTION_BOOL(OPTION_ADD_INFO) = true; + break; + } + else { + /* All long options with arguments */ + if (options_long_witharg("output", &argc, &argv, &argarg)) { + OPTS_OPTION_STR(OPTION_OUTPUT) = argarg; + opts_output_wasset = true; + } else { + con_out("Unknown parameter: %s\n", argv[0]); + return false; + } + } + break; + + default: + con_out("Unknown parameter: %s\n", argv[0]); + return false; + } + } + else + { + /* it's a QC filename */ + item.filename = argv[0]; + item.type = TYPE_QC; + vec_push(items, item); + } + } + opts_ini_init(config); + return true; +} + +/* returns the line number, or -1 on error */ +static bool progs_nextline(char **out, size_t *alen,FILE *src) { + int len; + char *line; + char *start; + char *end; + + line = *out; + len = fs_file_getline(&line, alen, src); + if (len == -1) + return false; + + /* start at first non-blank */ + for (start = line; isspace(*start); ++start) {} + /* end at the first non-blank */ + for (end = start; *end && !isspace(*end); ++end) {} + + *out = line; + /* move the actual filename to the beginning */ + while (start != end) { + *line++ = *start++; + } + *line = 0; + return true; +} + +int main(int argc, char **argv) { + size_t itr; + int retval = 0; + bool opts_output_free = false; + bool operators_free = false; + bool progs_src = false; + FILE *outfile = NULL; + struct parser_s *parser = NULL; + struct ftepp_s *ftepp = NULL; + + app_name = argv[0]; + con_init (); + opts_init("progs.dat", COMPILER_GMQCC, (1024 << 3)); + + util_seed(time(0)); + + if (!options_parse(argc, argv)) { + return usage(); + } + + if (OPTS_FLAG(TRUE_EMPTY_STRINGS) && OPTS_FLAG(FALSE_EMPTY_STRINGS)) { + con_err("-ftrue-empty-strings and -ffalse-empty-strings are mutually exclusive"); + exit(EXIT_FAILURE); + } + + /* the standard decides which set of operators to use */ + if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_GMQCC) { + operators = c_operators; + operator_count = c_operator_count; + } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_FTEQCC) { + operators = fte_operators; + operator_count = fte_operator_count; + } else { + operators = qcc_operators; + operator_count = qcc_operator_count; + } + + if (operators == fte_operators) { + /* fix ternary? */ + if (OPTS_FLAG(CORRECT_TERNARY)) { + oper_info *newops; + if (operators[operator_count-2].id != opid1(',') || + operators[operator_count-1].id != opid2(':','?')) + { + con_err("internal error: operator precedence table wasn't updated correctly!\n"); + exit(EXIT_FAILURE); + } + operators_free = true; + newops = (oper_info*)mem_a(sizeof(operators[0]) * operator_count); + memcpy(newops, operators, sizeof(operators[0]) * operator_count); + memcpy(&newops[operator_count-2], &operators[operator_count-1], sizeof(newops[0])); + memcpy(&newops[operator_count-1], &operators[operator_count-2], sizeof(newops[0])); + newops[operator_count-2].prec = newops[operator_count-1].prec+1; + operators = newops; + } + } + + if (OPTS_OPTION_BOOL(OPTION_DUMP)) { + for (itr = 0; itr < COUNT_FLAGS; ++itr) + con_out("Flag %s = %i\n", opts_flag_list[itr].name, OPTS_FLAG(itr)); + for (itr = 0; itr < COUNT_WARNINGS; ++itr) + con_out("Warning %s = %i\n", opts_warn_list[itr].name, OPTS_WARN(itr)); + + con_out("output = %s\n", OPTS_OPTION_STR(OPTION_OUTPUT)); + con_out("optimization level = %u\n", OPTS_OPTION_U32(OPTION_O)); + con_out("standard = %u\n", OPTS_OPTION_U32(OPTION_STANDARD)); + } + + if (OPTS_OPTION_BOOL(OPTION_PP_ONLY)) { + if (opts_output_wasset) { + outfile = fs_file_open(OPTS_OPTION_STR(OPTION_OUTPUT), "wb"); + if (!outfile) { + con_err("failed to open `%s` for writing\n", OPTS_OPTION_STR(OPTION_OUTPUT)); + retval = 1; + goto cleanup; + } + } + else { + outfile = con_default_out(); + } + } + + if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY)) { + if (!(parser = parser_create())) { + con_err("failed to initialize parser\n"); + retval = 1; + goto cleanup; + } + } + + if (OPTS_OPTION_BOOL(OPTION_PP_ONLY) || OPTS_FLAG(FTEPP)) { + if (!(ftepp = ftepp_create())) { + con_err("failed to initialize parser\n"); + retval = 1; + goto cleanup; + } + } + + util_debug("COM", "starting ...\n"); + + /* add macros */ + if (OPTS_OPTION_BOOL(OPTION_PP_ONLY) || OPTS_FLAG(FTEPP)) { + for (itr = 0; itr < vec_size(ppems); itr++) { + ftepp_add_macro(ftepp, ppems[itr].name, ppems[itr].value); + mem_d(ppems[itr].name); + + /* can be null */ + if (ppems[itr].value) + mem_d(ppems[itr].value); + } + } + + if (!vec_size(items)) { + FILE *src; + char *line; + size_t linelen = 0; + + progs_src = true; + + src = fs_file_open("progs.src", "rb"); + if (!src) { + con_err("failed to open `progs.src` for reading\n"); + retval = 1; + goto cleanup; + } + + line = NULL; + if (!progs_nextline(&line, &linelen, src) || !line[0]) { + con_err("illformatted progs.src file: expected output filename in first line\n"); + retval = 1; + goto srcdone; + } + + if (!opts_output_wasset) { + OPTS_OPTION_STR(OPTION_OUTPUT) = util_strdup(line); + opts_output_free = true; + } + + while (progs_nextline(&line, &linelen, src)) { + argitem item; + if (!line[0] || (line[0] == '/' && line[1] == '/')) + continue; + item.filename = util_strdup(line); + item.type = TYPE_QC; + vec_push(items, item); + } + +srcdone: + fs_file_close(src); + mem_d(line); + } + + if (retval) + goto cleanup; + + if (vec_size(items)) { + if (!OPTS_OPTION_BOOL(OPTION_QUIET) && + !OPTS_OPTION_BOOL(OPTION_PP_ONLY)) + { + con_out("Mode: %s\n", (progs_src ? "progs.src" : "manual")); + con_out("There are %lu items to compile:\n", (unsigned long)vec_size(items)); + } + + for (itr = 0; itr < vec_size(items); ++itr) { + if (!OPTS_OPTION_BOOL(OPTION_QUIET) && + !OPTS_OPTION_BOOL(OPTION_PP_ONLY)) + { + con_out(" item: %s (%s)\n", + items[itr].filename, + ( (items[itr].type == TYPE_QC ? "qc" : + (items[itr].type == TYPE_ASM ? "asm" : + (items[itr].type == TYPE_SRC ? "progs.src" : + ("unknown")))))); + } + + if (OPTS_OPTION_BOOL(OPTION_PP_ONLY)) { + const char *out; + if (!ftepp_preprocess_file(ftepp, items[itr].filename)) { + retval = 1; + goto cleanup; + } + out = ftepp_get(ftepp); + if (out) + fs_file_printf(outfile, "%s", out); + ftepp_flush(ftepp); + } + else { + if (OPTS_FLAG(FTEPP)) { + const char *data; + if (!ftepp_preprocess_file(ftepp, items[itr].filename)) { + retval = 1; + goto cleanup; + } + data = ftepp_get(ftepp); + if (vec_size(data)) { + if (!parser_compile_string(parser, items[itr].filename, data, vec_size(data))) { + retval = 1; + goto cleanup; + } + } + ftepp_flush(ftepp); + } + else { + if (!parser_compile_file(parser, items[itr].filename)) { + retval = 1; + goto cleanup; + } + } + } + + if (progs_src) { + mem_d(items[itr].filename); + items[itr].filename = NULL; + } + } + + ftepp_finish(ftepp); + ftepp = NULL; + if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY)) { + if (!parser_finish(parser, OPTS_OPTION_STR(OPTION_OUTPUT))) { + retval = 1; + goto cleanup; + } + } + } + + /* stuff */ + if (!OPTS_OPTION_BOOL(OPTION_QUIET) && + !OPTS_OPTION_BOOL(OPTION_PP_ONLY)) + { + for (itr = 0; itr < COUNT_OPTIMIZATIONS; ++itr) { + if (opts_optimizationcount[itr]) { + con_out("%s: %u\n", opts_opt_list[itr].name, (unsigned int)opts_optimizationcount[itr]); + } + } + } + +cleanup: + util_debug("COM", "cleaning ...\n"); + if (ftepp) + ftepp_finish(ftepp); + con_close(); + vec_free(items); + vec_free(ppems); + + if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY)) + parser_cleanup(parser); + if (opts_output_free) + mem_d(OPTS_OPTION_STR(OPTION_OUTPUT)); + if (operators_free) + mem_d((void*)operators); + + lex_cleanup(); + stat_info(); + + return retval; +} diff --git a/misc/source/gmqcc-src/misc/check-doc.sh b/misc/source/gmqcc-src/misc/check-doc.sh new file mode 100644 index 00000000..73b169a1 --- /dev/null +++ b/misc/source/gmqcc-src/misc/check-doc.sh @@ -0,0 +1,43 @@ +#!/bin/sh +prog=$0 + +die() { + echo "$@" + exit 1 +} + +want() { + test -e "$1" && return + echo "$prog: missing $1" + echo "$prog: run this script from the top of a gmqcc source tree" + exit 1 +} + +for i in opts.def \ + doc/gmqcc.1 \ + gmqcc.ini.example +do want "$i"; done + +# y/_ABCDEFGHIJKLMNOPQRSTUVWXYZ/-abcdefghijklmnopqrstuvwxyz/; +check_opt() { + opt_def_name=$1 + arg_char=$2 + + for i in $(sed -ne \ + '/^#ifdef GMQCC_TYPE_'${opt_def_name}'$/,/^#endif/{ + /GMQCC_DEFINE_FLAG/{ + s/^.*GMQCC_DEFINE_FLAG(\([^,)]*\)[),].*$/\1/;p; + } + }' opts.def) + do + opt=$(echo "$i" | tr -- '_A-Z' '-a-z') + grep -qF -- ".It Fl "${arg_char}" Ns Cm $opt" \ + doc/gmqcc.1 || echo "doc/gmqcc.1: missing: -${arg_char}$opt" + grep -q -- "[^a-zA-Z_]$i[^a-zA-Z_]" \ + gmqcc.ini.example || echo "gmqcc.ini.example: missing: $i" + done +} + +check_opt FLAGS f +check_opt WARNS W +check_opt OPTIMIZATIONS O diff --git a/misc/source/gmqcc-src/msvc/gmqcc.sln b/misc/source/gmqcc-src/msvc/gmqcc.sln new file mode 100644 index 00000000..39daed86 --- /dev/null +++ b/misc/source/gmqcc-src/msvc/gmqcc.sln @@ -0,0 +1,38 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gmqcc", "gmqcc\gmqcc.vcxproj", "{A6BD74E1-31BB-4D00-A9E0-09FF1BC76ED6}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qcvm", "qcvm\qcvm.vcxproj", "{DC980E20-C7A8-4112-A517-631DBDA788E7}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pak", "pak\pak.vcxproj", "{A6F66BE9-57EF-4E93-AA9D-6E0C8B0990AD}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testsuite", "testsuite\testsuite.vcxproj", "{7E2839D9-9C1A-4489-9FF9-FDC854EBED3D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A6BD74E1-31BB-4D00-A9E0-09FF1BC76ED6}.Debug|Win32.ActiveCfg = Debug|Win32 + {A6BD74E1-31BB-4D00-A9E0-09FF1BC76ED6}.Debug|Win32.Build.0 = Debug|Win32 + {A6BD74E1-31BB-4D00-A9E0-09FF1BC76ED6}.Release|Win32.ActiveCfg = Release|Win32 + {A6BD74E1-31BB-4D00-A9E0-09FF1BC76ED6}.Release|Win32.Build.0 = Release|Win32 + {DC980E20-C7A8-4112-A517-631DBDA788E7}.Debug|Win32.ActiveCfg = Debug|Win32 + {DC980E20-C7A8-4112-A517-631DBDA788E7}.Debug|Win32.Build.0 = Debug|Win32 + {DC980E20-C7A8-4112-A517-631DBDA788E7}.Release|Win32.ActiveCfg = Release|Win32 + {DC980E20-C7A8-4112-A517-631DBDA788E7}.Release|Win32.Build.0 = Release|Win32 + {A6F66BE9-57EF-4E93-AA9D-6E0C8B0990AD}.Debug|Win32.ActiveCfg = Debug|Win32 + {A6F66BE9-57EF-4E93-AA9D-6E0C8B0990AD}.Debug|Win32.Build.0 = Debug|Win32 + {A6F66BE9-57EF-4E93-AA9D-6E0C8B0990AD}.Release|Win32.ActiveCfg = Release|Win32 + {A6F66BE9-57EF-4E93-AA9D-6E0C8B0990AD}.Release|Win32.Build.0 = Release|Win32 + {7E2839D9-9C1A-4489-9FF9-FDC854EBED3D}.Debug|Win32.ActiveCfg = Debug|Win32 + {7E2839D9-9C1A-4489-9FF9-FDC854EBED3D}.Debug|Win32.Build.0 = Debug|Win32 + {7E2839D9-9C1A-4489-9FF9-FDC854EBED3D}.Release|Win32.ActiveCfg = Release|Win32 + {7E2839D9-9C1A-4489-9FF9-FDC854EBED3D}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/misc/source/gmqcc-src/msvc/pak/pak.vcxproj b/misc/source/gmqcc-src/msvc/pak/pak.vcxproj new file mode 100644 index 00000000..ab075a90 --- /dev/null +++ b/misc/source/gmqcc-src/msvc/pak/pak.vcxproj @@ -0,0 +1,78 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {A6F66BE9-57EF-4E93-AA9D-6E0C8B0990AD} + pak + + + + Application + true + MultiByte + + + Application + false + true + MultiByte + + + + + + + + + + + + + + + Level3 + Disabled + + + true + + + + + Level3 + MaxSpeed + true + true + + + true + true + true + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/misc/source/gmqcc-src/msvc/pak/pak.vcxproj.filters b/misc/source/gmqcc-src/msvc/pak/pak.vcxproj.filters new file mode 100644 index 00000000..745f4ce6 --- /dev/null +++ b/misc/source/gmqcc-src/msvc/pak/pak.vcxproj.filters @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/misc/source/gmqcc-src/opts.c b/misc/source/gmqcc-src/opts.c new file mode 100644 index 00000000..e6ca420b --- /dev/null +++ b/misc/source/gmqcc-src/opts.c @@ -0,0 +1,374 @@ +/* + * Copyright (C) 2012, 2013 + * Wolfgang Bumiller + * Dale Weiler + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include +#include +#include + +#include "gmqcc.h" + +unsigned int opts_optimizationcount[COUNT_OPTIMIZATIONS]; +opts_cmd_t opts; /* command lien options */ + +static void opts_setdefault(void) { + memset(&opts, 0, sizeof(opts_cmd_t)); + OPTS_OPTION_BOOL(OPTION_CORRECTION) = true; + + /* warnings */ + opts_set(opts.warn, WARN_UNUSED_VARIABLE, true); + opts_set(opts.warn, WARN_USED_UNINITIALIZED, true); + opts_set(opts.warn, WARN_UNKNOWN_CONTROL_SEQUENCE, true); + opts_set(opts.warn, WARN_EXTENSIONS, true); + opts_set(opts.warn, WARN_FIELD_REDECLARED, true); + opts_set(opts.warn, WARN_MISSING_RETURN_VALUES, true); + opts_set(opts.warn, WARN_INVALID_PARAMETER_COUNT, true); + opts_set(opts.warn, WARN_LOCAL_CONSTANTS, true); + opts_set(opts.warn, WARN_VOID_VARIABLES, true); + opts_set(opts.warn, WARN_IMPLICIT_FUNCTION_POINTER, true); + opts_set(opts.warn, WARN_VARIADIC_FUNCTION, true); + opts_set(opts.warn, WARN_FRAME_MACROS, true); + opts_set(opts.warn, WARN_EFFECTLESS_STATEMENT, true); + opts_set(opts.warn, WARN_END_SYS_FIELDS, true); + opts_set(opts.warn, WARN_ASSIGN_FUNCTION_TYPES, true); + opts_set(opts.warn, WARN_CPP, true); + opts_set(opts.warn, WARN_MULTIFILE_IF, true); + opts_set(opts.warn, WARN_DOUBLE_DECLARATION, true); + opts_set(opts.warn, WARN_CONST_VAR, true); + opts_set(opts.warn, WARN_MULTIBYTE_CHARACTER, true); + opts_set(opts.warn, WARN_UNKNOWN_PRAGMAS, true); + opts_set(opts.warn, WARN_UNREACHABLE_CODE, true); + opts_set(opts.warn, WARN_UNKNOWN_ATTRIBUTE, true); + opts_set(opts.warn, WARN_RESERVED_NAMES, true); + opts_set(opts.warn, WARN_UNINITIALIZED_CONSTANT, true); + opts_set(opts.warn, WARN_DEPRECATED, true); + opts_set(opts.warn, WARN_PARENTHESIS, true); + + /* flags */ + opts_set(opts.flags, ADJUST_VECTOR_FIELDS, true); + opts_set(opts.flags, CORRECT_TERNARY, true); + opts_set(opts.flags, BAIL_ON_WERROR, true); + opts_set(opts.flags, LEGACY_VECTOR_MATHS, true); + opts_set(opts.flags, DARKPLACES_STRING_TABLE_BUG, true); + +} + +void opts_backup_non_Wall() { + size_t i; + for (i = 0; i <= WARN_DEBUG; ++i) + opts_set(opts.warn_backup, i, OPTS_WARN(i)); +} + +void opts_restore_non_Wall() { + size_t i; + for (i = 0; i <= WARN_DEBUG; ++i) + opts_set(opts.warn, i, OPTS_GENERIC(opts.warn_backup, i)); +} + +void opts_backup_non_Werror_all() { + size_t i; + for (i = 0; i <= WARN_DEBUG; ++i) + opts_set(opts.werror_backup, i, OPTS_WERROR(i)); +} + +void opts_restore_non_Werror_all() { + size_t i; + for (i = 0; i <= WARN_DEBUG; ++i) + opts_set(opts.werror, i, OPTS_GENERIC(opts.werror_backup, i)); +} + +void opts_init(const char *output, int standard, size_t arraysize) { + opts_setdefault(); + + OPTS_OPTION_STR(OPTION_OUTPUT) = (char*)output; + OPTS_OPTION_U32(OPTION_STANDARD) = standard; + OPTS_OPTION_U32(OPTION_MAX_ARRAY_SIZE) = arraysize; + OPTS_OPTION_U16(OPTION_MEMDUMPCOLS) = 16; +} + +static bool opts_setflag_all(const char *name, bool on, uint32_t *flags, const opts_flag_def *list, size_t listsize) { + size_t i; + + for (i = 0; i < listsize; ++i) { + if (!strcmp(name, list[i].name)) { + longbit lb = list[i].bit; + + if (on) + flags[lb.idx] |= (1<<(lb.bit)); + else + flags[lb.idx] &= ~(1<<(lb.bit)); + + return true; + } + } + return false; +} +bool opts_setflag (const char *name, bool on) { + return opts_setflag_all(name, on, opts.flags, opts_flag_list, COUNT_FLAGS); +} +bool opts_setwarn (const char *name, bool on) { + return opts_setflag_all(name, on, opts.warn, opts_warn_list, COUNT_WARNINGS); +} +bool opts_setwerror(const char *name, bool on) { + return opts_setflag_all(name, on, opts.werror, opts_warn_list, COUNT_WARNINGS); +} +bool opts_setoptim (const char *name, bool on) { + return opts_setflag_all(name, on, opts.optimization, opts_opt_list, COUNT_OPTIMIZATIONS); +} + +void opts_set(uint32_t *flags, size_t idx, bool on) { + longbit lb; + LONGBIT_SET(lb, idx); + + if (on) + flags[lb.idx] |= (1<<(lb.bit)); + else + flags[lb.idx] &= ~(1<<(lb.bit)); +} + +void opts_setoptimlevel(unsigned int level) { + size_t i; + for (i = 0; i < COUNT_OPTIMIZATIONS; ++i) + opts_set(opts.optimization, i, level >= opts_opt_oflag[i]); + + if (!level) + opts.optimizeoff = true; +} + +/* + * Standard configuration parser and subsystem. Yes, optionally you may + * create ini files or cfg (the driver accepts both) for a project opposed + * to supplying just a progs.src (since you also may need to supply command + * line arguments or set the options of the compiler) [which cannot be done + * from a progs.src. + */ +static char *opts_ini_rstrip(char *s) { + char *p = s + strlen(s); + while(p > s && isspace(*--p)) + *p = '\0'; + return s; +} + +static char *opts_ini_lskip(const char *s) { + while (*s && isspace(*s)) + s++; + return (char*)s; +} + +static char *opts_ini_next(const char *s, char c) { + bool last = false; + while (*s && *s != c && !(last && *s == ';')) + last = !!isspace(*s), s++; + + return (char*)s; +} + +static size_t opts_ini_parse ( + FILE *filehandle, + char *(*loadhandle)(const char *, const char *, const char *), + char **errorhandle +) { + size_t linesize; + size_t lineno = 1; + size_t error = 0; + char *line = NULL; + char section_data[2048] = ""; + char oldname_data[2048] = ""; + + /* parsing and reading variables */ + char *parse_beg; + char *parse_end; + char *read_name; + char *read_value; + + while (fs_file_getline(&line, &linesize, filehandle) != EOF) { + parse_beg = line; + + /* handle BOM */ + if (lineno == 1 && ( + (unsigned char)parse_beg[0] == 0xEF && + (unsigned char)parse_beg[1] == 0xBB && + (unsigned char)parse_beg[2] == 0xBF + ) + ) { + parse_beg ++; /* 0xEF */ + parse_beg ++; /* 0xBB */ + parse_beg ++; /* 0xBF */ + } + + if (*(parse_beg = opts_ini_lskip(opts_ini_rstrip(parse_beg))) == ';' || *parse_beg == '#') { + /* ignore '#' is a perl extension */ + } else if (*parse_beg == '[') { + /* section found */ + if (*(parse_end = opts_ini_next(parse_beg + 1, ']')) == ']') { + * parse_end = '\0'; /* terminate bro */ + util_strncpy(section_data, parse_beg + 1, sizeof(section_data)); + section_data[sizeof(section_data) - 1] = '\0'; + *oldname_data = '\0'; + } else if (!error) { + /* otherwise set error to the current line number */ + error = lineno; + } + } else if (*parse_beg && *parse_beg != ';') { + /* not a comment, must be a name value pair :) */ + if (*(parse_end = opts_ini_next(parse_beg, '=')) != '=') + parse_end = opts_ini_next(parse_beg, ':'); + + if (*parse_end == '=' || *parse_end == ':') { + *parse_end = '\0'; /* terminate bro */ + read_name = opts_ini_rstrip(parse_beg); + read_value = opts_ini_lskip(parse_end + 1); + if (*(parse_end = opts_ini_next(read_value, '\0')) == ';') + * parse_end = '\0'; + opts_ini_rstrip(read_value); + + /* valid name value pair, lets call down to handler */ + util_strncpy(oldname_data, read_name, sizeof(oldname_data)); + oldname_data[sizeof(oldname_data) - 1] ='\0'; + + if ((*errorhandle = loadhandle(section_data, read_name, read_value)) && !error) + error = lineno; + } else if (!error) { + /* otherwise set error to the current line number */ + error = lineno; + } + } + lineno++; + } + mem_d(line); + return error; +} + +/* + * returns true/false for a char that contains ("true" or "false" or numeric 0/1) + */ +static bool opts_ini_bool(const char *value) { + if (!strcmp(value, "true")) return true; + if (!strcmp(value, "false")) return false; + return !!strtol(value, NULL, 10); +} + +static char *opts_ini_load(const char *section, const char *name, const char *value) { + char *error = NULL; + bool found = false; + + /* + * undef all of these because they may still be defined like in my + * case they where. + */ + #undef GMQCC_TYPE_FLAGS + #undef GMQCC_TYPE_OPTIMIZATIONS + #undef GMQCC_TYPE_WARNS + + /* flags */ + #define GMQCC_TYPE_FLAGS + #define GMQCC_DEFINE_FLAG(X) \ + if (!strcmp(section, "flags") && !strcmp(name, #X)) { \ + opts_set(opts.flags, X, opts_ini_bool(value)); \ + found = true; \ + } + #include "opts.def" + + /* warnings */ + #define GMQCC_TYPE_WARNS + #define GMQCC_DEFINE_FLAG(X) \ + if (!strcmp(section, "warnings") && !strcmp(name, #X)) { \ + opts_set(opts.warn, WARN_##X, opts_ini_bool(value)); \ + found = true; \ + } + #include "opts.def" + + /* Werror-individuals */ + #define GMQCC_TYPE_WARNS + #define GMQCC_DEFINE_FLAG(X) \ + if (!strcmp(section, "errors") && !strcmp(name, #X)) { \ + opts_set(opts.werror, WARN_##X, opts_ini_bool(value)); \ + found = true; \ + } + #include "opts.def" + + /* optimizations */ + #define GMQCC_TYPE_OPTIMIZATIONS + #define GMQCC_DEFINE_FLAG(X,Y) \ + if (!strcmp(section, "optimizations") && !strcmp(name, #X)) { \ + opts_set(opts.optimization, OPTIM_##X, opts_ini_bool(value)); \ + found = true; \ + } + #include "opts.def" + + /* nothing was found ever! */ + if (!found) { + if (strcmp(section, "flags") && + strcmp(section, "warnings") && + strcmp(section, "optimizations")) + { + vec_upload(error, "invalid section `", 17); + vec_upload(error, section, strlen(section)); + vec_push (error, '`'); + vec_push (error, '\0'); + } else { + vec_upload(error, "invalid variable `", 18); + vec_upload(error, name, strlen(name)); + vec_push (error, '`'); + vec_upload(error, " in section: `", 14); + vec_upload(error, section, strlen(section)); + vec_push (error, '`'); + vec_push (error, '\0'); + } + } + return error; +} + +/* + * Actual loading subsystem, this finds the ini or cfg file, and properly + * loads it and executes it to set compiler options. + */ +void opts_ini_init(const char *file) { + /* + * Possible matches are: + * gmqcc.ini + * gmqcc.cfg + */ + char *error; + size_t line; + FILE *ini; + + + if (!file) { + /* try ini */ + if (!(ini = fs_file_open((file = "gmqcc.ini"), "r"))) + /* try cfg */ + if (!(ini = fs_file_open((file = "gmqcc.cfg"), "r"))) + return; + } else if (!(ini = fs_file_open(file, "r"))) + return; + + con_out("found ini file `%s`\n", file); + + if ((line = opts_ini_parse(ini, &opts_ini_load, &error)) != 0) { + /* there was a parse error with the ini file */ + con_printmsg(LVL_ERROR, file, line, 0 /*TODO: column for ini error*/, "error", error); + vec_free(error); + } + + fs_file_close(ini); +} diff --git a/misc/source/gmqcc-src/opts.def b/misc/source/gmqcc-src/opts.def new file mode 100644 index 00000000..77a40941 --- /dev/null +++ b/misc/source/gmqcc-src/opts.def @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2012, 2013 + * Wolfgang Bumiller + * Dale Weiler + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef GMQCC_DEFINE_FLAG +# error "bad opts.def usage" +#endif + +/* codegen flags */ +#ifdef GMQCC_TYPE_FLAGS + GMQCC_DEFINE_FLAG(DARKPLACES_STRING_TABLE_BUG) + GMQCC_DEFINE_FLAG(ADJUST_VECTOR_FIELDS) + GMQCC_DEFINE_FLAG(FTEPP) + GMQCC_DEFINE_FLAG(FTEPP_PREDEFS) + GMQCC_DEFINE_FLAG(RELAXED_SWITCH) + GMQCC_DEFINE_FLAG(SHORT_LOGIC) + GMQCC_DEFINE_FLAG(PERL_LOGIC) + GMQCC_DEFINE_FLAG(TRANSLATABLE_STRINGS) + GMQCC_DEFINE_FLAG(INITIALIZED_NONCONSTANTS) + GMQCC_DEFINE_FLAG(ASSIGN_FUNCTION_TYPES) + GMQCC_DEFINE_FLAG(LNO) + GMQCC_DEFINE_FLAG(CORRECT_TERNARY) + GMQCC_DEFINE_FLAG(SINGLE_VECTOR_DEFS) + GMQCC_DEFINE_FLAG(CORRECT_LOGIC) + GMQCC_DEFINE_FLAG(TRUE_EMPTY_STRINGS) + GMQCC_DEFINE_FLAG(FALSE_EMPTY_STRINGS) + GMQCC_DEFINE_FLAG(UTF8) + GMQCC_DEFINE_FLAG(BAIL_ON_WERROR) + GMQCC_DEFINE_FLAG(LOOP_LABELS) + GMQCC_DEFINE_FLAG(UNTYPED_NIL) + GMQCC_DEFINE_FLAG(PERMISSIVE) + GMQCC_DEFINE_FLAG(VARIADIC_ARGS) + GMQCC_DEFINE_FLAG(LEGACY_VECTOR_MATHS) + GMQCC_DEFINE_FLAG(EXPRESSIONS_FOR_BUILTINS) + GMQCC_DEFINE_FLAG(RETURN_ASSIGNMENTS) +#endif + +/* warning flags */ +#ifdef GMQCC_TYPE_WARNS + GMQCC_DEFINE_FLAG(UNINITIALIZED_GLOBAL) + GMQCC_DEFINE_FLAG(DEBUG) + GMQCC_DEFINE_FLAG(UNUSED_VARIABLE) + GMQCC_DEFINE_FLAG(USED_UNINITIALIZED) + GMQCC_DEFINE_FLAG(UNKNOWN_CONTROL_SEQUENCE) + GMQCC_DEFINE_FLAG(EXTENSIONS) + GMQCC_DEFINE_FLAG(FIELD_REDECLARED) + GMQCC_DEFINE_FLAG(MISSING_RETURN_VALUES) + GMQCC_DEFINE_FLAG(INVALID_PARAMETER_COUNT) + GMQCC_DEFINE_FLAG(LOCAL_SHADOWS) + GMQCC_DEFINE_FLAG(LOCAL_CONSTANTS) + GMQCC_DEFINE_FLAG(VOID_VARIABLES) + GMQCC_DEFINE_FLAG(IMPLICIT_FUNCTION_POINTER) + GMQCC_DEFINE_FLAG(VARIADIC_FUNCTION) + GMQCC_DEFINE_FLAG(FRAME_MACROS) + GMQCC_DEFINE_FLAG(EFFECTLESS_STATEMENT) + GMQCC_DEFINE_FLAG(END_SYS_FIELDS) + GMQCC_DEFINE_FLAG(ASSIGN_FUNCTION_TYPES) + GMQCC_DEFINE_FLAG(CPP) + GMQCC_DEFINE_FLAG(MULTIFILE_IF) + GMQCC_DEFINE_FLAG(DOUBLE_DECLARATION) + GMQCC_DEFINE_FLAG(CONST_VAR) + GMQCC_DEFINE_FLAG(MULTIBYTE_CHARACTER) + GMQCC_DEFINE_FLAG(TERNARY_PRECEDENCE) + GMQCC_DEFINE_FLAG(UNKNOWN_PRAGMAS) + GMQCC_DEFINE_FLAG(UNREACHABLE_CODE) + GMQCC_DEFINE_FLAG(UNKNOWN_ATTRIBUTE) + GMQCC_DEFINE_FLAG(RESERVED_NAMES) + GMQCC_DEFINE_FLAG(UNINITIALIZED_CONSTANT) + GMQCC_DEFINE_FLAG(DIFFERENT_QUALIFIERS) + GMQCC_DEFINE_FLAG(DIFFERENT_ATTRIBUTES) + GMQCC_DEFINE_FLAG(DEPRECATED) + GMQCC_DEFINE_FLAG(PARENTHESIS) +#endif + +#ifdef GMQCC_TYPE_OPTIMIZATIONS + GMQCC_DEFINE_FLAG(PEEPHOLE, 1) + GMQCC_DEFINE_FLAG(TAIL_RECURSION, 1) + GMQCC_DEFINE_FLAG(OVERLAP_LOCALS, 3) + GMQCC_DEFINE_FLAG(LOCAL_TEMPS, 3) + GMQCC_DEFINE_FLAG(GLOBAL_TEMPS, 3) + GMQCC_DEFINE_FLAG(STRIP_CONSTANT_NAMES, 1) + GMQCC_DEFINE_FLAG(OVERLAP_STRINGS, 2) + GMQCC_DEFINE_FLAG(CALL_STORES, 3) + GMQCC_DEFINE_FLAG(VOID_RETURN, 1) + GMQCC_DEFINE_FLAG(VECTOR_COMPONENTS, 1) +#endif + +#ifdef GMQCC_TYPE_OPTIONS + GMQCC_DEFINE_FLAG(O) + GMQCC_DEFINE_FLAG(OUTPUT) + GMQCC_DEFINE_FLAG(QUIET) + GMQCC_DEFINE_FLAG(G) + GMQCC_DEFINE_FLAG(STANDARD) + GMQCC_DEFINE_FLAG(DEBUG) + GMQCC_DEFINE_FLAG(MEMDUMPCOLS) + GMQCC_DEFINE_FLAG(MEMCHK) + GMQCC_DEFINE_FLAG(DUMPFIN) + GMQCC_DEFINE_FLAG(DUMP) + GMQCC_DEFINE_FLAG(FORCECRC) + GMQCC_DEFINE_FLAG(FORCED_CRC) + GMQCC_DEFINE_FLAG(PP_ONLY) + GMQCC_DEFINE_FLAG(MAX_ARRAY_SIZE) + GMQCC_DEFINE_FLAG(ADD_INFO) + GMQCC_DEFINE_FLAG(CORRECTION) + GMQCC_DEFINE_FLAG(STATISTICS) +#endif + +/* some cleanup so we don't have to */ +#undef GMQCC_TYPE_FLAGS +#undef GMQCC_TYPE_WARNS +#undef GMQCC_TYPE_OPTIONS +#undef GMQCC_TYPE_OPTIMIZATIONS +#undef GMQCC_DEFINE_FLAG diff --git a/misc/source/gmqcc-src/pak.c b/misc/source/gmqcc-src/pak.c new file mode 100644 index 00000000..2371ee11 --- /dev/null +++ b/misc/source/gmqcc-src/pak.c @@ -0,0 +1,584 @@ +/* + * Copyright (C) 2013 + * Dale Weiler + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include +#include + +#include "gmqcc.h" + +/* + * The PAK format uses a FOURCC concept for storing the magic ident within + * the header as a uint32_t. + */ +#define PAK_FOURCC ((uint32_t)(((uint8_t)'P'|((uint8_t)'A'<<8)|((uint8_t)'C'<<16)|((uint8_t)'K'<<24)))) + +typedef struct { + uint32_t magic; /* "PACK" */ + + /* + * Offset to first directory entry in PAK file. It's often + * best to store the directories at the end of the file opposed + * to the front, since it allows easy insertion without having + * to load the entire file into memory again. + */ + uint32_t diroff; + uint32_t dirlen; +} pak_header_t; + +/* + * A directory, is sort of a "file entry". The concept of + * a directory in Quake world is a "file entry/record". This + * describes a file (with directories/nested ones too in it's + * file name). Hence it can be a file, file with directory, or + * file with directories. + */ +typedef struct { + char name[56]; + uint32_t pos; + uint32_t len; +} pak_directory_t; + +/* + * Used to get the next token from a string, where the + * strings themselfs are seperated by chracters from + * `sep`. This is essentially strsep. + */ +static char *pak_tree_sep(char **str, const char *sep) { + char *beg = *str; + char *end; + + if (!beg) + return NULL; + + if (*(end = beg + strcspn(beg, sep))) + * end++ = '\0'; /* null terminate */ + else + end = 0; + + *str = end; + return beg; +} + +/* + * When given a string like "a/b/c/d/e/file" + * this function will handle the creation of + * the directory structure, included nested + * directories. + */ +static void pak_tree_build(const char *entry) { + char *directory; + char *elements[28]; + char *pathsplit; + char *token; + + size_t itr; + size_t jtr; + + pathsplit = (char *)mem_a(56); + directory = (char *)mem_a(56); + + memset(pathsplit, 0, 56); + + util_strncpy(directory, entry, 56); + for (itr = 0; (token = pak_tree_sep(&directory, "/")) != NULL; itr++) { + elements[itr] = token; + } + + for (jtr = 0; jtr < itr - 1; jtr++) { + util_strcat(pathsplit, elements[jtr]); + util_strcat(pathsplit, "/"); + + if (fs_dir_make(pathsplit)) { + mem_d(pathsplit); + mem_d(directory); + + /* TODO: undo on fail */ + + return; + } + } + + mem_d(pathsplit); + mem_d(directory); +} + +typedef struct { + pak_directory_t *directories; + pak_header_t header; + FILE *handle; + bool insert; +} pak_file_t; + +static pak_file_t *pak_open_read(const char *file) { + pak_file_t *pak; + size_t itr; + + if (!(pak = (pak_file_t*)mem_a(sizeof(pak_file_t)))) + return NULL; + + if (!(pak->handle = fs_file_open(file, "rb"))) { + mem_d(pak); + return NULL; + } + + pak->directories = NULL; + pak->insert = false; /* read doesn't allow insert */ + + memset (&pak->header, 0, sizeof(pak_header_t)); + fs_file_read (&pak->header, sizeof(pak_header_t), 1, pak->handle); + util_endianswap(&pak->header, 1, sizeof(pak_header_t)); + + /* + * Every PAK file has "PACK" stored as FOURCC data in the + * header. If this data cannot compare (as checked here), it's + * probably not a PAK file. + */ + if (pak->header.magic != PAK_FOURCC) { + fs_file_close(pak->handle); + mem_d (pak); + return NULL; + } + + /* + * Time to read in the directory handles and prepare the directories + * vector. We're going to be reading some the file inwards soon. + */ + fs_file_seek(pak->handle, pak->header.diroff, SEEK_SET); + + /* + * Read in all directories from the PAK file. These are considered + * to be the "file entries". + */ + for (itr = 0; itr < pak->header.dirlen / 64; itr++) { + pak_directory_t dir; + fs_file_read (&dir, sizeof(pak_directory_t), 1, pak->handle); + util_endianswap(&dir, 1, sizeof(pak_directory_t)); + + vec_push(pak->directories, dir); + } + return pak; +} + +static pak_file_t *pak_open_write(const char *file) { + pak_file_t *pak; + + if (!(pak = (pak_file_t*)mem_a(sizeof(pak_file_t)))) + return NULL; + + /* + * Generate the required directory structure / tree for + * writing this PAK file too. + */ + pak_tree_build(file); + + if (!(pak->handle = fs_file_open(file, "wb"))) { + /* + * The directory tree that was created, needs to be + * removed entierly if we failed to open a file. + */ + /* TODO backup directory clean */ + + mem_d(pak); + return NULL; + } + + memset(&(pak->header), 0, sizeof(pak_header_t)); + + /* + * We're in "insert" mode, we need to do things like header + * "patching" and writing the directories at the end of the + * file. + */ + pak->insert = true; + pak->header.magic = PAK_FOURCC; + + /* on BE systems we need to swap the byte order of the FOURCC */ + util_endianswap(&pak->header.magic, 1, sizeof(uint32_t)); + + /* + * We need to write out the header since files will be wrote out to + * this even with directory entries, and that not wrote. The header + * will need to be patched in later with a file_seek, and overwrite, + * we could use offsets and other trickery. This is just easier. + */ + fs_file_write(&(pak->header), sizeof(pak_header_t), 1, pak->handle); + + return pak; +} + +static pak_file_t *pak_open(const char *file, const char *mode) { + if (!file || !mode) + return NULL; + + switch (*mode) { + case 'r': return pak_open_read (file); + case 'w': return pak_open_write(file); + } + + return NULL; +} + +static bool pak_exists(pak_file_t *pak, const char *file, pak_directory_t **dir) { + size_t itr; + + if (!pak || !file) + return false; + + for (itr = 0; itr < vec_size(pak->directories); itr++) { + if (!strcmp(pak->directories[itr].name, file)) { + /* + * Store back a pointer to the directory that matches + * the request if requested (NULL is not allowed). + */ + if (dir) { + *dir = &(pak->directories[itr]); + } + return true; + } + } + + return false; +} + +/* + * Extraction abilities. These work as you expect them to. + */ +static bool pak_extract_one(pak_file_t *pak, const char *file, const char *outdir) { + pak_directory_t *dir = NULL; + unsigned char *dat = NULL; + char *local = NULL; + FILE *out; + + if (!pak_exists(pak, file, &dir)) { + return false; + } + + if (!(dat = (unsigned char *)mem_a(dir->len))) { + return false; + } + + /* + * Generate the directory structure / tree that will be required + * to store the extracted file. + */ + pak_tree_build(file); + + /* TODO portable path seperators */ + util_asprintf(&local, "%s/%s", outdir, file); + + /* + * Now create the file, if this operation fails. Then abort + * It shouldn't fail though. + */ + if (!(out = fs_file_open(local, "wb"))) { + mem_d(dat); + return false; + } + + /* free memory for directory string */ + mem_d(local); + + /* read */ + fs_file_seek (pak->handle, dir->pos, SEEK_SET); + fs_file_read (dat, 1, dir->len, pak->handle); + + /* write */ + fs_file_write(dat, 1, dir->len, out); + + /* close */ + fs_file_close(out); + + /* free */ + mem_d(dat); + + return true; +} + +static bool pak_extract_all(pak_file_t *pak, const char *dir) { + size_t itr; + + if (!fs_dir_make(dir)) + return false; + + for (itr = 0; itr < vec_size(pak->directories); itr++) { + if (!pak_extract_one(pak, pak->directories[itr].name, dir)) + return false; + } + + return true; +} + +/* + * Insertion functions (the opposite of extraction). Yes for generating + * PAKs. + */ +static bool pak_insert_one(pak_file_t *pak, const char *file) { + pak_directory_t dir; + unsigned char *dat; + FILE *fp; + + /* + * We don't allow insertion on files that already exist within the + * pak file. Weird shit can happen if we allow that ;). We also + * don't allow insertion if the pak isn't opened in write mode. + */ + if (!pak || !file || !pak->insert || pak_exists(pak, file, NULL)) + return false; + + if (!(fp = fs_file_open(file, "rb"))) + return false; + + /* + * Calculate the total file length, since it will be wrote to + * the directory entry, and the actual contents of the file + * to the PAK file itself. + */ + fs_file_seek(fp, 0, SEEK_END); + dir.len = fs_file_tell(fp); + fs_file_seek(fp, 0, SEEK_SET); + + dir.pos = fs_file_tell(pak->handle); + + /* + * We're limited to 56 bytes for a file name string, that INCLUDES + * the directory and '/' seperators. + */ + if (strlen(file) >= 56) { + fs_file_close(fp); + return false; + } + + util_strncpy(dir.name, file, strlen(file)); + + /* + * Allocate some memory for loading in the data that will be + * redirected into the PAK file. + */ + if (!(dat = (unsigned char *)mem_a(dir.len))) { + fs_file_close(fp); + return false; + } + + fs_file_read (dat, dir.len, 1, fp); + fs_file_close(fp); + fs_file_write(dat, dir.len, 1, pak->handle); + + /* + * Now add the directory to the directories vector, so pak_close + * can actually write it. + */ + vec_push(pak->directories, dir); + + return true; +} + +/* + * Like pak_insert_one, except this collects files in all directories + * from a root directory, and inserts them all. + */ +bool pak_insert_all(pak_file_t *pak, const char *dir) { + DIR *dp; + struct dirent *dirp; + + if (!(pak->insert)) + return false; + + if (!(dp = fs_dir_open(dir))) + return false; + + while ((dirp = fs_dir_read(dp))) { + if (!(pak_insert_one(pak, dirp->d_name))) { + fs_dir_close(dp); + return false; + } + } + + fs_dir_close(dp); + return true; +} + +static bool pak_close(pak_file_t *pak) { + size_t itr; + + if (!pak) + return false; + + /* + * In insert mode we need to patch the header, and write + * our directory entries at the end of the file. + */ + if (pak->insert) { + pak->header.dirlen = vec_size(pak->directories) * 64; + pak->header.diroff = ftell(pak->handle); + + /* patch header */ + fs_file_seek (pak->handle, 0, SEEK_SET); + fs_file_write(&(pak->header), sizeof(pak_header_t), 1, pak->handle); + + /* write directories */ + fs_file_seek (pak->handle, pak->header.diroff, SEEK_SET); + + for (itr = 0; itr < vec_size(pak->directories); itr++) { + fs_file_write(&(pak->directories[itr]), sizeof(pak_directory_t), 1, pak->handle); + } + } + + vec_free (pak->directories); + fs_file_close(pak->handle); + mem_d (pak); + + return true; +} + +/* + * Fancy GCC-like LONG parsing allows things like --opt=param with + * assignment operator. This is used for redirecting stdout/stderr + * console to specific files of your choice. + */ +static bool parsecmd(const char *optname, int *argc_, char ***argv_, char **out, int ds, bool split) { + int argc = *argc_; + char **argv = *argv_; + + size_t len = strlen(optname); + + if (strncmp(argv[0]+ds, optname, len)) + return false; + + /* it's --optname, check how the parameter is supplied */ + if (argv[0][ds+len] == '=') { + *out = argv[0]+ds+len+1; + return true; + } + + if (!split || argc < ds) /* no parameter was provided, or only single-arg form accepted */ + return false; + + /* using --opt param */ + *out = argv[1]; + --*argc_; + ++*argv_; + return true; +} + +int main(int argc, char **argv) { + bool extract = true; + char *redirout = (char*)stdout; + char *redirerr = (char*)stderr; + char *file = NULL; + char **files = NULL; + pak_file_t *pak = NULL; + size_t iter = 0; + + con_init(); + + /* + * Command line option parsing commences now We only need to support + * a few things in the test suite. + */ + while (argc > 1) { + ++argv; + --argc; + + if (argv[0][0] == '-') { + if (parsecmd("redirout", &argc, &argv, &redirout, 1, false)) + continue; + if (parsecmd("redirerr", &argc, &argv, &redirerr, 1, false)) + continue; + if (parsecmd("file", &argc, &argv, &file, 1, false)) + continue; + + con_change(redirout, redirerr); + + switch (argv[0][1]) { + case 'e': extract = true; continue; + case 'c': extract = false; continue; + } + + if (!strcmp(argv[0]+1, "debug")) { + OPTS_OPTION_BOOL(OPTION_DEBUG) = true; + continue; + } + if (!strcmp(argv[0]+1, "memchk")) { + OPTS_OPTION_BOOL(OPTION_MEMCHK) = true; + continue; + } + if (!strcmp(argv[0]+1, "nocolor")) { + con_color(0); + continue; + } + } + + vec_push(files, argv[0]); + } + con_change(redirout, redirerr); + + + if (!file) { + con_err("-file must be specified for output/input PAK file\n"); + vec_free(files); + return EXIT_FAILURE; + } + + if (extract) { + if (!(pak = pak_open(file, "r"))) { + con_err("failed to open PAK file %s\n", file); + vec_free(files); + return EXIT_FAILURE; + } + + if (!pak_extract_all(pak, "./")) { + con_err("failed to extract PAK %s (files may be missing)\n", file); + pak_close(pak); + vec_free(files); + return EXIT_FAILURE; + } + + /* not possible */ + pak_close(pak); + vec_free(files); + stat_info(); + + return EXIT_SUCCESS; + } + + if (!(pak = pak_open(file, "w"))) { + con_err("failed to open PAK %s for writing\n", file); + vec_free(files); + return EXIT_FAILURE; + } + + for (iter = 0; iter < vec_size(files); iter++) { + if (!(pak_insert_one(pak, files[iter]))) { + con_err("failed inserting %s for PAK %s\n", files[iter], file); + pak_close(pak); + vec_free(files); + return EXIT_FAILURE; + } + } + + /* not possible */ + pak_close(pak); + vec_free(files); + + stat_info(); + return EXIT_SUCCESS; +} diff --git a/misc/source/gmqcc-src/parser.c b/misc/source/gmqcc-src/parser.c new file mode 100644 index 00000000..e92cb6e6 --- /dev/null +++ b/misc/source/gmqcc-src/parser.c @@ -0,0 +1,6462 @@ +/* + * Copyright (C) 2012, 2013 + * Wolfgang Bumiller + * Dale Weiler + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include +#include + +#include "gmqcc.h" +#include "lexer.h" +#include "ast.h" + +/* beginning of locals */ +#define PARSER_HT_LOCALS 2 + +#define PARSER_HT_SIZE 128 +#define TYPEDEF_HT_SIZE 16 + +typedef struct parser_s { + lex_file *lex; + int tok; + + bool ast_cleaned; + + ast_expression **globals; + ast_expression **fields; + ast_function **functions; + ast_value **imm_float; + ast_value **imm_string; + ast_value **imm_vector; + size_t translated; + + ht ht_imm_string; + + /* must be deleted first, they reference immediates and values */ + ast_value **accessors; + + ast_value *imm_float_zero; + ast_value *imm_float_one; + ast_value *imm_float_neg_one; + + ast_value *imm_vector_zero; + + ast_value *nil; + ast_value *reserved_version; + + size_t crc_globals; + size_t crc_fields; + + ast_function *function; + ht aliases; + + /* All the labels the function defined... + * Should they be in ast_function instead? + */ + ast_label **labels; + ast_goto **gotos; + const char **breaks; + const char **continues; + + /* A list of hashtables for each scope */ + ht *variables; + ht htfields; + ht htglobals; + ht *typedefs; + + /* same as above but for the spelling corrector */ + correct_trie_t **correct_variables; + size_t ***correct_variables_score; /* vector of vector of size_t* */ + + /* not to be used directly, we use the hash table */ + ast_expression **_locals; + size_t *_blocklocals; + ast_value **_typedefs; + size_t *_blocktypedefs; + lex_ctx *_block_ctx; + + /* we store the '=' operator info */ + const oper_info *assign_op; + + /* magic values */ + ast_value *const_vec[3]; + + /* pragma flags */ + bool noref; + + /* collected information */ + size_t max_param_count; + + /* code generator */ + code_t *code; +} parser_t; + +static ast_expression * const intrinsic_debug_typestring = (ast_expression*)0x1; + +static void parser_enterblock(parser_t *parser); +static bool parser_leaveblock(parser_t *parser); +static void parser_addlocal(parser_t *parser, const char *name, ast_expression *e); +static void parser_addglobal(parser_t *parser, const char *name, ast_expression *e); +static bool parse_typedef(parser_t *parser); +static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool is_static, uint32_t qflags, char *vstring); +static ast_block* parse_block(parser_t *parser); +static bool parse_block_into(parser_t *parser, ast_block *block); +static bool parse_statement_or_block(parser_t *parser, ast_expression **out); +static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out, bool allow_cases); +static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue, bool with_labels); +static ast_expression* parse_expression(parser_t *parser, bool stopatcomma, bool with_labels); +static ast_value* parser_create_array_setter_proto(parser_t *parser, ast_value *array, const char *funcname); +static ast_value* parser_create_array_getter_proto(parser_t *parser, ast_value *array, const ast_expression *elemtype, const char *funcname); +static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef); + +static void parseerror(parser_t *parser, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vcompile_error(parser->lex->tok.ctx, fmt, ap); + va_end(ap); +} + +/* returns true if it counts as an error */ +static bool GMQCC_WARN parsewarning(parser_t *parser, int warntype, const char *fmt, ...) +{ + bool r; + va_list ap; + va_start(ap, fmt); + r = vcompile_warning(parser->lex->tok.ctx, warntype, fmt, ap); + va_end(ap); + return r; +} + +/********************************************************************** + * some maths used for constant folding + */ + +vector vec3_add(vector a, vector b) +{ + vector out; + out.x = a.x + b.x; + out.y = a.y + b.y; + out.z = a.z + b.z; + return out; +} + +vector vec3_sub(vector a, vector b) +{ + vector out; + out.x = a.x - b.x; + out.y = a.y - b.y; + out.z = a.z - b.z; + return out; +} + +qcfloat vec3_mulvv(vector a, vector b) +{ + return (a.x * b.x + a.y * b.y + a.z * b.z); +} + +vector vec3_mulvf(vector a, float b) +{ + vector out; + out.x = a.x * b; + out.y = a.y * b; + out.z = a.z * b; + return out; +} + +/********************************************************************** + * parsing + */ + +static bool parser_next(parser_t *parser) +{ + /* lex_do kills the previous token */ + parser->tok = lex_do(parser->lex); + if (parser->tok == TOKEN_EOF) + return true; + if (parser->tok >= TOKEN_ERROR) { + parseerror(parser, "lex error"); + return false; + } + return true; +} + +#define parser_tokval(p) ((p)->lex->tok.value) +#define parser_token(p) (&((p)->lex->tok)) +#define parser_ctx(p) ((p)->lex->tok.ctx) + +static ast_value* parser_const_float(parser_t *parser, double d) +{ + size_t i; + ast_value *out; + lex_ctx ctx; + for (i = 0; i < vec_size(parser->imm_float); ++i) { + const double compare = parser->imm_float[i]->constval.vfloat; + if (memcmp((const void*)&compare, (const void *)&d, sizeof(double)) == 0) + return parser->imm_float[i]; + } + if (parser->lex) + ctx = parser_ctx(parser); + else { + memset(&ctx, 0, sizeof(ctx)); + } + out = ast_value_new(ctx, "#IMMEDIATE", TYPE_FLOAT); + out->cvq = CV_CONST; + out->hasvalue = true; + out->isimm = true; + out->constval.vfloat = d; + vec_push(parser->imm_float, out); + return out; +} + +static ast_value* parser_const_float_0(parser_t *parser) +{ + if (!parser->imm_float_zero) + parser->imm_float_zero = parser_const_float(parser, 0); + return parser->imm_float_zero; +} + +static ast_value* parser_const_float_neg1(parser_t *parser) { + if (!parser->imm_float_neg_one) + parser->imm_float_neg_one = parser_const_float(parser, -1); + return parser->imm_float_neg_one; +} + +static ast_value* parser_const_float_1(parser_t *parser) +{ + if (!parser->imm_float_one) + parser->imm_float_one = parser_const_float(parser, 1); + return parser->imm_float_one; +} + +static char *parser_strdup(const char *str) +{ + if (str && !*str) { + /* actually dup empty strings */ + char *out = (char*)mem_a(1); + *out = 0; + return out; + } + return util_strdup(str); +} + +static ast_value* parser_const_string(parser_t *parser, const char *str, bool dotranslate) +{ + size_t hash = util_hthash(parser->ht_imm_string, str); + ast_value *out; + if ( (out = (ast_value*)util_htgeth(parser->ht_imm_string, str, hash)) ) { + if (dotranslate && out->name[0] == '#') { + char name[32]; + util_snprintf(name, sizeof(name), "dotranslate_%lu", (unsigned long)(parser->translated++)); + ast_value_set_name(out, name); + out->expression.flags |= AST_FLAG_INCLUDE_DEF; + } + return out; + } + /* + for (i = 0; i < vec_size(parser->imm_string); ++i) { + if (!strcmp(parser->imm_string[i]->constval.vstring, str)) + return parser->imm_string[i]; + } + */ + if (dotranslate) { + char name[32]; + util_snprintf(name, sizeof(name), "dotranslate_%lu", (unsigned long)(parser->translated++)); + out = ast_value_new(parser_ctx(parser), name, TYPE_STRING); + out->expression.flags |= AST_FLAG_INCLUDE_DEF; + } else + out = ast_value_new(parser_ctx(parser), "#IMMEDIATE", TYPE_STRING); + out->cvq = CV_CONST; + out->hasvalue = true; + out->isimm = true; + out->constval.vstring = parser_strdup(str); + vec_push(parser->imm_string, out); + util_htseth(parser->ht_imm_string, str, hash, out); + return out; +} + +static ast_value* parser_const_vector(parser_t *parser, vector v) +{ + size_t i; + ast_value *out; + for (i = 0; i < vec_size(parser->imm_vector); ++i) { + if (!memcmp(&parser->imm_vector[i]->constval.vvec, &v, sizeof(v))) + return parser->imm_vector[i]; + } + out = ast_value_new(parser_ctx(parser), "#IMMEDIATE", TYPE_VECTOR); + out->cvq = CV_CONST; + out->hasvalue = true; + out->isimm = true; + out->constval.vvec = v; + vec_push(parser->imm_vector, out); + return out; +} + +static ast_value* parser_const_vector_f(parser_t *parser, float x, float y, float z) +{ + vector v; + v.x = x; + v.y = y; + v.z = z; + return parser_const_vector(parser, v); +} + +static ast_value* parser_const_vector_0(parser_t *parser) +{ + if (!parser->imm_vector_zero) + parser->imm_vector_zero = parser_const_vector_f(parser, 0, 0, 0); + return parser->imm_vector_zero; +} + +static ast_expression* parser_find_field(parser_t *parser, const char *name) +{ + return ( ast_expression*)util_htget(parser->htfields, name); +} + +static ast_expression* parser_find_label(parser_t *parser, const char *name) +{ + size_t i; + for(i = 0; i < vec_size(parser->labels); i++) + if (!strcmp(parser->labels[i]->name, name)) + return (ast_expression*)parser->labels[i]; + return NULL; +} + +static ast_expression* parser_find_global(parser_t *parser, const char *name) +{ + ast_expression *var = (ast_expression*)util_htget(parser->aliases, parser_tokval(parser)); + if (var) + return var; + return (ast_expression*)util_htget(parser->htglobals, name); +} + +static ast_expression* parser_find_param(parser_t *parser, const char *name) +{ + size_t i; + ast_value *fun; + if (!parser->function) + return NULL; + fun = parser->function->vtype; + for (i = 0; i < vec_size(fun->expression.params); ++i) { + if (!strcmp(fun->expression.params[i]->name, name)) + return (ast_expression*)(fun->expression.params[i]); + } + return NULL; +} + +static ast_expression* parser_find_local(parser_t *parser, const char *name, size_t upto, bool *isparam) +{ + size_t i, hash; + ast_expression *e; + + hash = util_hthash(parser->htglobals, name); + + *isparam = false; + for (i = vec_size(parser->variables); i > upto;) { + --i; + if ( (e = (ast_expression*)util_htgeth(parser->variables[i], name, hash)) ) + return e; + } + *isparam = true; + return parser_find_param(parser, name); +} + +static ast_expression* parser_find_var(parser_t *parser, const char *name) +{ + bool dummy; + ast_expression *v; + v = parser_find_local(parser, name, 0, &dummy); + if (!v) v = parser_find_global(parser, name); + return v; +} + +static ast_value* parser_find_typedef(parser_t *parser, const char *name, size_t upto) +{ + size_t i, hash; + ast_value *e; + hash = util_hthash(parser->typedefs[0], name); + + for (i = vec_size(parser->typedefs); i > upto;) { + --i; + if ( (e = (ast_value*)util_htgeth(parser->typedefs[i], name, hash)) ) + return e; + } + return NULL; +} + +/* include intrinsics */ +#include "intrin.h" + +typedef struct +{ + size_t etype; /* 0 = expression, others are operators */ + bool isparen; + size_t off; + ast_expression *out; + ast_block *block; /* for commas and function calls */ + lex_ctx ctx; +} sy_elem; + +enum { + PAREN_EXPR, + PAREN_FUNC, + PAREN_INDEX, + PAREN_TERNARY1, + PAREN_TERNARY2 +}; +typedef struct +{ + sy_elem *out; + sy_elem *ops; + size_t *argc; + unsigned int *paren; +} shunt; + +static sy_elem syexp(lex_ctx ctx, ast_expression *v) { + sy_elem e; + e.etype = 0; + e.off = 0; + e.out = v; + e.block = NULL; + e.ctx = ctx; + e.isparen = false; + return e; +} + +static sy_elem syblock(lex_ctx ctx, ast_block *v) { + sy_elem e; + e.etype = 0; + e.off = 0; + e.out = (ast_expression*)v; + e.block = v; + e.ctx = ctx; + e.isparen = false; + return e; +} + +static sy_elem syop(lex_ctx ctx, const oper_info *op) { + sy_elem e; + e.etype = 1 + (op - operators); + e.off = 0; + e.out = NULL; + e.block = NULL; + e.ctx = ctx; + e.isparen = false; + return e; +} + +static sy_elem syparen(lex_ctx ctx, size_t off) { + sy_elem e; + e.etype = 0; + e.off = off; + e.out = NULL; + e.block = NULL; + e.ctx = ctx; + e.isparen = true; + return e; +} + +/* With regular precedence rules, ent.foo[n] is the same as (ent.foo)[n], + * so we need to rotate it to become ent.(foo[n]). + */ +static bool rotate_entfield_array_index_nodes(ast_expression **out) +{ + ast_array_index *index, *oldindex; + ast_entfield *entfield; + + ast_value *field; + ast_expression *sub; + ast_expression *entity; + + lex_ctx ctx = ast_ctx(*out); + + if (!ast_istype(*out, ast_array_index)) + return false; + index = (ast_array_index*)*out; + + if (!ast_istype(index->array, ast_entfield)) + return false; + entfield = (ast_entfield*)index->array; + + if (!ast_istype(entfield->field, ast_value)) + return false; + field = (ast_value*)entfield->field; + + sub = index->index; + entity = entfield->entity; + + oldindex = index; + + index = ast_array_index_new(ctx, (ast_expression*)field, sub); + entfield = ast_entfield_new(ctx, entity, (ast_expression*)index); + *out = (ast_expression*)entfield; + + oldindex->array = NULL; + oldindex->index = NULL; + ast_delete(oldindex); + + return true; +} + +static bool immediate_is_true(lex_ctx ctx, ast_value *v) +{ + switch (v->expression.vtype) { + case TYPE_FLOAT: + return !!v->constval.vfloat; + case TYPE_INTEGER: + return !!v->constval.vint; + case TYPE_VECTOR: + if (OPTS_FLAG(CORRECT_LOGIC)) + return v->constval.vvec.x && + v->constval.vvec.y && + v->constval.vvec.z; + else + return !!(v->constval.vvec.x); + case TYPE_STRING: + if (!v->constval.vstring) + return false; + if (v->constval.vstring && OPTS_FLAG(TRUE_EMPTY_STRINGS)) + return true; + return !!v->constval.vstring[0]; + default: + compile_error(ctx, "internal error: immediate_is_true on invalid type"); + return !!v->constval.vfunc; + } +} + +static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) +{ + const oper_info *op; + lex_ctx ctx; + ast_expression *out = NULL; + ast_expression *exprs[3]; + ast_block *blocks[3]; + ast_value *asvalue[3]; + ast_binstore *asbinstore; + size_t i, assignop, addop, subop; + qcint generated_op = 0; + + char ty1[1024]; + char ty2[1024]; + + if (!vec_size(sy->ops)) { + parseerror(parser, "internal error: missing operator"); + return false; + } + + if (vec_last(sy->ops).isparen) { + parseerror(parser, "unmatched parenthesis"); + return false; + } + + op = &operators[vec_last(sy->ops).etype - 1]; + ctx = vec_last(sy->ops).ctx; + + if (vec_size(sy->out) < op->operands) { + compile_error(ctx, "internal error: not enough operands: %i (operator %s (%i))", vec_size(sy->out), + op->op, (int)op->id); + return false; + } + + vec_shrinkby(sy->ops, 1); + + /* op(:?) has no input and no output */ + if (!op->operands) + return true; + + vec_shrinkby(sy->out, op->operands); + for (i = 0; i < op->operands; ++i) { + exprs[i] = sy->out[vec_size(sy->out)+i].out; + blocks[i] = sy->out[vec_size(sy->out)+i].block; + asvalue[i] = (ast_value*)exprs[i]; + + if (exprs[i]->vtype == TYPE_NOEXPR && + !(i != 0 && op->id == opid2('?',':')) && + !(i == 1 && op->id == opid1('.'))) + { + if (ast_istype(exprs[i], ast_label)) + compile_error(ast_ctx(exprs[i]), "expected expression, got an unknown identifier"); + else + compile_error(ast_ctx(exprs[i]), "not an expression"); + (void)!compile_warning(ast_ctx(exprs[i]), WARN_DEBUG, "expression %u\n", (unsigned int)i); + } + } + + if (blocks[0] && !vec_size(blocks[0]->exprs) && op->id != opid1(',')) { + compile_error(ctx, "internal error: operator cannot be applied on empty blocks"); + return false; + } + +#define NotSameType(T) \ + (exprs[0]->vtype != exprs[1]->vtype || \ + exprs[0]->vtype != T) +#define CanConstFold1(A) \ + (ast_istype((A), ast_value) && ((ast_value*)(A))->hasvalue && (((ast_value*)(A))->cvq == CV_CONST) &&\ + (A)->vtype != TYPE_FUNCTION) +#define CanConstFold(A, B) \ + (CanConstFold1(A) && CanConstFold1(B)) +#define ConstV(i) (asvalue[(i)]->constval.vvec) +#define ConstF(i) (asvalue[(i)]->constval.vfloat) +#define ConstS(i) (asvalue[(i)]->constval.vstring) + switch (op->id) + { + default: + compile_error(ctx, "internal error: unhandled operator: %s (%i)", op->op, (int)op->id); + return false; + + case opid1('.'): + if (exprs[0]->vtype == TYPE_VECTOR && + exprs[1]->vtype == TYPE_NOEXPR) + { + if (exprs[1] == (ast_expression*)parser->const_vec[0]) + out = (ast_expression*)ast_member_new(ctx, exprs[0], 0, NULL); + else if (exprs[1] == (ast_expression*)parser->const_vec[1]) + out = (ast_expression*)ast_member_new(ctx, exprs[0], 1, NULL); + else if (exprs[1] == (ast_expression*)parser->const_vec[2]) + out = (ast_expression*)ast_member_new(ctx, exprs[0], 2, NULL); + else { + compile_error(ctx, "access to invalid vector component"); + return false; + } + } + else if (exprs[0]->vtype == TYPE_ENTITY) { + if (exprs[1]->vtype != TYPE_FIELD) { + compile_error(ast_ctx(exprs[1]), "type error: right hand of member-operand should be an entity-field"); + return false; + } + out = (ast_expression*)ast_entfield_new(ctx, exprs[0], exprs[1]); + } + else if (exprs[0]->vtype == TYPE_VECTOR) { + compile_error(ast_ctx(exprs[1]), "vectors cannot be accessed this way"); + return false; + } + else { + compile_error(ast_ctx(exprs[1]), "type error: member-of operator on something that is not an entity or vector"); + return false; + } + break; + + case opid1('['): + if (exprs[0]->vtype != TYPE_ARRAY && + !(exprs[0]->vtype == TYPE_FIELD && + exprs[0]->next->vtype == TYPE_ARRAY)) + { + ast_type_to_string(exprs[0], ty1, sizeof(ty1)); + compile_error(ast_ctx(exprs[0]), "cannot index value of type %s", ty1); + return false; + } + if (exprs[1]->vtype != TYPE_FLOAT) { + ast_type_to_string(exprs[0], ty1, sizeof(ty1)); + compile_error(ast_ctx(exprs[1]), "index must be of type float, not %s", ty1); + return false; + } + out = (ast_expression*)ast_array_index_new(ctx, exprs[0], exprs[1]); + if (rotate_entfield_array_index_nodes(&out)) + { +#if 0 + /* This is not broken in fteqcc anymore */ + if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_GMQCC) { + /* this error doesn't need to make us bail out */ + (void)!parsewarning(parser, WARN_EXTENSIONS, + "accessing array-field members of an entity without parenthesis\n" + " -> this is an extension from -std=gmqcc"); + } +#endif + } + break; + + case opid1(','): + if (vec_size(sy->paren) && vec_last(sy->paren) == PAREN_FUNC) { + vec_push(sy->out, syexp(ctx, exprs[0])); + vec_push(sy->out, syexp(ctx, exprs[1])); + vec_last(sy->argc)++; + return true; + } + if (blocks[0]) { + if (!ast_block_add_expr(blocks[0], exprs[1])) + return false; + } else { + blocks[0] = ast_block_new(ctx); + if (!ast_block_add_expr(blocks[0], exprs[0]) || + !ast_block_add_expr(blocks[0], exprs[1])) + { + return false; + } + } + ast_block_set_type(blocks[0], exprs[1]); + + vec_push(sy->out, syblock(ctx, blocks[0])); + return true; + + case opid2('+','P'): + out = exprs[0]; + break; + case opid2('-','P'): + switch (exprs[0]->vtype) { + case TYPE_FLOAT: + if (CanConstFold1(exprs[0])) + out = (ast_expression*)parser_const_float(parser, -ConstF(0)); + else + out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F, + (ast_expression*)parser_const_float_0(parser), + exprs[0]); + break; + case TYPE_VECTOR: + if (CanConstFold1(exprs[0])) + out = (ast_expression*)parser_const_vector_f(parser, + -ConstV(0).x, -ConstV(0).y, -ConstV(0).z); + else + out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_V, + (ast_expression*)parser_const_vector_0(parser), + exprs[0]); + break; + default: + compile_error(ctx, "invalid types used in expression: cannot negate type %s", + type_name[exprs[0]->vtype]); + return false; + } + break; + + case opid2('!','P'): + switch (exprs[0]->vtype) { + case TYPE_FLOAT: + if (CanConstFold1(exprs[0])) + out = (ast_expression*)parser_const_float(parser, !ConstF(0)); + else + out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_F, exprs[0]); + break; + case TYPE_VECTOR: + if (CanConstFold1(exprs[0])) + out = (ast_expression*)parser_const_float(parser, + (!ConstV(0).x && !ConstV(0).y && !ConstV(0).z)); + else + out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_V, exprs[0]); + break; + case TYPE_STRING: + if (CanConstFold1(exprs[0])) { + if (OPTS_FLAG(TRUE_EMPTY_STRINGS)) + out = (ast_expression*)parser_const_float(parser, !ConstS(0)); + else + out = (ast_expression*)parser_const_float(parser, !ConstS(0) || !*ConstS(0)); + } else { + if (OPTS_FLAG(TRUE_EMPTY_STRINGS)) + out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_F, exprs[0]); + else + out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_S, exprs[0]); + } + break; + /* we don't constant-fold NOT for these types */ + case TYPE_ENTITY: + out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_ENT, exprs[0]); + break; + case TYPE_FUNCTION: + out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_FNC, exprs[0]); + break; + default: + compile_error(ctx, "invalid types used in expression: cannot logically negate type %s", + type_name[exprs[0]->vtype]); + return false; + } + break; + + case opid1('+'): + if (exprs[0]->vtype != exprs[1]->vtype || + (exprs[0]->vtype != TYPE_VECTOR && exprs[0]->vtype != TYPE_FLOAT) ) + { + compile_error(ctx, "invalid types used in expression: cannot add type %s and %s", + type_name[exprs[0]->vtype], + type_name[exprs[1]->vtype]); + return false; + } + switch (exprs[0]->vtype) { + case TYPE_FLOAT: + if (CanConstFold(exprs[0], exprs[1])) + { + out = (ast_expression*)parser_const_float(parser, ConstF(0) + ConstF(1)); + } + else + out = (ast_expression*)ast_binary_new(ctx, INSTR_ADD_F, exprs[0], exprs[1]); + break; + case TYPE_VECTOR: + if (CanConstFold(exprs[0], exprs[1])) + out = (ast_expression*)parser_const_vector(parser, vec3_add(ConstV(0), ConstV(1))); + else + out = (ast_expression*)ast_binary_new(ctx, INSTR_ADD_V, exprs[0], exprs[1]); + break; + default: + compile_error(ctx, "invalid types used in expression: cannot add type %s and %s", + type_name[exprs[0]->vtype], + type_name[exprs[1]->vtype]); + return false; + }; + break; + case opid1('-'): + if (exprs[0]->vtype != exprs[1]->vtype || + (exprs[0]->vtype != TYPE_VECTOR && exprs[0]->vtype != TYPE_FLOAT) ) + { + compile_error(ctx, "invalid types used in expression: cannot subtract type %s from %s", + type_name[exprs[1]->vtype], + type_name[exprs[0]->vtype]); + return false; + } + switch (exprs[0]->vtype) { + case TYPE_FLOAT: + if (CanConstFold(exprs[0], exprs[1])) + out = (ast_expression*)parser_const_float(parser, ConstF(0) - ConstF(1)); + else + out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F, exprs[0], exprs[1]); + break; + case TYPE_VECTOR: + if (CanConstFold(exprs[0], exprs[1])) + out = (ast_expression*)parser_const_vector(parser, vec3_sub(ConstV(0), ConstV(1))); + else + out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_V, exprs[0], exprs[1]); + break; + default: + compile_error(ctx, "invalid types used in expression: cannot subtract type %s from %s", + type_name[exprs[1]->vtype], + type_name[exprs[0]->vtype]); + return false; + }; + break; + case opid1('*'): + if (exprs[0]->vtype != exprs[1]->vtype && + !(exprs[0]->vtype == TYPE_VECTOR && + exprs[1]->vtype == TYPE_FLOAT) && + !(exprs[1]->vtype == TYPE_VECTOR && + exprs[0]->vtype == TYPE_FLOAT) + ) + { + compile_error(ctx, "invalid types used in expression: cannot multiply types %s and %s", + type_name[exprs[1]->vtype], + type_name[exprs[0]->vtype]); + return false; + } + switch (exprs[0]->vtype) { + case TYPE_FLOAT: + if (exprs[1]->vtype == TYPE_VECTOR) + { + if (CanConstFold(exprs[0], exprs[1])) + out = (ast_expression*)parser_const_vector(parser, vec3_mulvf(ConstV(1), ConstF(0))); + else + out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_FV, exprs[0], exprs[1]); + } + else + { + if (CanConstFold(exprs[0], exprs[1])) + out = (ast_expression*)parser_const_float(parser, ConstF(0) * ConstF(1)); + else + out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, exprs[0], exprs[1]); + } + break; + case TYPE_VECTOR: + if (exprs[1]->vtype == TYPE_FLOAT) + { + if (CanConstFold(exprs[0], exprs[1])) + out = (ast_expression*)parser_const_vector(parser, vec3_mulvf(ConstV(0), ConstF(1))); + else + out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_VF, exprs[0], exprs[1]); + } + else + { + if (CanConstFold(exprs[0], exprs[1])) + out = (ast_expression*)parser_const_float(parser, vec3_mulvv(ConstV(0), ConstV(1))); + else if (OPTS_OPTIMIZATION(OPTIM_VECTOR_COMPONENTS) && CanConstFold1(exprs[0])) { + vector vec = ConstV(0); + if (!vec.y && !vec.z) { /* 'n 0 0' * v */ + ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS]; + out = (ast_expression*)ast_member_new(ctx, exprs[1], 0, NULL); + out->node.keep = false; + ((ast_member*)out)->rvalue = true; + if (vec.x != 1) + out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, (ast_expression*)parser_const_float(parser, vec.x), out); + } + else if (!vec.x && !vec.z) { /* '0 n 0' * v */ + ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS]; + out = (ast_expression*)ast_member_new(ctx, exprs[1], 1, NULL); + out->node.keep = false; + ((ast_member*)out)->rvalue = true; + if (vec.y != 1) + out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, (ast_expression*)parser_const_float(parser, vec.y), out); + } + else if (!vec.x && !vec.y) { /* '0 n 0' * v */ + ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS]; + out = (ast_expression*)ast_member_new(ctx, exprs[1], 2, NULL); + out->node.keep = false; + ((ast_member*)out)->rvalue = true; + if (vec.z != 1) + out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, (ast_expression*)parser_const_float(parser, vec.z), out); + } + else + out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_V, exprs[0], exprs[1]); + } + else if (OPTS_OPTIMIZATION(OPTIM_VECTOR_COMPONENTS) && CanConstFold1(exprs[1])) { + vector vec = ConstV(1); + if (!vec.y && !vec.z) { /* v * 'n 0 0' */ + ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS]; + out = (ast_expression*)ast_member_new(ctx, exprs[0], 0, NULL); + out->node.keep = false; + ((ast_member*)out)->rvalue = true; + if (vec.x != 1) + out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, out, (ast_expression*)parser_const_float(parser, vec.x)); + } + else if (!vec.x && !vec.z) { /* v * '0 n 0' */ + ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS]; + out = (ast_expression*)ast_member_new(ctx, exprs[0], 1, NULL); + out->node.keep = false; + ((ast_member*)out)->rvalue = true; + if (vec.y != 1) + out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, out, (ast_expression*)parser_const_float(parser, vec.y)); + } + else if (!vec.x && !vec.y) { /* v * '0 n 0' */ + ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS]; + out = (ast_expression*)ast_member_new(ctx, exprs[0], 2, NULL); + out->node.keep = false; + ((ast_member*)out)->rvalue = true; + if (vec.z != 1) + out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, out, (ast_expression*)parser_const_float(parser, vec.z)); + } + else + out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_V, exprs[0], exprs[1]); + } + else + out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_V, exprs[0], exprs[1]); + } + break; + default: + compile_error(ctx, "invalid types used in expression: cannot multiply types %s and %s", + type_name[exprs[1]->vtype], + type_name[exprs[0]->vtype]); + return false; + }; + break; + case opid1('/'): + if (exprs[1]->vtype != TYPE_FLOAT) { + ast_type_to_string(exprs[0], ty1, sizeof(ty1)); + ast_type_to_string(exprs[1], ty2, sizeof(ty2)); + compile_error(ctx, "invalid types used in expression: cannot divide tyeps %s and %s", ty1, ty2); + return false; + } + if (exprs[0]->vtype == TYPE_FLOAT) { + if (CanConstFold(exprs[0], exprs[1])) + out = (ast_expression*)parser_const_float(parser, ConstF(0) / ConstF(1)); + else + out = (ast_expression*)ast_binary_new(ctx, INSTR_DIV_F, exprs[0], exprs[1]); + } + else if (exprs[0]->vtype == TYPE_VECTOR) { + if (CanConstFold(exprs[0], exprs[1])) + out = (ast_expression*)parser_const_vector(parser, vec3_mulvf(ConstV(0), 1.0/ConstF(1))); + else { + if (CanConstFold1(exprs[1])) { + out = (ast_expression*)parser_const_float(parser, 1.0 / ConstF(1)); + } else { + out = (ast_expression*)ast_binary_new(ctx, INSTR_DIV_F, + (ast_expression*)parser_const_float_1(parser), + exprs[1]); + } + if (!out) { + compile_error(ctx, "internal error: failed to generate division"); + return false; + } + out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_VF, exprs[0], out); + } + } + else + { + ast_type_to_string(exprs[0], ty1, sizeof(ty1)); + ast_type_to_string(exprs[1], ty2, sizeof(ty2)); + compile_error(ctx, "invalid types used in expression: cannot divide tyeps %s and %s", ty1, ty2); + return false; + } + break; + + case opid1('%'): + if (NotSameType(TYPE_FLOAT)) { + compile_error(ctx, "invalid types used in expression: cannot perform modulo operation between types %s and %s", + type_name[exprs[0]->vtype], + type_name[exprs[1]->vtype]); + return false; + } + if (CanConstFold(exprs[0], exprs[1])) { + out = (ast_expression*)parser_const_float(parser, + (float)(((qcint)ConstF(0)) % ((qcint)ConstF(1)))); + } else { + /* generate a call to __builtin_mod */ + ast_expression *mod = intrin_func(parser, "mod"); + ast_call *call = NULL; + if (!mod) return false; /* can return null for missing floor */ + + call = ast_call_new(parser_ctx(parser), mod); + vec_push(call->params, exprs[0]); + vec_push(call->params, exprs[1]); + + out = (ast_expression*)call; + } + break; + + case opid2('%','='): + compile_error(ctx, "%= is unimplemented"); + return false; + + case opid1('|'): + case opid1('&'): + if (NotSameType(TYPE_FLOAT)) { + compile_error(ctx, "invalid types used in expression: cannot perform bit operations between types %s and %s", + type_name[exprs[0]->vtype], + type_name[exprs[1]->vtype]); + return false; + } + if (CanConstFold(exprs[0], exprs[1])) + out = (ast_expression*)parser_const_float(parser, + (op->id == opid1('|') ? (float)( ((qcint)ConstF(0)) | ((qcint)ConstF(1)) ) : + (float)( ((qcint)ConstF(0)) & ((qcint)ConstF(1)) ) )); + else + out = (ast_expression*)ast_binary_new(ctx, + (op->id == opid1('|') ? INSTR_BITOR : INSTR_BITAND), + exprs[0], exprs[1]); + break; + case opid1('^'): + compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor via ^"); + return false; + + case opid2('<','<'): + case opid2('>','>'): + if (CanConstFold(exprs[0], exprs[1]) && ! NotSameType(TYPE_FLOAT)) { + if (op->id == opid2('<','<')) + out = (ast_expression*)parser_const_float(parser, (double)((unsigned int)(ConstF(0)) << (unsigned int)(ConstF(1)))); + else + out = (ast_expression*)parser_const_float(parser, (double)((unsigned int)(ConstF(0)) >> (unsigned int)(ConstF(1)))); + break; + } + case opid3('<','<','='): + case opid3('>','>','='): + compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-shifts"); + return false; + + case opid2('|','|'): + generated_op += 1; /* INSTR_OR */ + case opid2('&','&'): + generated_op += INSTR_AND; + if (CanConstFold(exprs[0], exprs[1])) + { + if (OPTS_FLAG(PERL_LOGIC)) { + if (immediate_is_true(ctx, asvalue[0])) + out = exprs[1]; + } + else + out = (ast_expression*)parser_const_float(parser, + ( (generated_op == INSTR_OR) + ? (immediate_is_true(ctx, asvalue[0]) || immediate_is_true(ctx, asvalue[1])) + : (immediate_is_true(ctx, asvalue[0]) && immediate_is_true(ctx, asvalue[1])) ) + ? 1 : 0); + } + else + { + if (OPTS_FLAG(PERL_LOGIC) && !ast_compare_type(exprs[0], exprs[1])) { + ast_type_to_string(exprs[0], ty1, sizeof(ty1)); + ast_type_to_string(exprs[1], ty2, sizeof(ty2)); + compile_error(ctx, "invalid types for logical operation with -fperl-logic: %s and %s", ty1, ty2); + return false; + } + for (i = 0; i < 2; ++i) { + if (OPTS_FLAG(CORRECT_LOGIC) && exprs[i]->vtype == TYPE_VECTOR) { + out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_V, exprs[i]); + if (!out) break; + out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_F, out); + if (!out) break; + exprs[i] = out; out = NULL; + if (OPTS_FLAG(PERL_LOGIC)) { + /* here we want to keep the right expressions' type */ + break; + } + } + else if (OPTS_FLAG(FALSE_EMPTY_STRINGS) && exprs[i]->vtype == TYPE_STRING) { + out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_S, exprs[i]); + if (!out) break; + out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_F, out); + if (!out) break; + exprs[i] = out; out = NULL; + if (OPTS_FLAG(PERL_LOGIC)) { + /* here we want to keep the right expressions' type */ + break; + } + } + } + out = (ast_expression*)ast_binary_new(ctx, generated_op, exprs[0], exprs[1]); + } + break; + + case opid2('?',':'): + if (vec_last(sy->paren) != PAREN_TERNARY2) { + compile_error(ctx, "mismatched parenthesis/ternary"); + return false; + } + vec_pop(sy->paren); + if (!ast_compare_type(exprs[1], exprs[2])) { + ast_type_to_string(exprs[1], ty1, sizeof(ty1)); + ast_type_to_string(exprs[2], ty2, sizeof(ty2)); + compile_error(ctx, "operands of ternary expression must have the same type, got %s and %s", ty1, ty2); + return false; + } + if (CanConstFold1(exprs[0])) + out = (immediate_is_true(ctx, asvalue[0]) ? exprs[1] : exprs[2]); + else + out = (ast_expression*)ast_ternary_new(ctx, exprs[0], exprs[1], exprs[2]); + break; + + case opid2('*', '*'): + if (NotSameType(TYPE_FLOAT)) { + ast_type_to_string(exprs[0], ty1, sizeof(ty1)); + ast_type_to_string(exprs[1], ty2, sizeof(ty2)); + compile_error(ctx, "invalid types used in exponentiation: %s and %s", + ty1, ty2); + + return false; + } + + if (CanConstFold(exprs[0], exprs[1])) { + out = (ast_expression*)parser_const_float(parser, powf(ConstF(0), ConstF(1))); + } else { + ast_call *gencall = ast_call_new(parser_ctx(parser), intrin_func(parser, "pow")); + vec_push(gencall->params, exprs[0]); + vec_push(gencall->params, exprs[1]); + out = (ast_expression*)gencall; + } + break; + + case opid3('<','=','>'): /* -1, 0, or 1 */ + if (NotSameType(TYPE_FLOAT)) { + ast_type_to_string(exprs[0], ty1, sizeof(ty1)); + ast_type_to_string(exprs[1], ty2, sizeof(ty2)); + compile_error(ctx, "invalid types used in comparision: %s and %s", + ty1, ty2); + + return false; + } + + if (CanConstFold(exprs[0], exprs[1])) { + if (ConstF(0) < ConstF(1)) + out = (ast_expression*)parser_const_float_neg1(parser); + else if (ConstF(0) == ConstF(1)) + out = (ast_expression*)parser_const_float_0(parser); + else if (ConstF(0) > ConstF(1)) + out = (ast_expression*)parser_const_float_1(parser); + } else { + ast_binary *eq = ast_binary_new(ctx, INSTR_EQ_F, exprs[0], exprs[1]); + + eq->refs = (ast_binary_ref)false; /* references nothing */ + + /* if (lt) { */ + out = (ast_expression*)ast_ternary_new(ctx, + (ast_expression*)ast_binary_new(ctx, INSTR_LT, exprs[0], exprs[1]), + /* out = -1 */ + (ast_expression*)parser_const_float_neg1(parser), + /* } else { */ + /* if (eq) { */ + (ast_expression*)ast_ternary_new(ctx, (ast_expression*)eq, + /* out = 0 */ + (ast_expression*)parser_const_float_0(parser), + /* } else { */ + /* out = 1 */ + (ast_expression*)parser_const_float_1(parser) + /* } */ + ) + /* } */ + ); + + } + break; + + case opid1('>'): + generated_op += 1; /* INSTR_GT */ + case opid1('<'): + generated_op += 1; /* INSTR_LT */ + case opid2('>', '='): + generated_op += 1; /* INSTR_GE */ + case opid2('<', '='): + generated_op += INSTR_LE; + if (NotSameType(TYPE_FLOAT)) { + compile_error(ctx, "invalid types used in expression: cannot perform comparison between types %s and %s", + type_name[exprs[0]->vtype], + type_name[exprs[1]->vtype]); + return false; + } + out = (ast_expression*)ast_binary_new(ctx, generated_op, exprs[0], exprs[1]); + break; + case opid2('!', '='): + if (exprs[0]->vtype != exprs[1]->vtype) { + compile_error(ctx, "invalid types used in expression: cannot perform comparison between types %s and %s", + type_name[exprs[0]->vtype], + type_name[exprs[1]->vtype]); + return false; + } + out = (ast_expression*)ast_binary_new(ctx, type_ne_instr[exprs[0]->vtype], exprs[0], exprs[1]); + break; + case opid2('=', '='): + if (exprs[0]->vtype != exprs[1]->vtype) { + compile_error(ctx, "invalid types used in expression: cannot perform comparison between types %s and %s", + type_name[exprs[0]->vtype], + type_name[exprs[1]->vtype]); + return false; + } + out = (ast_expression*)ast_binary_new(ctx, type_eq_instr[exprs[0]->vtype], exprs[0], exprs[1]); + break; + + case opid1('='): + if (ast_istype(exprs[0], ast_entfield)) { + ast_expression *field = ((ast_entfield*)exprs[0])->field; + if (OPTS_FLAG(ADJUST_VECTOR_FIELDS) && + exprs[0]->vtype == TYPE_FIELD && + exprs[0]->next->vtype == TYPE_VECTOR) + { + assignop = type_storep_instr[TYPE_VECTOR]; + } + else + assignop = type_storep_instr[exprs[0]->vtype]; + if (assignop == VINSTR_END || !ast_compare_type(field->next, exprs[1])) + { + ast_type_to_string(field->next, ty1, sizeof(ty1)); + ast_type_to_string(exprs[1], ty2, sizeof(ty2)); + if (OPTS_FLAG(ASSIGN_FUNCTION_TYPES) && + field->next->vtype == TYPE_FUNCTION && + exprs[1]->vtype == TYPE_FUNCTION) + { + (void)!compile_warning(ctx, WARN_ASSIGN_FUNCTION_TYPES, + "invalid types in assignment: cannot assign %s to %s", ty2, ty1); + } + else + compile_error(ctx, "invalid types in assignment: cannot assign %s to %s", ty2, ty1); + } + } + else + { + if (OPTS_FLAG(ADJUST_VECTOR_FIELDS) && + exprs[0]->vtype == TYPE_FIELD && + exprs[0]->next->vtype == TYPE_VECTOR) + { + assignop = type_store_instr[TYPE_VECTOR]; + } + else { + assignop = type_store_instr[exprs[0]->vtype]; + } + + if (assignop == VINSTR_END) { + ast_type_to_string(exprs[0], ty1, sizeof(ty1)); + ast_type_to_string(exprs[1], ty2, sizeof(ty2)); + compile_error(ctx, "invalid types in assignment: cannot assign %s to %s", ty2, ty1); + } + else if (!ast_compare_type(exprs[0], exprs[1])) + { + ast_type_to_string(exprs[0], ty1, sizeof(ty1)); + ast_type_to_string(exprs[1], ty2, sizeof(ty2)); + if (OPTS_FLAG(ASSIGN_FUNCTION_TYPES) && + exprs[0]->vtype == TYPE_FUNCTION && + exprs[1]->vtype == TYPE_FUNCTION) + { + (void)!compile_warning(ctx, WARN_ASSIGN_FUNCTION_TYPES, + "invalid types in assignment: cannot assign %s to %s", ty2, ty1); + } + else + compile_error(ctx, "invalid types in assignment: cannot assign %s to %s", ty2, ty1); + } + } + if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) { + compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name); + } + out = (ast_expression*)ast_store_new(ctx, assignop, exprs[0], exprs[1]); + break; + case opid3('+','+','P'): + case opid3('-','-','P'): + /* prefix ++ */ + if (exprs[0]->vtype != TYPE_FLOAT) { + ast_type_to_string(exprs[0], ty1, sizeof(ty1)); + compile_error(ast_ctx(exprs[0]), "invalid type for prefix increment: %s", ty1); + return false; + } + if (op->id == opid3('+','+','P')) + addop = INSTR_ADD_F; + else + addop = INSTR_SUB_F; + if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) { + compile_error(ast_ctx(exprs[0]), "assignment to constant `%s`", asvalue[0]->name); + } + if (ast_istype(exprs[0], ast_entfield)) { + out = (ast_expression*)ast_binstore_new(ctx, INSTR_STOREP_F, addop, + exprs[0], + (ast_expression*)parser_const_float_1(parser)); + } else { + out = (ast_expression*)ast_binstore_new(ctx, INSTR_STORE_F, addop, + exprs[0], + (ast_expression*)parser_const_float_1(parser)); + } + break; + case opid3('S','+','+'): + case opid3('S','-','-'): + /* prefix ++ */ + if (exprs[0]->vtype != TYPE_FLOAT) { + ast_type_to_string(exprs[0], ty1, sizeof(ty1)); + compile_error(ast_ctx(exprs[0]), "invalid type for suffix increment: %s", ty1); + return false; + } + if (op->id == opid3('S','+','+')) { + addop = INSTR_ADD_F; + subop = INSTR_SUB_F; + } else { + addop = INSTR_SUB_F; + subop = INSTR_ADD_F; + } + if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) { + compile_error(ast_ctx(exprs[0]), "assignment to constant `%s`", asvalue[0]->name); + } + if (ast_istype(exprs[0], ast_entfield)) { + out = (ast_expression*)ast_binstore_new(ctx, INSTR_STOREP_F, addop, + exprs[0], + (ast_expression*)parser_const_float_1(parser)); + } else { + out = (ast_expression*)ast_binstore_new(ctx, INSTR_STORE_F, addop, + exprs[0], + (ast_expression*)parser_const_float_1(parser)); + } + if (!out) + return false; + out = (ast_expression*)ast_binary_new(ctx, subop, + out, + (ast_expression*)parser_const_float_1(parser)); + break; + case opid2('+','='): + case opid2('-','='): + if (exprs[0]->vtype != exprs[1]->vtype || + (exprs[0]->vtype != TYPE_VECTOR && exprs[0]->vtype != TYPE_FLOAT) ) + { + ast_type_to_string(exprs[0], ty1, sizeof(ty1)); + ast_type_to_string(exprs[1], ty2, sizeof(ty2)); + compile_error(ctx, "invalid types used in expression: cannot add or subtract type %s and %s", + ty1, ty2); + return false; + } + if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) { + compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name); + } + if (ast_istype(exprs[0], ast_entfield)) + assignop = type_storep_instr[exprs[0]->vtype]; + else + assignop = type_store_instr[exprs[0]->vtype]; + switch (exprs[0]->vtype) { + case TYPE_FLOAT: + out = (ast_expression*)ast_binstore_new(ctx, assignop, + (op->id == opid2('+','=') ? INSTR_ADD_F : INSTR_SUB_F), + exprs[0], exprs[1]); + break; + case TYPE_VECTOR: + out = (ast_expression*)ast_binstore_new(ctx, assignop, + (op->id == opid2('+','=') ? INSTR_ADD_V : INSTR_SUB_V), + exprs[0], exprs[1]); + break; + default: + compile_error(ctx, "invalid types used in expression: cannot add or subtract type %s and %s", + type_name[exprs[0]->vtype], + type_name[exprs[1]->vtype]); + return false; + }; + break; + case opid2('*','='): + case opid2('/','='): + if (exprs[1]->vtype != TYPE_FLOAT || + !(exprs[0]->vtype == TYPE_FLOAT || + exprs[0]->vtype == TYPE_VECTOR)) + { + ast_type_to_string(exprs[0], ty1, sizeof(ty1)); + ast_type_to_string(exprs[1], ty2, sizeof(ty2)); + compile_error(ctx, "invalid types used in expression: %s and %s", + ty1, ty2); + return false; + } + if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) { + compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name); + } + if (ast_istype(exprs[0], ast_entfield)) + assignop = type_storep_instr[exprs[0]->vtype]; + else + assignop = type_store_instr[exprs[0]->vtype]; + switch (exprs[0]->vtype) { + case TYPE_FLOAT: + out = (ast_expression*)ast_binstore_new(ctx, assignop, + (op->id == opid2('*','=') ? INSTR_MUL_F : INSTR_DIV_F), + exprs[0], exprs[1]); + break; + case TYPE_VECTOR: + if (op->id == opid2('*','=')) { + out = (ast_expression*)ast_binstore_new(ctx, assignop, INSTR_MUL_VF, + exprs[0], exprs[1]); + } else { + /* there's no DIV_VF */ + if (CanConstFold1(exprs[1])) { + out = (ast_expression*)parser_const_float(parser, 1.0 / ConstF(1)); + } else { + out = (ast_expression*)ast_binary_new(ctx, INSTR_DIV_F, + (ast_expression*)parser_const_float_1(parser), + exprs[1]); + } + if (!out) { + compile_error(ctx, "internal error: failed to generate division"); + return false; + } + out = (ast_expression*)ast_binstore_new(ctx, assignop, INSTR_MUL_VF, + exprs[0], out); + } + break; + default: + compile_error(ctx, "invalid types used in expression: cannot add or subtract type %s and %s", + type_name[exprs[0]->vtype], + type_name[exprs[1]->vtype]); + return false; + }; + break; + case opid2('&','='): + case opid2('|','='): + if (NotSameType(TYPE_FLOAT)) { + ast_type_to_string(exprs[0], ty1, sizeof(ty1)); + ast_type_to_string(exprs[1], ty2, sizeof(ty2)); + compile_error(ctx, "invalid types used in expression: %s and %s", + ty1, ty2); + return false; + } + if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) { + compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name); + } + if (ast_istype(exprs[0], ast_entfield)) + assignop = type_storep_instr[exprs[0]->vtype]; + else + assignop = type_store_instr[exprs[0]->vtype]; + out = (ast_expression*)ast_binstore_new(ctx, assignop, + (op->id == opid2('&','=') ? INSTR_BITAND : INSTR_BITOR), + exprs[0], exprs[1]); + break; + case opid3('&','~','='): + /* This is like: a &= ~(b); + * But QC has no bitwise-not, so we implement it as + * a -= a & (b); + */ + if (NotSameType(TYPE_FLOAT)) { + ast_type_to_string(exprs[0], ty1, sizeof(ty1)); + ast_type_to_string(exprs[1], ty2, sizeof(ty2)); + compile_error(ctx, "invalid types used in expression: %s and %s", + ty1, ty2); + return false; + } + if (ast_istype(exprs[0], ast_entfield)) + assignop = type_storep_instr[exprs[0]->vtype]; + else + assignop = type_store_instr[exprs[0]->vtype]; + out = (ast_expression*)ast_binary_new(ctx, INSTR_BITAND, exprs[0], exprs[1]); + if (!out) + return false; + if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) { + compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name); + } + asbinstore = ast_binstore_new(ctx, assignop, INSTR_SUB_F, exprs[0], out); + asbinstore->keep_dest = true; + out = (ast_expression*)asbinstore; + break; + + case opid2('~', 'P'): + if (exprs[0]->vtype != TYPE_FLOAT) { + ast_type_to_string(exprs[0], ty1, sizeof(ty1)); + compile_error(ast_ctx(exprs[0]), "invalid type for bit not: %s", ty1); + return false; + } + + if(CanConstFold1(exprs[0])) + out = (ast_expression*)parser_const_float(parser, ~(qcint)ConstF(0)); + else + out = (ast_expression*) + ast_binary_new(ctx, INSTR_SUB_F, (ast_expression*)parser_const_float_neg1(parser), exprs[0]); + break; + } +#undef NotSameType + + if (!out) { + compile_error(ctx, "failed to apply operator %s", op->op); + return false; + } + + vec_push(sy->out, syexp(ctx, out)); + return true; +} + +static bool parser_close_call(parser_t *parser, shunt *sy) +{ + /* was a function call */ + ast_expression *fun; + ast_value *funval = NULL; + ast_call *call; + + size_t fid; + size_t paramcount, i; + + fid = vec_last(sy->ops).off; + vec_shrinkby(sy->ops, 1); + + /* out[fid] is the function + * everything above is parameters... + */ + if (!vec_size(sy->argc)) { + parseerror(parser, "internal error: no argument counter available"); + return false; + } + + paramcount = vec_last(sy->argc); + vec_pop(sy->argc); + + if (vec_size(sy->out) < fid) { + parseerror(parser, "internal error: broken function call%lu < %lu+%lu\n", + (unsigned long)vec_size(sy->out), + (unsigned long)fid, + (unsigned long)paramcount); + return false; + } + + fun = sy->out[fid].out; + + if (fun == intrinsic_debug_typestring) { + char ty[1024]; + if (fid+2 != vec_size(sy->out) || + vec_last(sy->out).block) + { + parseerror(parser, "intrinsic __builtin_debug_typestring requires exactly 1 parameter"); + return false; + } + ast_type_to_string(vec_last(sy->out).out, ty, sizeof(ty)); + ast_unref(vec_last(sy->out).out); + sy->out[fid] = syexp(ast_ctx(vec_last(sy->out).out), + (ast_expression*)parser_const_string(parser, ty, false)); + vec_shrinkby(sy->out, 1); + return true; + } + + call = ast_call_new(sy->ops[vec_size(sy->ops)].ctx, fun); + if (!call) + return false; + + if (fid+1 < vec_size(sy->out)) + ++paramcount; + + if (fid+1 + paramcount != vec_size(sy->out)) { + parseerror(parser, "internal error: parameter count mismatch: (%lu+1+%lu), %lu", + (unsigned long)fid, (unsigned long)paramcount, (unsigned long)vec_size(sy->out)); + return false; + } + + for (i = 0; i < paramcount; ++i) + vec_push(call->params, sy->out[fid+1 + i].out); + vec_shrinkby(sy->out, paramcount); + (void)!ast_call_check_types(call); + if (parser->max_param_count < paramcount) + parser->max_param_count = paramcount; + + if (ast_istype(fun, ast_value)) { + funval = (ast_value*)fun; + if ((fun->flags & AST_FLAG_VARIADIC) && + !(/*funval->cvq == CV_CONST && */ funval->hasvalue && funval->constval.vfunc->builtin)) + { + call->va_count = (ast_expression*)parser_const_float(parser, (double)paramcount); + } + } + + /* overwrite fid, the function, with a call */ + sy->out[fid] = syexp(call->expression.node.context, (ast_expression*)call); + + if (fun->vtype != TYPE_FUNCTION) { + parseerror(parser, "not a function (%s)", type_name[fun->vtype]); + return false; + } + + if (!fun->next) { + parseerror(parser, "could not determine function return type"); + return false; + } else { + ast_value *fval = (ast_istype(fun, ast_value) ? ((ast_value*)fun) : NULL); + + if (fun->flags & AST_FLAG_DEPRECATED) { + if (!fval) { + return !parsewarning(parser, WARN_DEPRECATED, + "call to function (which is marked deprecated)\n", + "-> it has been declared here: %s:%i", + ast_ctx(fun).file, ast_ctx(fun).line); + } + if (!fval->desc) { + return !parsewarning(parser, WARN_DEPRECATED, + "call to `%s` (which is marked deprecated)\n" + "-> `%s` declared here: %s:%i", + fval->name, fval->name, ast_ctx(fun).file, ast_ctx(fun).line); + } + return !parsewarning(parser, WARN_DEPRECATED, + "call to `%s` (deprecated: %s)\n" + "-> `%s` declared here: %s:%i", + fval->name, fval->desc, fval->name, ast_ctx(fun).file, + ast_ctx(fun).line); + } + + if (vec_size(fun->params) != paramcount && + !((fun->flags & AST_FLAG_VARIADIC) && + vec_size(fun->params) < paramcount)) + { + const char *fewmany = (vec_size(fun->params) > paramcount) ? "few" : "many"; + if (fval) + return !parsewarning(parser, WARN_INVALID_PARAMETER_COUNT, + "too %s parameters for call to %s: expected %i, got %i\n" + " -> `%s` has been declared here: %s:%i", + fewmany, fval->name, (int)vec_size(fun->params), (int)paramcount, + fval->name, ast_ctx(fun).file, (int)ast_ctx(fun).line); + else + return !parsewarning(parser, WARN_INVALID_PARAMETER_COUNT, + "too %s parameters for function call: expected %i, got %i\n" + " -> it has been declared here: %s:%i", + fewmany, (int)vec_size(fun->params), (int)paramcount, + ast_ctx(fun).file, (int)ast_ctx(fun).line); + } + } + + return true; +} + +static bool parser_close_paren(parser_t *parser, shunt *sy) +{ + if (!vec_size(sy->ops)) { + parseerror(parser, "unmatched closing paren"); + return false; + } + + while (vec_size(sy->ops)) { + if (vec_last(sy->ops).isparen) { + if (vec_last(sy->paren) == PAREN_FUNC) { + vec_pop(sy->paren); + if (!parser_close_call(parser, sy)) + return false; + break; + } + if (vec_last(sy->paren) == PAREN_EXPR) { + vec_pop(sy->paren); + if (!vec_size(sy->out)) { + compile_error(vec_last(sy->ops).ctx, "empty paren expression"); + vec_shrinkby(sy->ops, 1); + return false; + } + vec_shrinkby(sy->ops, 1); + break; + } + if (vec_last(sy->paren) == PAREN_INDEX) { + vec_pop(sy->paren); + /* pop off the parenthesis */ + vec_shrinkby(sy->ops, 1); + /* then apply the index operator */ + if (!parser_sy_apply_operator(parser, sy)) + return false; + break; + } + if (vec_last(sy->paren) == PAREN_TERNARY1) { + vec_last(sy->paren) = PAREN_TERNARY2; + /* pop off the parenthesis */ + vec_shrinkby(sy->ops, 1); + break; + } + compile_error(vec_last(sy->ops).ctx, "invalid parenthesis"); + return false; + } + if (!parser_sy_apply_operator(parser, sy)) + return false; + } + return true; +} + +static void parser_reclassify_token(parser_t *parser) +{ + size_t i; + for (i = 0; i < operator_count; ++i) { + if (!strcmp(parser_tokval(parser), operators[i].op)) { + parser->tok = TOKEN_OPERATOR; + return; + } + } +} + +static ast_expression* parse_vararg_do(parser_t *parser) +{ + ast_expression *idx, *out; + ast_value *typevar; + ast_value *funtype = parser->function->vtype; + + lex_ctx ctx = parser_ctx(parser); + + if (!parser_next(parser) || parser->tok != '(') { + parseerror(parser, "expected parameter index and type in parenthesis"); + return NULL; + } + if (!parser_next(parser)) { + parseerror(parser, "error parsing parameter index"); + return NULL; + } + + idx = parse_expression_leave(parser, true, false, false); + if (!idx) + return NULL; + + if (parser->tok != ',') { + ast_unref(idx); + parseerror(parser, "expected comma after parameter index"); + return NULL; + } + + if (!parser_next(parser) || (parser->tok != TOKEN_IDENT && parser->tok != TOKEN_TYPENAME)) { + ast_unref(idx); + parseerror(parser, "expected typename for vararg"); + return NULL; + } + + typevar = parse_typename(parser, NULL, NULL); + if (!typevar) { + ast_unref(idx); + return NULL; + } + + if (parser->tok != ')') { + ast_unref(idx); + ast_delete(typevar); + parseerror(parser, "expected closing paren"); + return NULL; + } + +#if 0 + if (!parser_next(parser)) { + ast_unref(idx); + ast_delete(typevar); + parseerror(parser, "parse error after vararg"); + return NULL; + } +#endif + + if (!parser->function->varargs) { + ast_unref(idx); + ast_delete(typevar); + parseerror(parser, "function has no variable argument list"); + return NULL; + } + + if (funtype->expression.varparam && + !ast_compare_type((ast_expression*)typevar, (ast_expression*)funtype->expression.varparam)) + { + char ty1[1024]; + char ty2[1024]; + ast_type_to_string((ast_expression*)typevar, ty1, sizeof(ty1)); + ast_type_to_string((ast_expression*)funtype->expression.varparam, ty2, sizeof(ty2)); + compile_error(ast_ctx(typevar), + "function was declared to take varargs of type `%s`, requested type is: %s", + ty2, ty1); + } + + out = (ast_expression*)ast_array_index_new(ctx, (ast_expression*)(parser->function->varargs), idx); + ast_type_adopt(out, typevar); + ast_delete(typevar); + return out; +} + +static ast_expression* parse_vararg(parser_t *parser) +{ + bool old_noops = parser->lex->flags.noops; + + ast_expression *out; + + parser->lex->flags.noops = true; + out = parse_vararg_do(parser); + + parser->lex->flags.noops = old_noops; + return out; +} + +/* not to be exposed */ +extern bool ftepp_predef_exists(const char *name); + +static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels) +{ + if (OPTS_FLAG(TRANSLATABLE_STRINGS) && + parser->tok == TOKEN_IDENT && + !strcmp(parser_tokval(parser), "_")) + { + /* a translatable string */ + ast_value *val; + + parser->lex->flags.noops = true; + if (!parser_next(parser) || parser->tok != '(') { + parseerror(parser, "use _(\"string\") to create a translatable string constant"); + return false; + } + parser->lex->flags.noops = false; + if (!parser_next(parser) || parser->tok != TOKEN_STRINGCONST) { + parseerror(parser, "expected a constant string in translatable-string extension"); + return false; + } + val = parser_const_string(parser, parser_tokval(parser), true); + if (!val) + return false; + vec_push(sy->out, syexp(parser_ctx(parser), (ast_expression*)val)); + + if (!parser_next(parser) || parser->tok != ')') { + parseerror(parser, "expected closing paren after translatable string"); + return false; + } + return true; + } + else if (parser->tok == TOKEN_DOTS) + { + ast_expression *va; + if (!OPTS_FLAG(VARIADIC_ARGS)) { + parseerror(parser, "cannot access varargs (try -fvariadic-args)"); + return false; + } + va = parse_vararg(parser); + if (!va) + return false; + vec_push(sy->out, syexp(parser_ctx(parser), va)); + return true; + } + else if (parser->tok == TOKEN_FLOATCONST) { + ast_value *val; + val = parser_const_float(parser, (parser_token(parser)->constval.f)); + if (!val) + return false; + vec_push(sy->out, syexp(parser_ctx(parser), (ast_expression*)val)); + return true; + } + else if (parser->tok == TOKEN_INTCONST || parser->tok == TOKEN_CHARCONST) { + ast_value *val; + val = parser_const_float(parser, (double)(parser_token(parser)->constval.i)); + if (!val) + return false; + vec_push(sy->out, syexp(parser_ctx(parser), (ast_expression*)val)); + return true; + } + else if (parser->tok == TOKEN_STRINGCONST) { + ast_value *val; + val = parser_const_string(parser, parser_tokval(parser), false); + if (!val) + return false; + vec_push(sy->out, syexp(parser_ctx(parser), (ast_expression*)val)); + return true; + } + else if (parser->tok == TOKEN_VECTORCONST) { + ast_value *val; + val = parser_const_vector(parser, parser_token(parser)->constval.v); + if (!val) + return false; + vec_push(sy->out, syexp(parser_ctx(parser), (ast_expression*)val)); + return true; + } + else if (parser->tok == TOKEN_IDENT) + { + const char *ctoken = parser_tokval(parser); + ast_expression *prev = vec_size(sy->out) ? vec_last(sy->out).out : NULL; + ast_expression *var; + /* a_vector.{x,y,z} */ + if (!vec_size(sy->ops) || + !vec_last(sy->ops).etype || + operators[vec_last(sy->ops).etype-1].id != opid1('.') || + (prev >= intrinsic_debug_typestring && + prev <= intrinsic_debug_typestring)) + { + /* When adding more intrinsics, fix the above condition */ + prev = NULL; + } + if (prev && prev->vtype == TYPE_VECTOR && ctoken[0] >= 'x' && ctoken[0] <= 'z' && !ctoken[1]) + { + var = (ast_expression*)parser->const_vec[ctoken[0]-'x']; + } else { + var = parser_find_var(parser, parser_tokval(parser)); + if (!var) + var = parser_find_field(parser, parser_tokval(parser)); + } + if (!var && with_labels) { + var = (ast_expression*)parser_find_label(parser, parser_tokval(parser)); + if (!with_labels) { + ast_label *lbl = ast_label_new(parser_ctx(parser), parser_tokval(parser), true); + var = (ast_expression*)lbl; + vec_push(parser->labels, lbl); + } + } + if (!var && !strcmp(parser_tokval(parser), "__FUNC__")) + var = (ast_expression*)parser_const_string(parser, parser->function->name, false); + if (!var) { + /* intrinsics */ + if (!strcmp(parser_tokval(parser), "__builtin_debug_typestring")) { + var = (ast_expression*)intrinsic_debug_typestring; + } + /* now we try for the real intrinsic hashtable. If the string + * begins with __builtin, we simply skip past it, otherwise we + * use the identifier as is. + */ + else if (!strncmp(parser_tokval(parser), "__builtin_", 10)) { + var = intrin_func(parser, parser_tokval(parser) + 10 /* skip __builtin */); + } + + if (!var) { + char *correct = NULL; + size_t i; + + /* + * sometimes people use preprocessing predefs without enabling them + * i've done this thousands of times already myself. Lets check for + * it in the predef table. And diagnose it better :) + */ + if (!OPTS_FLAG(FTEPP_PREDEFS) && ftepp_predef_exists(parser_tokval(parser))) { + parseerror(parser, "unexpected ident: %s (use -fftepp-predef to enable pre-defined macros)", parser_tokval(parser)); + return false; + } + + /* + * TODO: determine the best score for the identifier: be it + * a variable, a field. + * + * We should also consider adding correction tables for + * other things as well. + */ + if (OPTS_OPTION_BOOL(OPTION_CORRECTION)) { + correction_t corr; + correct_init(&corr); + + for (i = 0; i < vec_size(parser->correct_variables); i++) { + correct = correct_str(&corr, parser->correct_variables[i], parser_tokval(parser)); + if (strcmp(correct, parser_tokval(parser))) { + break; + } else if (correct) { + mem_d(correct); + correct = NULL; + } + } + correct_free(&corr); + + if (correct) { + parseerror(parser, "unexpected ident: %s (did you mean %s?)", parser_tokval(parser), correct); + mem_d(correct); + return false; + } + } + parseerror(parser, "unexpected ident: %s", parser_tokval(parser)); + return false; + } + } + else + { + if (ast_istype(var, ast_value)) { + ((ast_value*)var)->uses++; + } + else if (ast_istype(var, ast_member)) { + ast_member *mem = (ast_member*)var; + if (ast_istype(mem->owner, ast_value)) + ((ast_value*)(mem->owner))->uses++; + } + } + vec_push(sy->out, syexp(parser_ctx(parser), var)); + return true; + } + parseerror(parser, "unexpected token `%s`", parser_tokval(parser)); + return false; +} + +static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue, bool with_labels) +{ + ast_expression *expr = NULL; + shunt sy; + size_t i; + bool wantop = false; + /* only warn once about an assignment in a truth value because the current code + * would trigger twice on: if(a = b && ...), once for the if-truth-value, once for the && part + */ + bool warn_truthvalue = true; + + /* count the parens because an if starts with one, so the + * end of a condition is an unmatched closing paren + */ + int ternaries = 0; + + memset(&sy, 0, sizeof(sy)); + + parser->lex->flags.noops = false; + + parser_reclassify_token(parser); + + while (true) + { + if (parser->tok == TOKEN_TYPENAME) { + parseerror(parser, "unexpected typename"); + goto onerr; + } + + if (parser->tok == TOKEN_OPERATOR) + { + /* classify the operator */ + const oper_info *op; + const oper_info *olast = NULL; + size_t o; + for (o = 0; o < operator_count; ++o) { + if (((!(operators[o].flags & OP_PREFIX) == !!wantop)) && + /* !(operators[o].flags & OP_SUFFIX) && / * remove this */ + !strcmp(parser_tokval(parser), operators[o].op)) + { + break; + } + } + if (o == operator_count) { + compile_error(parser_ctx(parser), "unknown operator: %s", parser_tokval(parser)); + goto onerr; + } + /* found an operator */ + op = &operators[o]; + + /* when declaring variables, a comma starts a new variable */ + if (op->id == opid1(',') && !vec_size(sy.paren) && stopatcomma) { + /* fixup the token */ + parser->tok = ','; + break; + } + + /* a colon without a pervious question mark cannot be a ternary */ + if (!ternaries && op->id == opid2(':','?')) { + parser->tok = ':'; + break; + } + + if (op->id == opid1(',')) { + if (vec_size(sy.paren) && vec_last(sy.paren) == PAREN_TERNARY2) { + (void)!parsewarning(parser, WARN_TERNARY_PRECEDENCE, "suggesting parenthesis around ternary expression"); + } + } + + if (vec_size(sy.ops) && !vec_last(sy.ops).isparen) + olast = &operators[vec_last(sy.ops).etype-1]; + +#define IsAssignOp(x) (\ + (x) == opid1('=') || \ + (x) == opid2('+','=') || \ + (x) == opid2('-','=') || \ + (x) == opid2('*','=') || \ + (x) == opid2('/','=') || \ + (x) == opid2('%','=') || \ + (x) == opid2('&','=') || \ + (x) == opid2('|','=') || \ + (x) == opid3('&','~','=') \ + ) + if (warn_truthvalue) { + if ( (olast && IsAssignOp(olast->id) && (op->id == opid2('&','&') || op->id == opid2('|','|'))) || + (olast && IsAssignOp(op->id) && (olast->id == opid2('&','&') || olast->id == opid2('|','|'))) || + (truthvalue && !vec_size(sy.paren) && IsAssignOp(op->id)) + ) + { + (void)!parsewarning(parser, WARN_PARENTHESIS, "suggesting parenthesis around assignment used as truth value"); + warn_truthvalue = false; + } + } + + while (olast && ( + (op->prec < olast->prec) || + (op->assoc == ASSOC_LEFT && op->prec <= olast->prec) ) ) + { + if (!parser_sy_apply_operator(parser, &sy)) + goto onerr; + if (vec_size(sy.ops) && !vec_last(sy.ops).isparen) + olast = &operators[vec_last(sy.ops).etype-1]; + else + olast = NULL; + } + + if (op->id == opid1('(')) { + if (wantop) { + size_t sycount = vec_size(sy.out); + /* we expected an operator, this is the function-call operator */ + vec_push(sy.paren, PAREN_FUNC); + vec_push(sy.ops, syparen(parser_ctx(parser), sycount-1)); + vec_push(sy.argc, 0); + } else { + vec_push(sy.paren, PAREN_EXPR); + vec_push(sy.ops, syparen(parser_ctx(parser), 0)); + } + wantop = false; + } else if (op->id == opid1('[')) { + if (!wantop) { + parseerror(parser, "unexpected array subscript"); + goto onerr; + } + vec_push(sy.paren, PAREN_INDEX); + /* push both the operator and the paren, this makes life easier */ + vec_push(sy.ops, syop(parser_ctx(parser), op)); + vec_push(sy.ops, syparen(parser_ctx(parser), 0)); + wantop = false; + } else if (op->id == opid2('?',':')) { + vec_push(sy.ops, syop(parser_ctx(parser), op)); + vec_push(sy.ops, syparen(parser_ctx(parser), 0)); + wantop = false; + ++ternaries; + vec_push(sy.paren, PAREN_TERNARY1); + } else if (op->id == opid2(':','?')) { + if (!vec_size(sy.paren)) { + parseerror(parser, "unexpected colon outside ternary expression (missing parenthesis?)"); + goto onerr; + } + if (vec_last(sy.paren) != PAREN_TERNARY1) { + parseerror(parser, "unexpected colon outside ternary expression (missing parenthesis?)"); + goto onerr; + } + if (!parser_close_paren(parser, &sy)) + goto onerr; + vec_push(sy.ops, syop(parser_ctx(parser), op)); + wantop = false; + --ternaries; + } else { + vec_push(sy.ops, syop(parser_ctx(parser), op)); + wantop = !!(op->flags & OP_SUFFIX); + } + } + else if (parser->tok == ')') { + while (vec_size(sy.paren) && vec_last(sy.paren) == PAREN_TERNARY2) { + if (!parser_sy_apply_operator(parser, &sy)) + goto onerr; + } + if (!vec_size(sy.paren)) + break; + if (wantop) { + if (vec_last(sy.paren) == PAREN_TERNARY1) { + parseerror(parser, "mismatched parentheses (closing paren in ternary expression?)"); + goto onerr; + } + if (!parser_close_paren(parser, &sy)) + goto onerr; + } else { + /* must be a function call without parameters */ + if (vec_last(sy.paren) != PAREN_FUNC) { + parseerror(parser, "closing paren in invalid position"); + goto onerr; + } + if (!parser_close_paren(parser, &sy)) + goto onerr; + } + wantop = true; + } + else if (parser->tok == '(') { + parseerror(parser, "internal error: '(' should be classified as operator"); + goto onerr; + } + else if (parser->tok == '[') { + parseerror(parser, "internal error: '[' should be classified as operator"); + goto onerr; + } + else if (parser->tok == ']') { + while (vec_size(sy.paren) && vec_last(sy.paren) == PAREN_TERNARY2) { + if (!parser_sy_apply_operator(parser, &sy)) + goto onerr; + } + if (!vec_size(sy.paren)) + break; + if (vec_last(sy.paren) != PAREN_INDEX) { + parseerror(parser, "mismatched parentheses, unexpected ']'"); + goto onerr; + } + if (!parser_close_paren(parser, &sy)) + goto onerr; + wantop = true; + } + else if (!wantop) { + if (!parse_sya_operand(parser, &sy, with_labels)) + goto onerr; +#if 0 + if (vec_size(sy.paren) && vec_last(sy.ops).isparen && vec_last(sy.paren) == PAREN_FUNC) + vec_last(sy.argc)++; +#endif + wantop = true; + } + else { + /* in this case we might want to allow constant string concatenation */ + bool concatenated = false; + if (parser->tok == TOKEN_STRINGCONST && vec_size(sy.out)) { + ast_expression *lexpr = vec_last(sy.out).out; + if (ast_istype(lexpr, ast_value)) { + ast_value *last = (ast_value*)lexpr; + if (last->isimm == true && last->cvq == CV_CONST && + last->hasvalue && last->expression.vtype == TYPE_STRING) + { + char *newstr = NULL; + util_asprintf(&newstr, "%s%s", last->constval.vstring, parser_tokval(parser)); + vec_last(sy.out).out = (ast_expression*)parser_const_string(parser, newstr, false); + mem_d(newstr); + concatenated = true; + } + } + } + if (!concatenated) { + parseerror(parser, "expected operator or end of statement"); + goto onerr; + } + } + + if (!parser_next(parser)) { + goto onerr; + } + if (parser->tok == ';' || + ((!vec_size(sy.paren) || (vec_size(sy.paren) == 1 && vec_last(sy.paren) == PAREN_TERNARY2)) && + (parser->tok == ']' || parser->tok == ')' || parser->tok == '}'))) + { + break; + } + } + + while (vec_size(sy.ops)) { + if (!parser_sy_apply_operator(parser, &sy)) + goto onerr; + } + + parser->lex->flags.noops = true; + if (vec_size(sy.out) != 1) { + parseerror(parser, "expression with not 1 but %lu output values...", (unsigned long) vec_size(sy.out)); + expr = NULL; + } else + expr = sy.out[0].out; + vec_free(sy.out); + vec_free(sy.ops); + if (vec_size(sy.paren)) { + parseerror(parser, "internal error: vec_size(sy.paren) = %lu", (unsigned long)vec_size(sy.paren)); + return NULL; + } + vec_free(sy.paren); + vec_free(sy.argc); + return expr; + +onerr: + parser->lex->flags.noops = true; + for (i = 0; i < vec_size(sy.out); ++i) { + if (sy.out[i].out) + ast_unref(sy.out[i].out); + } + vec_free(sy.out); + vec_free(sy.ops); + vec_free(sy.paren); + vec_free(sy.argc); + return NULL; +} + +static ast_expression* parse_expression(parser_t *parser, bool stopatcomma, bool with_labels) +{ + ast_expression *e = parse_expression_leave(parser, stopatcomma, false, with_labels); + if (!e) + return NULL; + if (parser->tok != ';') { + parseerror(parser, "semicolon expected after expression"); + ast_unref(e); + return NULL; + } + if (!parser_next(parser)) { + ast_unref(e); + return NULL; + } + return e; +} + +static void parser_enterblock(parser_t *parser) +{ + vec_push(parser->variables, util_htnew(PARSER_HT_SIZE)); + vec_push(parser->_blocklocals, vec_size(parser->_locals)); + vec_push(parser->typedefs, util_htnew(TYPEDEF_HT_SIZE)); + vec_push(parser->_blocktypedefs, vec_size(parser->_typedefs)); + vec_push(parser->_block_ctx, parser_ctx(parser)); + + /* corrector */ + vec_push(parser->correct_variables, correct_trie_new()); + vec_push(parser->correct_variables_score, NULL); +} + +static bool parser_leaveblock(parser_t *parser) +{ + bool rv = true; + size_t locals, typedefs; + + if (vec_size(parser->variables) <= PARSER_HT_LOCALS) { + parseerror(parser, "internal error: parser_leaveblock with no block"); + return false; + } + + util_htdel(vec_last(parser->variables)); + correct_del(vec_last(parser->correct_variables), vec_last(parser->correct_variables_score)); + + vec_pop(parser->variables); + vec_pop(parser->correct_variables); + vec_pop(parser->correct_variables_score); + if (!vec_size(parser->_blocklocals)) { + parseerror(parser, "internal error: parser_leaveblock with no block (2)"); + return false; + } + + locals = vec_last(parser->_blocklocals); + vec_pop(parser->_blocklocals); + while (vec_size(parser->_locals) != locals) { + ast_expression *e = vec_last(parser->_locals); + ast_value *v = (ast_value*)e; + vec_pop(parser->_locals); + if (ast_istype(e, ast_value) && !v->uses) { + if (compile_warning(ast_ctx(v), WARN_UNUSED_VARIABLE, "unused variable: `%s`", v->name)) + rv = false; + } + } + + typedefs = vec_last(parser->_blocktypedefs); + while (vec_size(parser->_typedefs) != typedefs) { + ast_delete(vec_last(parser->_typedefs)); + vec_pop(parser->_typedefs); + } + util_htdel(vec_last(parser->typedefs)); + vec_pop(parser->typedefs); + + vec_pop(parser->_block_ctx); + + return rv; +} + +static void parser_addlocal(parser_t *parser, const char *name, ast_expression *e) +{ + vec_push(parser->_locals, e); + util_htset(vec_last(parser->variables), name, (void*)e); + + /* corrector */ + correct_add ( + vec_last(parser->correct_variables), + &vec_last(parser->correct_variables_score), + name + ); +} + +static void parser_addglobal(parser_t *parser, const char *name, ast_expression *e) +{ + vec_push(parser->globals, e); + util_htset(parser->htglobals, name, e); + + /* corrector */ + correct_add ( + parser->correct_variables[0], + &parser->correct_variables_score[0], + name + ); +} + +static ast_expression* process_condition(parser_t *parser, ast_expression *cond, bool *_ifnot) +{ + bool ifnot = false; + ast_unary *unary; + ast_expression *prev; + + if (cond->vtype == TYPE_VOID || cond->vtype >= TYPE_VARIANT) { + char ty[1024]; + ast_type_to_string(cond, ty, sizeof(ty)); + compile_error(ast_ctx(cond), "invalid type for if() condition: %s", ty); + } + + if (OPTS_FLAG(FALSE_EMPTY_STRINGS) && cond->vtype == TYPE_STRING) + { + prev = cond; + cond = (ast_expression*)ast_unary_new(ast_ctx(cond), INSTR_NOT_S, cond); + if (!cond) { + ast_unref(prev); + parseerror(parser, "internal error: failed to process condition"); + return NULL; + } + ifnot = !ifnot; + } + else if (OPTS_FLAG(CORRECT_LOGIC) && cond->vtype == TYPE_VECTOR) + { + /* vector types need to be cast to true booleans */ + ast_binary *bin = (ast_binary*)cond; + if (!OPTS_FLAG(PERL_LOGIC) || !ast_istype(cond, ast_binary) || !(bin->op == INSTR_AND || bin->op == INSTR_OR)) + { + /* in perl-logic, AND and OR take care of the -fcorrect-logic */ + prev = cond; + cond = (ast_expression*)ast_unary_new(ast_ctx(cond), INSTR_NOT_V, cond); + if (!cond) { + ast_unref(prev); + parseerror(parser, "internal error: failed to process condition"); + return NULL; + } + ifnot = !ifnot; + } + } + + unary = (ast_unary*)cond; + while (ast_istype(cond, ast_unary) && unary->op == INSTR_NOT_F) + { + cond = unary->operand; + unary->operand = NULL; + ast_delete(unary); + ifnot = !ifnot; + unary = (ast_unary*)cond; + } + + if (!cond) + parseerror(parser, "internal error: failed to process condition"); + + if (ifnot) *_ifnot = !*_ifnot; + return cond; +} + +static bool parse_if(parser_t *parser, ast_block *block, ast_expression **out) +{ + ast_ifthen *ifthen; + ast_expression *cond, *ontrue = NULL, *onfalse = NULL; + bool ifnot = false; + + lex_ctx ctx = parser_ctx(parser); + + (void)block; /* not touching */ + + /* skip the 'if', parse an optional 'not' and check for an opening paren */ + if (!parser_next(parser)) { + parseerror(parser, "expected condition or 'not'"); + return false; + } + if (parser->tok == TOKEN_IDENT && !strcmp(parser_tokval(parser), "not")) { + ifnot = true; + if (!parser_next(parser)) { + parseerror(parser, "expected condition in parenthesis"); + return false; + } + } + if (parser->tok != '(') { + parseerror(parser, "expected 'if' condition in parenthesis"); + return false; + } + /* parse into the expression */ + if (!parser_next(parser)) { + parseerror(parser, "expected 'if' condition after opening paren"); + return false; + } + /* parse the condition */ + cond = parse_expression_leave(parser, false, true, false); + if (!cond) + return false; + /* closing paren */ + if (parser->tok != ')') { + parseerror(parser, "expected closing paren after 'if' condition"); + ast_unref(cond); + return false; + } + /* parse into the 'then' branch */ + if (!parser_next(parser)) { + parseerror(parser, "expected statement for on-true branch of 'if'"); + ast_unref(cond); + return false; + } + if (!parse_statement_or_block(parser, &ontrue)) { + ast_unref(cond); + return false; + } + if (!ontrue) + ontrue = (ast_expression*)ast_block_new(parser_ctx(parser)); + /* check for an else */ + if (!strcmp(parser_tokval(parser), "else")) { + /* parse into the 'else' branch */ + if (!parser_next(parser)) { + parseerror(parser, "expected on-false branch after 'else'"); + ast_delete(ontrue); + ast_unref(cond); + return false; + } + if (!parse_statement_or_block(parser, &onfalse)) { + ast_delete(ontrue); + ast_unref(cond); + return false; + } + } + + cond = process_condition(parser, cond, &ifnot); + if (!cond) { + if (ontrue) ast_delete(ontrue); + if (onfalse) ast_delete(onfalse); + return false; + } + + if (ifnot) + ifthen = ast_ifthen_new(ctx, cond, onfalse, ontrue); + else + ifthen = ast_ifthen_new(ctx, cond, ontrue, onfalse); + *out = (ast_expression*)ifthen; + return true; +} + +static bool parse_while_go(parser_t *parser, ast_block *block, ast_expression **out); +static bool parse_while(parser_t *parser, ast_block *block, ast_expression **out) +{ + bool rv; + char *label = NULL; + + /* skip the 'while' and get the body */ + if (!parser_next(parser)) { + if (OPTS_FLAG(LOOP_LABELS)) + parseerror(parser, "expected loop label or 'while' condition in parenthesis"); + else + parseerror(parser, "expected 'while' condition in parenthesis"); + return false; + } + + if (parser->tok == ':') { + if (!OPTS_FLAG(LOOP_LABELS)) + parseerror(parser, "labeled loops not activated, try using -floop-labels"); + if (!parser_next(parser) || parser->tok != TOKEN_IDENT) { + parseerror(parser, "expected loop label"); + return false; + } + label = util_strdup(parser_tokval(parser)); + if (!parser_next(parser)) { + mem_d(label); + parseerror(parser, "expected 'while' condition in parenthesis"); + return false; + } + } + + if (parser->tok != '(') { + parseerror(parser, "expected 'while' condition in parenthesis"); + return false; + } + + vec_push(parser->breaks, label); + vec_push(parser->continues, label); + + rv = parse_while_go(parser, block, out); + if (label) + mem_d(label); + if (vec_last(parser->breaks) != label || vec_last(parser->continues) != label) { + parseerror(parser, "internal error: label stack corrupted"); + rv = false; + ast_delete(*out); + *out = NULL; + } + else { + vec_pop(parser->breaks); + vec_pop(parser->continues); + } + return rv; +} + +static bool parse_while_go(parser_t *parser, ast_block *block, ast_expression **out) +{ + ast_loop *aloop; + ast_expression *cond, *ontrue; + + bool ifnot = false; + + lex_ctx ctx = parser_ctx(parser); + + (void)block; /* not touching */ + + /* parse into the expression */ + if (!parser_next(parser)) { + parseerror(parser, "expected 'while' condition after opening paren"); + return false; + } + /* parse the condition */ + cond = parse_expression_leave(parser, false, true, false); + if (!cond) + return false; + /* closing paren */ + if (parser->tok != ')') { + parseerror(parser, "expected closing paren after 'while' condition"); + ast_unref(cond); + return false; + } + /* parse into the 'then' branch */ + if (!parser_next(parser)) { + parseerror(parser, "expected while-loop body"); + ast_unref(cond); + return false; + } + if (!parse_statement_or_block(parser, &ontrue)) { + ast_unref(cond); + return false; + } + + cond = process_condition(parser, cond, &ifnot); + if (!cond) { + ast_unref(ontrue); + return false; + } + aloop = ast_loop_new(ctx, NULL, cond, ifnot, NULL, false, NULL, ontrue); + *out = (ast_expression*)aloop; + return true; +} + +static bool parse_dowhile_go(parser_t *parser, ast_block *block, ast_expression **out); +static bool parse_dowhile(parser_t *parser, ast_block *block, ast_expression **out) +{ + bool rv; + char *label = NULL; + + /* skip the 'do' and get the body */ + if (!parser_next(parser)) { + if (OPTS_FLAG(LOOP_LABELS)) + parseerror(parser, "expected loop label or body"); + else + parseerror(parser, "expected loop body"); + return false; + } + + if (parser->tok == ':') { + if (!OPTS_FLAG(LOOP_LABELS)) + parseerror(parser, "labeled loops not activated, try using -floop-labels"); + if (!parser_next(parser) || parser->tok != TOKEN_IDENT) { + parseerror(parser, "expected loop label"); + return false; + } + label = util_strdup(parser_tokval(parser)); + if (!parser_next(parser)) { + mem_d(label); + parseerror(parser, "expected loop body"); + return false; + } + } + + vec_push(parser->breaks, label); + vec_push(parser->continues, label); + + rv = parse_dowhile_go(parser, block, out); + if (label) + mem_d(label); + if (vec_last(parser->breaks) != label || vec_last(parser->continues) != label) { + parseerror(parser, "internal error: label stack corrupted"); + rv = false; + ast_delete(*out); + *out = NULL; + } + else { + vec_pop(parser->breaks); + vec_pop(parser->continues); + } + return rv; +} + +static bool parse_dowhile_go(parser_t *parser, ast_block *block, ast_expression **out) +{ + ast_loop *aloop; + ast_expression *cond, *ontrue; + + bool ifnot = false; + + lex_ctx ctx = parser_ctx(parser); + + (void)block; /* not touching */ + + if (!parse_statement_or_block(parser, &ontrue)) + return false; + + /* expect the "while" */ + if (parser->tok != TOKEN_KEYWORD || + strcmp(parser_tokval(parser), "while")) + { + parseerror(parser, "expected 'while' and condition"); + ast_delete(ontrue); + return false; + } + + /* skip the 'while' and check for opening paren */ + if (!parser_next(parser) || parser->tok != '(') { + parseerror(parser, "expected 'while' condition in parenthesis"); + ast_delete(ontrue); + return false; + } + /* parse into the expression */ + if (!parser_next(parser)) { + parseerror(parser, "expected 'while' condition after opening paren"); + ast_delete(ontrue); + return false; + } + /* parse the condition */ + cond = parse_expression_leave(parser, false, true, false); + if (!cond) + return false; + /* closing paren */ + if (parser->tok != ')') { + parseerror(parser, "expected closing paren after 'while' condition"); + ast_delete(ontrue); + ast_unref(cond); + return false; + } + /* parse on */ + if (!parser_next(parser) || parser->tok != ';') { + parseerror(parser, "expected semicolon after condition"); + ast_delete(ontrue); + ast_unref(cond); + return false; + } + + if (!parser_next(parser)) { + parseerror(parser, "parse error"); + ast_delete(ontrue); + ast_unref(cond); + return false; + } + + cond = process_condition(parser, cond, &ifnot); + if (!cond) { + ast_delete(ontrue); + return false; + } + aloop = ast_loop_new(ctx, NULL, NULL, false, cond, ifnot, NULL, ontrue); + *out = (ast_expression*)aloop; + return true; +} + +static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **out); +static bool parse_for(parser_t *parser, ast_block *block, ast_expression **out) +{ + bool rv; + char *label = NULL; + + /* skip the 'for' and check for opening paren */ + if (!parser_next(parser)) { + if (OPTS_FLAG(LOOP_LABELS)) + parseerror(parser, "expected loop label or 'for' expressions in parenthesis"); + else + parseerror(parser, "expected 'for' expressions in parenthesis"); + return false; + } + + if (parser->tok == ':') { + if (!OPTS_FLAG(LOOP_LABELS)) + parseerror(parser, "labeled loops not activated, try using -floop-labels"); + if (!parser_next(parser) || parser->tok != TOKEN_IDENT) { + parseerror(parser, "expected loop label"); + return false; + } + label = util_strdup(parser_tokval(parser)); + if (!parser_next(parser)) { + mem_d(label); + parseerror(parser, "expected 'for' expressions in parenthesis"); + return false; + } + } + + if (parser->tok != '(') { + parseerror(parser, "expected 'for' expressions in parenthesis"); + return false; + } + + vec_push(parser->breaks, label); + vec_push(parser->continues, label); + + rv = parse_for_go(parser, block, out); + if (label) + mem_d(label); + if (vec_last(parser->breaks) != label || vec_last(parser->continues) != label) { + parseerror(parser, "internal error: label stack corrupted"); + rv = false; + ast_delete(*out); + *out = NULL; + } + else { + vec_pop(parser->breaks); + vec_pop(parser->continues); + } + return rv; +} +static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **out) +{ + ast_loop *aloop; + ast_expression *initexpr, *cond, *increment, *ontrue; + ast_value *typevar; + + bool ifnot = false; + + lex_ctx ctx = parser_ctx(parser); + + parser_enterblock(parser); + + initexpr = NULL; + cond = NULL; + increment = NULL; + ontrue = NULL; + + /* parse into the expression */ + if (!parser_next(parser)) { + parseerror(parser, "expected 'for' initializer after opening paren"); + goto onerr; + } + + typevar = NULL; + if (parser->tok == TOKEN_IDENT) + typevar = parser_find_typedef(parser, parser_tokval(parser), 0); + + if (typevar || parser->tok == TOKEN_TYPENAME) { +#if 0 + if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_GMQCC) { + if (parsewarning(parser, WARN_EXTENSIONS, + "current standard does not allow variable declarations in for-loop initializers")) + goto onerr; + } +#endif + if (!parse_variable(parser, block, true, CV_VAR, typevar, false, false, 0, NULL)) + goto onerr; + } + else if (parser->tok != ';') + { + initexpr = parse_expression_leave(parser, false, false, false); + if (!initexpr) + goto onerr; + } + + /* move on to condition */ + if (parser->tok != ';') { + parseerror(parser, "expected semicolon after for-loop initializer"); + goto onerr; + } + if (!parser_next(parser)) { + parseerror(parser, "expected for-loop condition"); + goto onerr; + } + + /* parse the condition */ + if (parser->tok != ';') { + cond = parse_expression_leave(parser, false, true, false); + if (!cond) + goto onerr; + } + + /* move on to incrementor */ + if (parser->tok != ';') { + parseerror(parser, "expected semicolon after for-loop initializer"); + goto onerr; + } + if (!parser_next(parser)) { + parseerror(parser, "expected for-loop condition"); + goto onerr; + } + + /* parse the incrementor */ + if (parser->tok != ')') { + lex_ctx condctx = parser_ctx(parser); + increment = parse_expression_leave(parser, false, false, false); + if (!increment) + goto onerr; + if (!ast_side_effects(increment)) { + if (compile_warning(condctx, WARN_EFFECTLESS_STATEMENT, "statement has no effect")) + goto onerr; + } + } + + /* closing paren */ + if (parser->tok != ')') { + parseerror(parser, "expected closing paren after 'for-loop' incrementor"); + goto onerr; + } + /* parse into the 'then' branch */ + if (!parser_next(parser)) { + parseerror(parser, "expected for-loop body"); + goto onerr; + } + if (!parse_statement_or_block(parser, &ontrue)) + goto onerr; + + if (cond) { + cond = process_condition(parser, cond, &ifnot); + if (!cond) + goto onerr; + } + aloop = ast_loop_new(ctx, initexpr, cond, ifnot, NULL, false, increment, ontrue); + *out = (ast_expression*)aloop; + + if (!parser_leaveblock(parser)) { + ast_delete(aloop); + return false; + } + return true; +onerr: + if (initexpr) ast_unref(initexpr); + if (cond) ast_unref(cond); + if (increment) ast_unref(increment); + (void)!parser_leaveblock(parser); + return false; +} + +static bool parse_return(parser_t *parser, ast_block *block, ast_expression **out) +{ + ast_expression *exp = NULL; + ast_expression *var = NULL; + ast_return *ret = NULL; + ast_value *retval = parser->function->return_value; + ast_value *expected = parser->function->vtype; + + lex_ctx ctx = parser_ctx(parser); + + (void)block; /* not touching */ + + if (!parser_next(parser)) { + parseerror(parser, "expected return expression"); + return false; + } + + /* return assignments */ + if (parser->tok == '=') { + if (!OPTS_FLAG(RETURN_ASSIGNMENTS)) { + parseerror(parser, "return assignments not activated, try using -freturn-assigments"); + return false; + } + + if (type_store_instr[expected->expression.next->vtype] == VINSTR_END) { + char ty1[1024]; + ast_type_to_string(expected->expression.next, ty1, sizeof(ty1)); + parseerror(parser, "invalid return type: `%s'", ty1); + return false; + } + + if (!parser_next(parser)) { + parseerror(parser, "expected return assignment expression"); + return false; + } + + if (!(exp = parse_expression_leave(parser, false, false, false))) + return false; + + /* prepare the return value */ + if (!retval) { + retval = ast_value_new(ctx, "#LOCAL_RETURN", TYPE_VOID); + ast_type_adopt(retval, expected->expression.next); + parser->function->return_value = retval; + } + + if (!ast_compare_type(exp, (ast_expression*)retval)) { + char ty1[1024], ty2[1024]; + ast_type_to_string(exp, ty1, sizeof(ty1)); + ast_type_to_string(&retval->expression, ty2, sizeof(ty2)); + parseerror(parser, "invalid type for return value: `%s', expected `%s'", ty1, ty2); + } + + /* store to 'return' local variable */ + var = (ast_expression*)ast_store_new( + ctx, + type_store_instr[expected->expression.next->vtype], + (ast_expression*)retval, exp); + + if (!var) { + ast_unref(exp); + return false; + } + + if (parser->tok != ';') + parseerror(parser, "missing semicolon after return assignment"); + else if (!parser_next(parser)) + parseerror(parser, "parse error after return assignment"); + + *out = var; + return true; + } + + if (parser->tok != ';') { + exp = parse_expression(parser, false, false); + if (!exp) + return false; + + if (exp->vtype != TYPE_NIL && + exp->vtype != ((ast_expression*)expected)->next->vtype) + { + parseerror(parser, "return with invalid expression"); + } + + ret = ast_return_new(ctx, exp); + if (!ret) { + ast_unref(exp); + return false; + } + } else { + if (!parser_next(parser)) + parseerror(parser, "parse error"); + + if (!retval && expected->expression.next->vtype != TYPE_VOID) + { + (void)!parsewarning(parser, WARN_MISSING_RETURN_VALUES, "return without value"); + } + ret = ast_return_new(ctx, (ast_expression*)retval); + } + *out = (ast_expression*)ret; + return true; +} + +static bool parse_break_continue(parser_t *parser, ast_block *block, ast_expression **out, bool is_continue) +{ + size_t i; + unsigned int levels = 0; + lex_ctx ctx = parser_ctx(parser); + const char **loops = (is_continue ? parser->continues : parser->breaks); + + (void)block; /* not touching */ + if (!parser_next(parser)) { + parseerror(parser, "expected semicolon or loop label"); + return false; + } + + if (!vec_size(loops)) { + if (is_continue) + parseerror(parser, "`continue` can only be used inside loops"); + else + parseerror(parser, "`break` can only be used inside loops or switches"); + } + + if (parser->tok == TOKEN_IDENT) { + if (!OPTS_FLAG(LOOP_LABELS)) + parseerror(parser, "labeled loops not activated, try using -floop-labels"); + i = vec_size(loops); + while (i--) { + if (loops[i] && !strcmp(loops[i], parser_tokval(parser))) + break; + if (!i) { + parseerror(parser, "no such loop to %s: `%s`", + (is_continue ? "continue" : "break out of"), + parser_tokval(parser)); + return false; + } + ++levels; + } + if (!parser_next(parser)) { + parseerror(parser, "expected semicolon"); + return false; + } + } + + if (parser->tok != ';') { + parseerror(parser, "expected semicolon"); + return false; + } + + if (!parser_next(parser)) + parseerror(parser, "parse error"); + + *out = (ast_expression*)ast_breakcont_new(ctx, is_continue, levels); + return true; +} + +/* returns true when it was a variable qualifier, false otherwise! + * on error, cvq is set to CV_WRONG + */ +static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *noref, bool *is_static, uint32_t *_flags, char **message) +{ + bool had_const = false; + bool had_var = false; + bool had_noref = false; + bool had_attrib = false; + bool had_static = false; + uint32_t flags = 0; + + *cvq = CV_NONE; + for (;;) { + if (parser->tok == TOKEN_ATTRIBUTE_OPEN) { + had_attrib = true; + /* parse an attribute */ + if (!parser_next(parser)) { + parseerror(parser, "expected attribute after `[[`"); + *cvq = CV_WRONG; + return false; + } + if (!strcmp(parser_tokval(parser), "noreturn")) { + flags |= AST_FLAG_NORETURN; + if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) { + parseerror(parser, "`noreturn` attribute has no parameters, expected `]]`"); + *cvq = CV_WRONG; + return false; + } + } + else if (!strcmp(parser_tokval(parser), "noref")) { + had_noref = true; + if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) { + parseerror(parser, "`noref` attribute has no parameters, expected `]]`"); + *cvq = CV_WRONG; + return false; + } + } + else if (!strcmp(parser_tokval(parser), "inline")) { + flags |= AST_FLAG_INLINE; + if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) { + parseerror(parser, "`noref` attribute has no parameters, expected `]]`"); + *cvq = CV_WRONG; + return false; + } + } + else if (!strcmp(parser_tokval(parser), "alias") && !(flags & AST_FLAG_ALIAS)) { + flags |= AST_FLAG_ALIAS; + *message = NULL; + + if (!parser_next(parser)) { + parseerror(parser, "parse error in attribute"); + goto argerr; + } + + if (parser->tok == '(') { + if (!parser_next(parser) || parser->tok != TOKEN_STRINGCONST) { + parseerror(parser, "`alias` attribute missing parameter"); + goto argerr; + } + + *message = util_strdup(parser_tokval(parser)); + + if (!parser_next(parser)) { + parseerror(parser, "parse error in attribute"); + goto argerr; + } + + if (parser->tok != ')') { + parseerror(parser, "`alias` attribute expected `)` after parameter"); + goto argerr; + } + + if (!parser_next(parser)) { + parseerror(parser, "parse error in attribute"); + goto argerr; + } + } + + if (parser->tok != TOKEN_ATTRIBUTE_CLOSE) { + parseerror(parser, "`alias` attribute expected `]]`"); + goto argerr; + } + } + else if (!strcmp(parser_tokval(parser), "deprecated") && !(flags & AST_FLAG_DEPRECATED)) { + flags |= AST_FLAG_DEPRECATED; + *message = NULL; + + if (!parser_next(parser)) { + parseerror(parser, "parse error in attribute"); + goto argerr; + } + + if (parser->tok == '(') { + if (!parser_next(parser) || parser->tok != TOKEN_STRINGCONST) { + parseerror(parser, "`deprecated` attribute missing parameter"); + goto argerr; + } + + *message = util_strdup(parser_tokval(parser)); + + if (!parser_next(parser)) { + parseerror(parser, "parse error in attribute"); + goto argerr; + } + + if(parser->tok != ')') { + parseerror(parser, "`deprecated` attribute expected `)` after parameter"); + goto argerr; + } + + if (!parser_next(parser)) { + parseerror(parser, "parse error in attribute"); + goto argerr; + } + } + /* no message */ + if (parser->tok != TOKEN_ATTRIBUTE_CLOSE) { + parseerror(parser, "`deprecated` attribute expected `]]`"); + + argerr: /* ugly */ + if (*message) mem_d(*message); + *message = NULL; + *cvq = CV_WRONG; + return false; + } + } + else + { + /* Skip tokens until we hit a ]] */ + (void)!parsewarning(parser, WARN_UNKNOWN_ATTRIBUTE, "unknown attribute starting with `%s`", parser_tokval(parser)); + while (parser->tok != TOKEN_ATTRIBUTE_CLOSE) { + if (!parser_next(parser)) { + parseerror(parser, "error inside attribute"); + *cvq = CV_WRONG; + return false; + } + } + } + } + else if (with_local && !strcmp(parser_tokval(parser), "static")) + had_static = true; + else if (!strcmp(parser_tokval(parser), "const")) + had_const = true; + else if (!strcmp(parser_tokval(parser), "var")) + had_var = true; + else if (with_local && !strcmp(parser_tokval(parser), "local")) + had_var = true; + else if (!strcmp(parser_tokval(parser), "noref")) + had_noref = true; + else if (!had_const && !had_var && !had_noref && !had_attrib && !had_static && !flags) { + return false; + } + else + break; + if (!parser_next(parser)) + goto onerr; + } + if (had_const) + *cvq = CV_CONST; + else if (had_var) + *cvq = CV_VAR; + else + *cvq = CV_NONE; + *noref = had_noref; + *is_static = had_static; + *_flags = flags; + return true; +onerr: + parseerror(parser, "parse error after variable qualifier"); + *cvq = CV_WRONG; + return true; +} + +static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression **out); +static bool parse_switch(parser_t *parser, ast_block *block, ast_expression **out) +{ + bool rv; + char *label = NULL; + + /* skip the 'while' and get the body */ + if (!parser_next(parser)) { + if (OPTS_FLAG(LOOP_LABELS)) + parseerror(parser, "expected loop label or 'switch' operand in parenthesis"); + else + parseerror(parser, "expected 'switch' operand in parenthesis"); + return false; + } + + if (parser->tok == ':') { + if (!OPTS_FLAG(LOOP_LABELS)) + parseerror(parser, "labeled loops not activated, try using -floop-labels"); + if (!parser_next(parser) || parser->tok != TOKEN_IDENT) { + parseerror(parser, "expected loop label"); + return false; + } + label = util_strdup(parser_tokval(parser)); + if (!parser_next(parser)) { + mem_d(label); + parseerror(parser, "expected 'switch' operand in parenthesis"); + return false; + } + } + + if (parser->tok != '(') { + parseerror(parser, "expected 'switch' operand in parenthesis"); + return false; + } + + vec_push(parser->breaks, label); + + rv = parse_switch_go(parser, block, out); + if (label) + mem_d(label); + if (vec_last(parser->breaks) != label) { + parseerror(parser, "internal error: label stack corrupted"); + rv = false; + ast_delete(*out); + *out = NULL; + } + else { + vec_pop(parser->breaks); + } + return rv; +} + +static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression **out) +{ + ast_expression *operand; + ast_value *opval; + ast_value *typevar; + ast_switch *switchnode; + ast_switch_case swcase; + + int cvq; + bool noref, is_static; + uint32_t qflags = 0; + + lex_ctx ctx = parser_ctx(parser); + + (void)block; /* not touching */ + (void)opval; + + /* parse into the expression */ + if (!parser_next(parser)) { + parseerror(parser, "expected switch operand"); + return false; + } + /* parse the operand */ + operand = parse_expression_leave(parser, false, false, false); + if (!operand) + return false; + + switchnode = ast_switch_new(ctx, operand); + + /* closing paren */ + if (parser->tok != ')') { + ast_delete(switchnode); + parseerror(parser, "expected closing paren after 'switch' operand"); + return false; + } + + /* parse over the opening paren */ + if (!parser_next(parser) || parser->tok != '{') { + ast_delete(switchnode); + parseerror(parser, "expected list of cases"); + return false; + } + + if (!parser_next(parser)) { + ast_delete(switchnode); + parseerror(parser, "expected 'case' or 'default'"); + return false; + } + + /* new block; allow some variables to be declared here */ + parser_enterblock(parser); + while (true) { + typevar = NULL; + if (parser->tok == TOKEN_IDENT) + typevar = parser_find_typedef(parser, parser_tokval(parser), 0); + if (typevar || parser->tok == TOKEN_TYPENAME) { + if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false, 0, NULL)) { + ast_delete(switchnode); + return false; + } + continue; + } + if (parse_qualifiers(parser, true, &cvq, &noref, &is_static, &qflags, NULL)) + { + if (cvq == CV_WRONG) { + ast_delete(switchnode); + return false; + } + if (!parse_variable(parser, block, false, cvq, NULL, noref, is_static, qflags, NULL)) { + ast_delete(switchnode); + return false; + } + continue; + } + break; + } + + /* case list! */ + while (parser->tok != '}') { + ast_block *caseblock; + + if (!strcmp(parser_tokval(parser), "case")) { + if (!parser_next(parser)) { + ast_delete(switchnode); + parseerror(parser, "expected expression for case"); + return false; + } + swcase.value = parse_expression_leave(parser, false, false, false); + if (!swcase.value) { + ast_delete(switchnode); + parseerror(parser, "expected expression for case"); + return false; + } + if (!OPTS_FLAG(RELAXED_SWITCH)) { + if (!ast_istype(swcase.value, ast_value)) { /* || ((ast_value*)swcase.value)->cvq != CV_CONST) { */ + parseerror(parser, "case on non-constant values need to be explicitly enabled via -frelaxed-switch"); + ast_unref(operand); + return false; + } + } + } + else if (!strcmp(parser_tokval(parser), "default")) { + swcase.value = NULL; + if (!parser_next(parser)) { + ast_delete(switchnode); + parseerror(parser, "expected colon"); + return false; + } + } + else { + ast_delete(switchnode); + parseerror(parser, "expected 'case' or 'default'"); + return false; + } + + /* Now the colon and body */ + if (parser->tok != ':') { + if (swcase.value) ast_unref(swcase.value); + ast_delete(switchnode); + parseerror(parser, "expected colon"); + return false; + } + + if (!parser_next(parser)) { + if (swcase.value) ast_unref(swcase.value); + ast_delete(switchnode); + parseerror(parser, "expected statements or case"); + return false; + } + caseblock = ast_block_new(parser_ctx(parser)); + if (!caseblock) { + if (swcase.value) ast_unref(swcase.value); + ast_delete(switchnode); + return false; + } + swcase.code = (ast_expression*)caseblock; + vec_push(switchnode->cases, swcase); + while (true) { + ast_expression *expr; + if (parser->tok == '}') + break; + if (parser->tok == TOKEN_KEYWORD) { + if (!strcmp(parser_tokval(parser), "case") || + !strcmp(parser_tokval(parser), "default")) + { + break; + } + } + if (!parse_statement(parser, caseblock, &expr, true)) { + ast_delete(switchnode); + return false; + } + if (!expr) + continue; + if (!ast_block_add_expr(caseblock, expr)) { + ast_delete(switchnode); + return false; + } + } + } + + parser_leaveblock(parser); + + /* closing paren */ + if (parser->tok != '}') { + ast_delete(switchnode); + parseerror(parser, "expected closing paren of case list"); + return false; + } + if (!parser_next(parser)) { + ast_delete(switchnode); + parseerror(parser, "parse error after switch"); + return false; + } + *out = (ast_expression*)switchnode; + return true; +} + +/* parse computed goto sides */ +static ast_expression *parse_goto_computed(parser_t *parser, ast_expression **side) { + ast_expression *on_true; + ast_expression *on_false; + ast_expression *cond; + + if (!*side) + return NULL; + + if (ast_istype(*side, ast_ternary)) { + ast_ternary *tern = (ast_ternary*)*side; + on_true = parse_goto_computed(parser, &tern->on_true); + on_false = parse_goto_computed(parser, &tern->on_false); + + if (!on_true || !on_false) { + parseerror(parser, "expected label or expression in ternary"); + if (on_true) ast_unref(on_true); + if (on_false) ast_unref(on_false); + return NULL; + } + + cond = tern->cond; + tern->cond = NULL; + ast_delete(tern); + *side = NULL; + return (ast_expression*)ast_ifthen_new(parser_ctx(parser), cond, on_true, on_false); + } else if (ast_istype(*side, ast_label)) { + ast_goto *gt = ast_goto_new(parser_ctx(parser), ((ast_label*)*side)->name); + ast_goto_set_label(gt, ((ast_label*)*side)); + *side = NULL; + return (ast_expression*)gt; + } + return NULL; +} + +static bool parse_goto(parser_t *parser, ast_expression **out) +{ + ast_goto *gt = NULL; + ast_expression *lbl; + + if (!parser_next(parser)) + return false; + + if (parser->tok != TOKEN_IDENT) { + ast_expression *expression; + + /* could be an expression i.e computed goto :-) */ + if (parser->tok != '(') { + parseerror(parser, "expected label name after `goto`"); + return false; + } + + /* failed to parse expression for goto */ + if (!(expression = parse_expression(parser, false, true)) || + !(*out = parse_goto_computed(parser, &expression))) { + parseerror(parser, "invalid goto expression"); + ast_unref(expression); + return false; + } + + return true; + } + + /* not computed goto */ + gt = ast_goto_new(parser_ctx(parser), parser_tokval(parser)); + lbl = parser_find_label(parser, gt->name); + if (lbl) { + if (!ast_istype(lbl, ast_label)) { + parseerror(parser, "internal error: label is not an ast_label"); + ast_delete(gt); + return false; + } + ast_goto_set_label(gt, (ast_label*)lbl); + } + else + vec_push(parser->gotos, gt); + + if (!parser_next(parser) || parser->tok != ';') { + parseerror(parser, "semicolon expected after goto label"); + return false; + } + if (!parser_next(parser)) { + parseerror(parser, "parse error after goto"); + return false; + } + + *out = (ast_expression*)gt; + return true; +} + +static bool parse_skipwhite(parser_t *parser) +{ + do { + if (!parser_next(parser)) + return false; + } while (parser->tok == TOKEN_WHITE && parser->tok < TOKEN_ERROR); + return parser->tok < TOKEN_ERROR; +} + +static bool parse_eol(parser_t *parser) +{ + if (!parse_skipwhite(parser)) + return false; + return parser->tok == TOKEN_EOL; +} + +static bool parse_pragma_do(parser_t *parser) +{ + if (!parser_next(parser) || + parser->tok != TOKEN_IDENT || + strcmp(parser_tokval(parser), "pragma")) + { + parseerror(parser, "expected `pragma` keyword after `#`, got `%s`", parser_tokval(parser)); + return false; + } + if (!parse_skipwhite(parser) || parser->tok != TOKEN_IDENT) { + parseerror(parser, "expected pragma, got `%s`", parser_tokval(parser)); + return false; + } + + if (!strcmp(parser_tokval(parser), "noref")) { + if (!parse_skipwhite(parser) || parser->tok != TOKEN_INTCONST) { + parseerror(parser, "`noref` pragma requires an argument: 0 or 1"); + return false; + } + parser->noref = !!parser_token(parser)->constval.i; + if (!parse_eol(parser)) { + parseerror(parser, "parse error after `noref` pragma"); + return false; + } + } + else + { + (void)!parsewarning(parser, WARN_UNKNOWN_PRAGMAS, "ignoring #pragma %s", parser_tokval(parser)); + return false; + } + + return true; +} + +static bool parse_pragma(parser_t *parser) +{ + bool rv; + parser->lex->flags.preprocessing = true; + parser->lex->flags.mergelines = true; + rv = parse_pragma_do(parser); + if (parser->tok != TOKEN_EOL) { + parseerror(parser, "junk after pragma"); + rv = false; + } + parser->lex->flags.preprocessing = false; + parser->lex->flags.mergelines = false; + if (!parser_next(parser)) { + parseerror(parser, "parse error after pragma"); + rv = false; + } + return rv; +} + +static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out, bool allow_cases) +{ + bool noref, is_static; + int cvq = CV_NONE; + uint32_t qflags = 0; + ast_value *typevar = NULL; + char *vstring = NULL; + + *out = NULL; + + if (parser->tok == TOKEN_IDENT) + typevar = parser_find_typedef(parser, parser_tokval(parser), 0); + + if (typevar || parser->tok == TOKEN_TYPENAME || parser->tok == '.') + { + /* local variable */ + if (!block) { + parseerror(parser, "cannot declare a variable from here"); + return false; + } + if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) { + if (parsewarning(parser, WARN_EXTENSIONS, "missing 'local' keyword when declaring a local variable")) + return false; + } + if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false, 0, NULL)) + return false; + return true; + } + else if (parse_qualifiers(parser, !!block, &cvq, &noref, &is_static, &qflags, &vstring)) + { + if (cvq == CV_WRONG) + return false; + return parse_variable(parser, block, true, cvq, NULL, noref, is_static, qflags, vstring); + } + else if (parser->tok == TOKEN_KEYWORD) + { + if (!strcmp(parser_tokval(parser), "__builtin_debug_printtype")) + { + char ty[1024]; + ast_value *tdef; + + if (!parser_next(parser)) { + parseerror(parser, "parse error after __builtin_debug_printtype"); + return false; + } + + if (parser->tok == TOKEN_IDENT && (tdef = parser_find_typedef(parser, parser_tokval(parser), 0))) + { + ast_type_to_string((ast_expression*)tdef, ty, sizeof(ty)); + con_out("__builtin_debug_printtype: `%s`=`%s`\n", tdef->name, ty); + if (!parser_next(parser)) { + parseerror(parser, "parse error after __builtin_debug_printtype typename argument"); + return false; + } + } + else + { + if (!parse_statement(parser, block, out, allow_cases)) + return false; + if (!*out) + con_out("__builtin_debug_printtype: got no output node\n"); + else + { + ast_type_to_string(*out, ty, sizeof(ty)); + con_out("__builtin_debug_printtype: `%s`\n", ty); + } + } + return true; + } + else if (!strcmp(parser_tokval(parser), "return")) + { + return parse_return(parser, block, out); + } + else if (!strcmp(parser_tokval(parser), "if")) + { + return parse_if(parser, block, out); + } + else if (!strcmp(parser_tokval(parser), "while")) + { + return parse_while(parser, block, out); + } + else if (!strcmp(parser_tokval(parser), "do")) + { + return parse_dowhile(parser, block, out); + } + else if (!strcmp(parser_tokval(parser), "for")) + { + if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) { + if (parsewarning(parser, WARN_EXTENSIONS, "for loops are not recognized in the original Quake C standard, to enable try an alternate standard --std=?")) + return false; + } + return parse_for(parser, block, out); + } + else if (!strcmp(parser_tokval(parser), "break")) + { + return parse_break_continue(parser, block, out, false); + } + else if (!strcmp(parser_tokval(parser), "continue")) + { + return parse_break_continue(parser, block, out, true); + } + else if (!strcmp(parser_tokval(parser), "switch")) + { + return parse_switch(parser, block, out); + } + else if (!strcmp(parser_tokval(parser), "case") || + !strcmp(parser_tokval(parser), "default")) + { + if (!allow_cases) { + parseerror(parser, "unexpected 'case' label"); + return false; + } + return true; + } + else if (!strcmp(parser_tokval(parser), "goto")) + { + return parse_goto(parser, out); + } + else if (!strcmp(parser_tokval(parser), "typedef")) + { + if (!parser_next(parser)) { + parseerror(parser, "expected type definition after 'typedef'"); + return false; + } + return parse_typedef(parser); + } + parseerror(parser, "Unexpected keyword: `%s'", parser_tokval(parser)); + return false; + } + else if (parser->tok == '{') + { + ast_block *inner; + inner = parse_block(parser); + if (!inner) + return false; + *out = (ast_expression*)inner; + return true; + } + else if (parser->tok == ':') + { + size_t i; + ast_label *label; + if (!parser_next(parser)) { + parseerror(parser, "expected label name"); + return false; + } + if (parser->tok != TOKEN_IDENT) { + parseerror(parser, "label must be an identifier"); + return false; + } + label = (ast_label*)parser_find_label(parser, parser_tokval(parser)); + if (label) { + if (!label->undefined) { + parseerror(parser, "label `%s` already defined", label->name); + return false; + } + label->undefined = false; + } + else { + label = ast_label_new(parser_ctx(parser), parser_tokval(parser), false); + vec_push(parser->labels, label); + } + *out = (ast_expression*)label; + if (!parser_next(parser)) { + parseerror(parser, "parse error after label"); + return false; + } + for (i = 0; i < vec_size(parser->gotos); ++i) { + if (!strcmp(parser->gotos[i]->name, label->name)) { + ast_goto_set_label(parser->gotos[i], label); + vec_remove(parser->gotos, i, 1); + --i; + } + } + return true; + } + else if (parser->tok == ';') + { + if (!parser_next(parser)) { + parseerror(parser, "parse error after empty statement"); + return false; + } + return true; + } + else + { + lex_ctx ctx = parser_ctx(parser); + ast_expression *exp = parse_expression(parser, false, false); + if (!exp) + return false; + *out = exp; + if (!ast_side_effects(exp)) { + if (compile_warning(ctx, WARN_EFFECTLESS_STATEMENT, "statement has no effect")) + return false; + } + return true; + } +} + +static bool parse_enum(parser_t *parser) +{ + bool flag = false; + bool reverse = false; + qcfloat num = 0; + ast_value **values = NULL; + ast_value *var = NULL; + ast_value *asvalue; + + ast_expression *old; + + if (!parser_next(parser) || (parser->tok != '{' && parser->tok != ':')) { + parseerror(parser, "expected `{` or `:` after `enum` keyword"); + return false; + } + + /* enumeration attributes (can add more later) */ + if (parser->tok == ':') { + if (!parser_next(parser) || parser->tok != TOKEN_IDENT){ + parseerror(parser, "expected `flag` or `reverse` for enumeration attribute"); + return false; + } + + /* attributes? */ + if (!strcmp(parser_tokval(parser), "flag")) { + num = 1; + flag = true; + } + else if (!strcmp(parser_tokval(parser), "reverse")) { + reverse = true; + } + else { + parseerror(parser, "invalid attribute `%s` for enumeration", parser_tokval(parser)); + return false; + } + + if (!parser_next(parser) || parser->tok != '{') { + parseerror(parser, "expected `{` after enum attribute "); + return false; + } + } + + while (true) { + if (!parser_next(parser) || parser->tok != TOKEN_IDENT) { + if (parser->tok == '}') { + /* allow an empty enum */ + break; + } + parseerror(parser, "expected identifier or `}`"); + goto onerror; + } + + old = parser_find_field(parser, parser_tokval(parser)); + if (!old) + old = parser_find_global(parser, parser_tokval(parser)); + if (old) { + parseerror(parser, "value `%s` has already been declared here: %s:%i", + parser_tokval(parser), ast_ctx(old).file, ast_ctx(old).line); + goto onerror; + } + + var = ast_value_new(parser_ctx(parser), parser_tokval(parser), TYPE_FLOAT); + vec_push(values, var); + var->cvq = CV_CONST; + var->hasvalue = true; + + /* for flagged enumerations increment in POTs of TWO */ + var->constval.vfloat = (flag) ? (num *= 2) : (num ++); + parser_addglobal(parser, var->name, (ast_expression*)var); + + if (!parser_next(parser)) { + parseerror(parser, "expected `=`, `}` or comma after identifier"); + goto onerror; + } + + if (parser->tok == ',') + continue; + if (parser->tok == '}') + break; + if (parser->tok != '=') { + parseerror(parser, "expected `=`, `}` or comma after identifier"); + goto onerror; + } + + if (!parser_next(parser)) { + parseerror(parser, "expected expression after `=`"); + goto onerror; + } + + /* We got a value! */ + old = parse_expression_leave(parser, true, false, false); + asvalue = (ast_value*)old; + if (!ast_istype(old, ast_value) || asvalue->cvq != CV_CONST || !asvalue->hasvalue) { + compile_error(ast_ctx(var), "constant value or expression expected"); + goto onerror; + } + num = (var->constval.vfloat = asvalue->constval.vfloat) + 1; + + if (parser->tok == '}') + break; + if (parser->tok != ',') { + parseerror(parser, "expected `}` or comma after expression"); + goto onerror; + } + } + + /* patch them all (for reversed attribute) */ + if (reverse) { + size_t i; + for (i = 0; i < vec_size(values); i++) + values[i]->constval.vfloat = vec_size(values) - i - 1; + } + + if (parser->tok != '}') { + parseerror(parser, "internal error: breaking without `}`"); + goto onerror; + } + + if (!parser_next(parser) || parser->tok != ';') { + parseerror(parser, "expected semicolon after enumeration"); + goto onerror; + } + + if (!parser_next(parser)) { + parseerror(parser, "parse error after enumeration"); + goto onerror; + } + + vec_free(values); + return true; + +onerror: + vec_free(values); + return false; +} + +static bool parse_block_into(parser_t *parser, ast_block *block) +{ + bool retval = true; + + parser_enterblock(parser); + + if (!parser_next(parser)) { /* skip the '{' */ + parseerror(parser, "expected function body"); + goto cleanup; + } + + while (parser->tok != TOKEN_EOF && parser->tok < TOKEN_ERROR) + { + ast_expression *expr = NULL; + if (parser->tok == '}') + break; + + if (!parse_statement(parser, block, &expr, false)) { + /* parseerror(parser, "parse error"); */ + block = NULL; + goto cleanup; + } + if (!expr) + continue; + if (!ast_block_add_expr(block, expr)) { + ast_delete(block); + block = NULL; + goto cleanup; + } + } + + if (parser->tok != '}') { + block = NULL; + } else { + (void)parser_next(parser); + } + +cleanup: + if (!parser_leaveblock(parser)) + retval = false; + return retval && !!block; +} + +static ast_block* parse_block(parser_t *parser) +{ + ast_block *block; + block = ast_block_new(parser_ctx(parser)); + if (!block) + return NULL; + if (!parse_block_into(parser, block)) { + ast_block_delete(block); + return NULL; + } + return block; +} + +static bool parse_statement_or_block(parser_t *parser, ast_expression **out) +{ + if (parser->tok == '{') { + *out = (ast_expression*)parse_block(parser); + return !!*out; + } + return parse_statement(parser, NULL, out, false); +} + +static bool create_vector_members(ast_value *var, ast_member **me) +{ + size_t i; + size_t len = strlen(var->name); + + for (i = 0; i < 3; ++i) { + char *name = (char*)mem_a(len+3); + memcpy(name, var->name, len); + name[len+0] = '_'; + name[len+1] = 'x'+i; + name[len+2] = 0; + me[i] = ast_member_new(ast_ctx(var), (ast_expression*)var, i, name); + mem_d(name); + if (!me[i]) + break; + } + if (i == 3) + return true; + + /* unroll */ + do { ast_member_delete(me[--i]); } while(i); + return false; +} + +static bool parse_function_body(parser_t *parser, ast_value *var) +{ + ast_block *block = NULL; + ast_function *func; + ast_function *old; + size_t parami; + + ast_expression *framenum = NULL; + ast_expression *nextthink = NULL; + /* None of the following have to be deleted */ + ast_expression *fld_think = NULL, *fld_nextthink = NULL, *fld_frame = NULL; + ast_expression *gbl_time = NULL, *gbl_self = NULL; + bool has_frame_think; + + bool retval = true; + + has_frame_think = false; + old = parser->function; + + if (var->expression.flags & AST_FLAG_ALIAS) { + parseerror(parser, "function aliases cannot have bodies"); + return false; + } + + if (vec_size(parser->gotos) || vec_size(parser->labels)) { + parseerror(parser, "gotos/labels leaking"); + return false; + } + + if (!OPTS_FLAG(VARIADIC_ARGS) && var->expression.flags & AST_FLAG_VARIADIC) { + if (parsewarning(parser, WARN_VARIADIC_FUNCTION, + "variadic function with implementation will not be able to access additional parameters (try -fvariadic-args)")) + { + return false; + } + } + + if (parser->tok == '[') { + /* got a frame definition: [ framenum, nextthink ] + * this translates to: + * self.frame = framenum; + * self.nextthink = time + 0.1; + * self.think = nextthink; + */ + nextthink = NULL; + + fld_think = parser_find_field(parser, "think"); + fld_nextthink = parser_find_field(parser, "nextthink"); + fld_frame = parser_find_field(parser, "frame"); + if (!fld_think || !fld_nextthink || !fld_frame) { + parseerror(parser, "cannot use [frame,think] notation without the required fields"); + parseerror(parser, "please declare the following entityfields: `frame`, `think`, `nextthink`"); + return false; + } + gbl_time = parser_find_global(parser, "time"); + gbl_self = parser_find_global(parser, "self"); + if (!gbl_time || !gbl_self) { + parseerror(parser, "cannot use [frame,think] notation without the required globals"); + parseerror(parser, "please declare the following globals: `time`, `self`"); + return false; + } + + if (!parser_next(parser)) + return false; + + framenum = parse_expression_leave(parser, true, false, false); + if (!framenum) { + parseerror(parser, "expected a framenumber constant in[frame,think] notation"); + return false; + } + if (!ast_istype(framenum, ast_value) || !( (ast_value*)framenum )->hasvalue) { + ast_unref(framenum); + parseerror(parser, "framenumber in [frame,think] notation must be a constant"); + return false; + } + + if (parser->tok != ',') { + ast_unref(framenum); + parseerror(parser, "expected comma after frame number in [frame,think] notation"); + parseerror(parser, "Got a %i\n", parser->tok); + return false; + } + + if (!parser_next(parser)) { + ast_unref(framenum); + return false; + } + + if (parser->tok == TOKEN_IDENT && !parser_find_var(parser, parser_tokval(parser))) + { + /* qc allows the use of not-yet-declared functions here + * - this automatically creates a prototype */ + ast_value *thinkfunc; + ast_expression *functype = fld_think->next; + + thinkfunc = ast_value_new(parser_ctx(parser), parser_tokval(parser), functype->vtype); + if (!thinkfunc) { /* || !ast_type_adopt(thinkfunc, functype)*/ + ast_unref(framenum); + parseerror(parser, "failed to create implicit prototype for `%s`", parser_tokval(parser)); + return false; + } + ast_type_adopt(thinkfunc, functype); + + if (!parser_next(parser)) { + ast_unref(framenum); + ast_delete(thinkfunc); + return false; + } + + parser_addglobal(parser, thinkfunc->name, (ast_expression*)thinkfunc); + + nextthink = (ast_expression*)thinkfunc; + + } else { + nextthink = parse_expression_leave(parser, true, false, false); + if (!nextthink) { + ast_unref(framenum); + parseerror(parser, "expected a think-function in [frame,think] notation"); + return false; + } + } + + if (!ast_istype(nextthink, ast_value)) { + parseerror(parser, "think-function in [frame,think] notation must be a constant"); + retval = false; + } + + if (retval && parser->tok != ']') { + parseerror(parser, "expected closing `]` for [frame,think] notation"); + retval = false; + } + + if (retval && !parser_next(parser)) { + retval = false; + } + + if (retval && parser->tok != '{') { + parseerror(parser, "a function body has to be declared after a [frame,think] declaration"); + retval = false; + } + + if (!retval) { + ast_unref(nextthink); + ast_unref(framenum); + return false; + } + + has_frame_think = true; + } + + block = ast_block_new(parser_ctx(parser)); + if (!block) { + parseerror(parser, "failed to allocate block"); + if (has_frame_think) { + ast_unref(nextthink); + ast_unref(framenum); + } + return false; + } + + if (has_frame_think) { + lex_ctx ctx; + ast_expression *self_frame; + ast_expression *self_nextthink; + ast_expression *self_think; + ast_expression *time_plus_1; + ast_store *store_frame; + ast_store *store_nextthink; + ast_store *store_think; + + ctx = parser_ctx(parser); + self_frame = (ast_expression*)ast_entfield_new(ctx, gbl_self, fld_frame); + self_nextthink = (ast_expression*)ast_entfield_new(ctx, gbl_self, fld_nextthink); + self_think = (ast_expression*)ast_entfield_new(ctx, gbl_self, fld_think); + + time_plus_1 = (ast_expression*)ast_binary_new(ctx, INSTR_ADD_F, + gbl_time, (ast_expression*)parser_const_float(parser, 0.1)); + + if (!self_frame || !self_nextthink || !self_think || !time_plus_1) { + if (self_frame) ast_delete(self_frame); + if (self_nextthink) ast_delete(self_nextthink); + if (self_think) ast_delete(self_think); + if (time_plus_1) ast_delete(time_plus_1); + retval = false; + } + + if (retval) + { + store_frame = ast_store_new(ctx, INSTR_STOREP_F, self_frame, framenum); + store_nextthink = ast_store_new(ctx, INSTR_STOREP_F, self_nextthink, time_plus_1); + store_think = ast_store_new(ctx, INSTR_STOREP_FNC, self_think, nextthink); + + if (!store_frame) { + ast_delete(self_frame); + retval = false; + } + if (!store_nextthink) { + ast_delete(self_nextthink); + retval = false; + } + if (!store_think) { + ast_delete(self_think); + retval = false; + } + if (!retval) { + if (store_frame) ast_delete(store_frame); + if (store_nextthink) ast_delete(store_nextthink); + if (store_think) ast_delete(store_think); + retval = false; + } + if (!ast_block_add_expr(block, (ast_expression*)store_frame) || + !ast_block_add_expr(block, (ast_expression*)store_nextthink) || + !ast_block_add_expr(block, (ast_expression*)store_think)) + { + retval = false; + } + } + + if (!retval) { + parseerror(parser, "failed to generate code for [frame,think]"); + ast_unref(nextthink); + ast_unref(framenum); + ast_delete(block); + return false; + } + } + + if (var->hasvalue) { + parseerror(parser, "function `%s` declared with multiple bodies", var->name); + ast_block_delete(block); + goto enderr; + } + + func = ast_function_new(ast_ctx(var), var->name, var); + if (!func) { + parseerror(parser, "failed to allocate function for `%s`", var->name); + ast_block_delete(block); + goto enderr; + } + vec_push(parser->functions, func); + + parser_enterblock(parser); + + for (parami = 0; parami < vec_size(var->expression.params); ++parami) { + size_t e; + ast_value *param = var->expression.params[parami]; + ast_member *me[3]; + + if (param->expression.vtype != TYPE_VECTOR && + (param->expression.vtype != TYPE_FIELD || + param->expression.next->vtype != TYPE_VECTOR)) + { + continue; + } + + if (!create_vector_members(param, me)) { + ast_block_delete(block); + goto enderrfn; + } + + for (e = 0; e < 3; ++e) { + parser_addlocal(parser, me[e]->name, (ast_expression*)me[e]); + ast_block_collect(block, (ast_expression*)me[e]); + } + } + + if (var->argcounter) { + ast_value *argc = ast_value_new(ast_ctx(var), var->argcounter, TYPE_FLOAT); + parser_addlocal(parser, argc->name, (ast_expression*)argc); + func->argc = argc; + } + + if (OPTS_FLAG(VARIADIC_ARGS) && var->expression.flags & AST_FLAG_VARIADIC) { + char name[1024]; + ast_value *varargs = ast_value_new(ast_ctx(var), "reserved:va_args", TYPE_ARRAY); + varargs->expression.flags |= AST_FLAG_IS_VARARG; + varargs->expression.next = (ast_expression*)ast_value_new(ast_ctx(var), NULL, TYPE_VECTOR); + varargs->expression.count = 0; + util_snprintf(name, sizeof(name), "%s##va##SET", var->name); + if (!parser_create_array_setter_proto(parser, varargs, name)) { + ast_delete(varargs); + ast_block_delete(block); + goto enderrfn; + } + util_snprintf(name, sizeof(name), "%s##va##GET", var->name); + if (!parser_create_array_getter_proto(parser, varargs, varargs->expression.next, name)) { + ast_delete(varargs); + ast_block_delete(block); + goto enderrfn; + } + func->varargs = varargs; + + func->fixedparams = parser_const_float(parser, vec_size(var->expression.params)); + } + + parser->function = func; + if (!parse_block_into(parser, block)) { + ast_block_delete(block); + goto enderrfn; + } + + vec_push(func->blocks, block); + + + parser->function = old; + if (!parser_leaveblock(parser)) + retval = false; + if (vec_size(parser->variables) != PARSER_HT_LOCALS) { + parseerror(parser, "internal error: local scopes left"); + retval = false; + } + + if (parser->tok == ';') + return parser_next(parser); + else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) + parseerror(parser, "missing semicolon after function body (mandatory with -std=qcc)"); + return retval; + +enderrfn: + (void)!parser_leaveblock(parser); + vec_pop(parser->functions); + ast_function_delete(func); + var->constval.vfunc = NULL; + +enderr: + parser->function = old; + return false; +} + +static ast_expression *array_accessor_split( + parser_t *parser, + ast_value *array, + ast_value *index, + size_t middle, + ast_expression *left, + ast_expression *right + ) +{ + ast_ifthen *ifthen; + ast_binary *cmp; + + lex_ctx ctx = ast_ctx(array); + + if (!left || !right) { + if (left) ast_delete(left); + if (right) ast_delete(right); + return NULL; + } + + cmp = ast_binary_new(ctx, INSTR_LT, + (ast_expression*)index, + (ast_expression*)parser_const_float(parser, middle)); + if (!cmp) { + ast_delete(left); + ast_delete(right); + parseerror(parser, "internal error: failed to create comparison for array setter"); + return NULL; + } + + ifthen = ast_ifthen_new(ctx, (ast_expression*)cmp, left, right); + if (!ifthen) { + ast_delete(cmp); /* will delete left and right */ + parseerror(parser, "internal error: failed to create conditional jump for array setter"); + return NULL; + } + + return (ast_expression*)ifthen; +} + +static ast_expression *array_setter_node(parser_t *parser, ast_value *array, ast_value *index, ast_value *value, size_t from, size_t afterend) +{ + lex_ctx ctx = ast_ctx(array); + + if (from+1 == afterend) { + /* set this value */ + ast_block *block; + ast_return *ret; + ast_array_index *subscript; + ast_store *st; + int assignop = type_store_instr[value->expression.vtype]; + + if (value->expression.vtype == TYPE_FIELD && value->expression.next->vtype == TYPE_VECTOR) + assignop = INSTR_STORE_V; + + subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)parser_const_float(parser, from)); + if (!subscript) + return NULL; + + st = ast_store_new(ctx, assignop, (ast_expression*)subscript, (ast_expression*)value); + if (!st) { + ast_delete(subscript); + return NULL; + } + + block = ast_block_new(ctx); + if (!block) { + ast_delete(st); + return NULL; + } + + if (!ast_block_add_expr(block, (ast_expression*)st)) { + ast_delete(block); + return NULL; + } + + ret = ast_return_new(ctx, NULL); + if (!ret) { + ast_delete(block); + return NULL; + } + + if (!ast_block_add_expr(block, (ast_expression*)ret)) { + ast_delete(block); + return NULL; + } + + return (ast_expression*)block; + } else { + ast_expression *left, *right; + size_t diff = afterend - from; + size_t middle = from + diff/2; + left = array_setter_node(parser, array, index, value, from, middle); + right = array_setter_node(parser, array, index, value, middle, afterend); + return array_accessor_split(parser, array, index, middle, left, right); + } +} + +static ast_expression *array_field_setter_node( + parser_t *parser, + ast_value *array, + ast_value *entity, + ast_value *index, + ast_value *value, + size_t from, + size_t afterend) +{ + lex_ctx ctx = ast_ctx(array); + + if (from+1 == afterend) { + /* set this value */ + ast_block *block; + ast_return *ret; + ast_entfield *entfield; + ast_array_index *subscript; + ast_store *st; + int assignop = type_storep_instr[value->expression.vtype]; + + if (value->expression.vtype == TYPE_FIELD && value->expression.next->vtype == TYPE_VECTOR) + assignop = INSTR_STOREP_V; + + subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)parser_const_float(parser, from)); + if (!subscript) + return NULL; + + subscript->expression.next = ast_type_copy(ast_ctx(subscript), (ast_expression*)subscript); + subscript->expression.vtype = TYPE_FIELD; + + entfield = ast_entfield_new_force(ctx, + (ast_expression*)entity, + (ast_expression*)subscript, + (ast_expression*)subscript); + if (!entfield) { + ast_delete(subscript); + return NULL; + } + + st = ast_store_new(ctx, assignop, (ast_expression*)entfield, (ast_expression*)value); + if (!st) { + ast_delete(entfield); + return NULL; + } + + block = ast_block_new(ctx); + if (!block) { + ast_delete(st); + return NULL; + } + + if (!ast_block_add_expr(block, (ast_expression*)st)) { + ast_delete(block); + return NULL; + } + + ret = ast_return_new(ctx, NULL); + if (!ret) { + ast_delete(block); + return NULL; + } + + if (!ast_block_add_expr(block, (ast_expression*)ret)) { + ast_delete(block); + return NULL; + } + + return (ast_expression*)block; + } else { + ast_expression *left, *right; + size_t diff = afterend - from; + size_t middle = from + diff/2; + left = array_field_setter_node(parser, array, entity, index, value, from, middle); + right = array_field_setter_node(parser, array, entity, index, value, middle, afterend); + return array_accessor_split(parser, array, index, middle, left, right); + } +} + +static ast_expression *array_getter_node(parser_t *parser, ast_value *array, ast_value *index, size_t from, size_t afterend) +{ + lex_ctx ctx = ast_ctx(array); + + if (from+1 == afterend) { + ast_return *ret; + ast_array_index *subscript; + + subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)parser_const_float(parser, from)); + if (!subscript) + return NULL; + + ret = ast_return_new(ctx, (ast_expression*)subscript); + if (!ret) { + ast_delete(subscript); + return NULL; + } + + return (ast_expression*)ret; + } else { + ast_expression *left, *right; + size_t diff = afterend - from; + size_t middle = from + diff/2; + left = array_getter_node(parser, array, index, from, middle); + right = array_getter_node(parser, array, index, middle, afterend); + return array_accessor_split(parser, array, index, middle, left, right); + } +} + +static bool parser_create_array_accessor(parser_t *parser, ast_value *array, const char *funcname, ast_value **out) +{ + ast_function *func = NULL; + ast_value *fval = NULL; + ast_block *body = NULL; + + fval = ast_value_new(ast_ctx(array), funcname, TYPE_FUNCTION); + if (!fval) { + parseerror(parser, "failed to create accessor function value"); + return false; + } + + func = ast_function_new(ast_ctx(array), funcname, fval); + if (!func) { + ast_delete(fval); + parseerror(parser, "failed to create accessor function node"); + return false; + } + + body = ast_block_new(ast_ctx(array)); + if (!body) { + parseerror(parser, "failed to create block for array accessor"); + ast_delete(fval); + ast_delete(func); + return false; + } + + vec_push(func->blocks, body); + *out = fval; + + vec_push(parser->accessors, fval); + + return true; +} + +static ast_value* parser_create_array_setter_proto(parser_t *parser, ast_value *array, const char *funcname) +{ + ast_value *index = NULL; + ast_value *value = NULL; + ast_function *func; + ast_value *fval; + + if (!ast_istype(array->expression.next, ast_value)) { + parseerror(parser, "internal error: array accessor needs to build an ast_value with a copy of the element type"); + return NULL; + } + + if (!parser_create_array_accessor(parser, array, funcname, &fval)) + return NULL; + func = fval->constval.vfunc; + fval->expression.next = (ast_expression*)ast_value_new(ast_ctx(array), "", TYPE_VOID); + + index = ast_value_new(ast_ctx(array), "index", TYPE_FLOAT); + value = ast_value_copy((ast_value*)array->expression.next); + + if (!index || !value) { + parseerror(parser, "failed to create locals for array accessor"); + goto cleanup; + } + (void)!ast_value_set_name(value, "value"); /* not important */ + vec_push(fval->expression.params, index); + vec_push(fval->expression.params, value); + + array->setter = fval; + return fval; +cleanup: + if (index) ast_delete(index); + if (value) ast_delete(value); + ast_delete(func); + ast_delete(fval); + return NULL; +} + +static bool parser_create_array_setter_impl(parser_t *parser, ast_value *array) +{ + ast_expression *root = NULL; + root = array_setter_node(parser, array, + array->setter->expression.params[0], + array->setter->expression.params[1], + 0, array->expression.count); + if (!root) { + parseerror(parser, "failed to build accessor search tree"); + return false; + } + if (!ast_block_add_expr(array->setter->constval.vfunc->blocks[0], root)) { + ast_delete(root); + return false; + } + return true; +} + +static bool parser_create_array_setter(parser_t *parser, ast_value *array, const char *funcname) +{ + if (!parser_create_array_setter_proto(parser, array, funcname)) + return false; + return parser_create_array_setter_impl(parser, array); +} + +static bool parser_create_array_field_setter(parser_t *parser, ast_value *array, const char *funcname) +{ + ast_expression *root = NULL; + ast_value *entity = NULL; + ast_value *index = NULL; + ast_value *value = NULL; + ast_function *func; + ast_value *fval; + + if (!ast_istype(array->expression.next, ast_value)) { + parseerror(parser, "internal error: array accessor needs to build an ast_value with a copy of the element type"); + return false; + } + + if (!parser_create_array_accessor(parser, array, funcname, &fval)) + return false; + func = fval->constval.vfunc; + fval->expression.next = (ast_expression*)ast_value_new(ast_ctx(array), "", TYPE_VOID); + + entity = ast_value_new(ast_ctx(array), "entity", TYPE_ENTITY); + index = ast_value_new(ast_ctx(array), "index", TYPE_FLOAT); + value = ast_value_copy((ast_value*)array->expression.next); + if (!entity || !index || !value) { + parseerror(parser, "failed to create locals for array accessor"); + goto cleanup; + } + (void)!ast_value_set_name(value, "value"); /* not important */ + vec_push(fval->expression.params, entity); + vec_push(fval->expression.params, index); + vec_push(fval->expression.params, value); + + root = array_field_setter_node(parser, array, entity, index, value, 0, array->expression.count); + if (!root) { + parseerror(parser, "failed to build accessor search tree"); + goto cleanup; + } + + array->setter = fval; + return ast_block_add_expr(func->blocks[0], root); +cleanup: + if (entity) ast_delete(entity); + if (index) ast_delete(index); + if (value) ast_delete(value); + if (root) ast_delete(root); + ast_delete(func); + ast_delete(fval); + return false; +} + +static ast_value* parser_create_array_getter_proto(parser_t *parser, ast_value *array, const ast_expression *elemtype, const char *funcname) +{ + ast_value *index = NULL; + ast_value *fval; + ast_function *func; + + /* NOTE: checking array->expression.next rather than elemtype since + * for fields elemtype is a temporary fieldtype. + */ + if (!ast_istype(array->expression.next, ast_value)) { + parseerror(parser, "internal error: array accessor needs to build an ast_value with a copy of the element type"); + return NULL; + } + + if (!parser_create_array_accessor(parser, array, funcname, &fval)) + return NULL; + func = fval->constval.vfunc; + fval->expression.next = ast_type_copy(ast_ctx(array), elemtype); + + index = ast_value_new(ast_ctx(array), "index", TYPE_FLOAT); + + if (!index) { + parseerror(parser, "failed to create locals for array accessor"); + goto cleanup; + } + vec_push(fval->expression.params, index); + + array->getter = fval; + return fval; +cleanup: + if (index) ast_delete(index); + ast_delete(func); + ast_delete(fval); + return NULL; +} + +static bool parser_create_array_getter_impl(parser_t *parser, ast_value *array) +{ + ast_expression *root = NULL; + + root = array_getter_node(parser, array, array->getter->expression.params[0], 0, array->expression.count); + if (!root) { + parseerror(parser, "failed to build accessor search tree"); + return false; + } + if (!ast_block_add_expr(array->getter->constval.vfunc->blocks[0], root)) { + ast_delete(root); + return false; + } + return true; +} + +static bool parser_create_array_getter(parser_t *parser, ast_value *array, const ast_expression *elemtype, const char *funcname) +{ + if (!parser_create_array_getter_proto(parser, array, elemtype, funcname)) + return false; + return parser_create_array_getter_impl(parser, array); +} + +static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef); +static ast_value *parse_parameter_list(parser_t *parser, ast_value *var) +{ + lex_ctx ctx; + size_t i; + ast_value **params; + ast_value *param; + ast_value *fval; + bool first = true; + bool variadic = false; + ast_value *varparam = NULL; + char *argcounter = NULL; + + ctx = parser_ctx(parser); + + /* for the sake of less code we parse-in in this function */ + if (!parser_next(parser)) { + ast_delete(var); + parseerror(parser, "expected parameter list"); + return NULL; + } + + params = NULL; + + /* parse variables until we hit a closing paren */ + while (parser->tok != ')') { + if (!first) { + /* there must be commas between them */ + if (parser->tok != ',') { + parseerror(parser, "expected comma or end of parameter list"); + goto on_error; + } + if (!parser_next(parser)) { + parseerror(parser, "expected parameter"); + goto on_error; + } + } + first = false; + + if (parser->tok == TOKEN_DOTS) { + /* '...' indicates a varargs function */ + variadic = true; + if (!parser_next(parser) || (parser->tok != ')' && parser->tok != TOKEN_IDENT)) { + parseerror(parser, "`...` must be the last parameter of a variadic function declaration"); + goto on_error; + } + if (parser->tok == TOKEN_IDENT) { + argcounter = util_strdup(parser_tokval(parser)); + if (!parser_next(parser) || parser->tok != ')') { + parseerror(parser, "`...` must be the last parameter of a variadic function declaration"); + goto on_error; + } + } + } + else + { + /* for anything else just parse a typename */ + param = parse_typename(parser, NULL, NULL); + if (!param) + goto on_error; + vec_push(params, param); + if (param->expression.vtype >= TYPE_VARIANT) { + char tname[1024]; /* typename is reserved in C++ */ + ast_type_to_string((ast_expression*)param, tname, sizeof(tname)); + parseerror(parser, "type not supported as part of a parameter list: %s", tname); + goto on_error; + } + /* type-restricted varargs */ + if (parser->tok == TOKEN_DOTS) { + variadic = true; + varparam = vec_last(params); + vec_pop(params); + if (!parser_next(parser) || (parser->tok != ')' && parser->tok != TOKEN_IDENT)) { + parseerror(parser, "`...` must be the last parameter of a variadic function declaration"); + goto on_error; + } + if (parser->tok == TOKEN_IDENT) { + argcounter = util_strdup(parser_tokval(parser)); + if (!parser_next(parser) || parser->tok != ')') { + parseerror(parser, "`...` must be the last parameter of a variadic function declaration"); + goto on_error; + } + } + } + } + } + + if (vec_size(params) == 1 && params[0]->expression.vtype == TYPE_VOID) + vec_free(params); + + /* sanity check */ + if (vec_size(params) > 8 && OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) + (void)!parsewarning(parser, WARN_EXTENSIONS, "more than 8 parameters are not supported by this standard"); + + /* parse-out */ + if (!parser_next(parser)) { + parseerror(parser, "parse error after typename"); + goto on_error; + } + + /* now turn 'var' into a function type */ + fval = ast_value_new(ctx, "", TYPE_FUNCTION); + fval->expression.next = (ast_expression*)var; + if (variadic) + fval->expression.flags |= AST_FLAG_VARIADIC; + var = fval; + + var->expression.params = params; + var->expression.varparam = (ast_expression*)varparam; + var->argcounter = argcounter; + params = NULL; + + return var; + +on_error: + if (argcounter) + mem_d(argcounter); + if (varparam) + ast_delete(varparam); + ast_delete(var); + for (i = 0; i < vec_size(params); ++i) + ast_delete(params[i]); + vec_free(params); + return NULL; +} + +static ast_value *parse_arraysize(parser_t *parser, ast_value *var) +{ + ast_expression *cexp; + ast_value *cval, *tmp; + lex_ctx ctx; + + ctx = parser_ctx(parser); + + if (!parser_next(parser)) { + ast_delete(var); + parseerror(parser, "expected array-size"); + return NULL; + } + + cexp = parse_expression_leave(parser, true, false, false); + + if (!cexp || !ast_istype(cexp, ast_value)) { + if (cexp) + ast_unref(cexp); + ast_delete(var); + parseerror(parser, "expected array-size as constant positive integer"); + return NULL; + } + cval = (ast_value*)cexp; + + tmp = ast_value_new(ctx, "", TYPE_ARRAY); + tmp->expression.next = (ast_expression*)var; + var = tmp; + + if (cval->expression.vtype == TYPE_INTEGER) + tmp->expression.count = cval->constval.vint; + else if (cval->expression.vtype == TYPE_FLOAT) + tmp->expression.count = cval->constval.vfloat; + else { + ast_unref(cexp); + ast_delete(var); + parseerror(parser, "array-size must be a positive integer constant"); + return NULL; + } + ast_unref(cexp); + + if (parser->tok != ']') { + ast_delete(var); + parseerror(parser, "expected ']' after array-size"); + return NULL; + } + if (!parser_next(parser)) { + ast_delete(var); + parseerror(parser, "error after parsing array size"); + return NULL; + } + return var; +} + +/* Parse a complete typename. + * for single-variables (ie. function parameters or typedefs) storebase should be NULL + * but when parsing variables separated by comma + * 'storebase' should point to where the base-type should be kept. + * The base type makes up every bit of type information which comes *before* the + * variable name. + * + * The following will be parsed in its entirety: + * void() foo() + * The 'basetype' in this case is 'void()' + * and if there's a comma after it, say: + * void() foo(), bar + * then the type-information 'void()' can be stored in 'storebase' + */ +static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef) +{ + ast_value *var, *tmp; + lex_ctx ctx; + + const char *name = NULL; + bool isfield = false; + bool wasarray = false; + size_t morefields = 0; + + ctx = parser_ctx(parser); + + /* types may start with a dot */ + if (parser->tok == '.') { + isfield = true; + /* if we parsed a dot we need a typename now */ + if (!parser_next(parser)) { + parseerror(parser, "expected typename for field definition"); + return NULL; + } + + /* Further dots are handled seperately because they won't be part of the + * basetype + */ + while (parser->tok == '.') { + ++morefields; + if (!parser_next(parser)) { + parseerror(parser, "expected typename for field definition"); + return NULL; + } + } + } + if (parser->tok == TOKEN_IDENT) + cached_typedef = parser_find_typedef(parser, parser_tokval(parser), 0); + if (!cached_typedef && parser->tok != TOKEN_TYPENAME) { + parseerror(parser, "expected typename"); + return NULL; + } + + /* generate the basic type value */ + if (cached_typedef) { + var = ast_value_copy(cached_typedef); + ast_value_set_name(var, ""); + } else + var = ast_value_new(ctx, "", parser_token(parser)->constval.t); + + for (; morefields; --morefields) { + tmp = ast_value_new(ctx, "<.type>", TYPE_FIELD); + tmp->expression.next = (ast_expression*)var; + var = tmp; + } + + /* do not yet turn into a field - remember: + * .void() foo; is a field too + * .void()() foo; is a function + */ + + /* parse on */ + if (!parser_next(parser)) { + ast_delete(var); + parseerror(parser, "parse error after typename"); + return NULL; + } + + /* an opening paren now starts the parameter-list of a function + * this is where original-QC has parameter lists. + * We allow a single parameter list here. + * Much like fteqcc we don't allow `float()() x` + */ + if (parser->tok == '(') { + var = parse_parameter_list(parser, var); + if (!var) + return NULL; + } + + /* store the base if requested */ + if (storebase) { + *storebase = ast_value_copy(var); + if (isfield) { + tmp = ast_value_new(ctx, "", TYPE_FIELD); + tmp->expression.next = (ast_expression*)*storebase; + *storebase = tmp; + } + } + + /* there may be a name now */ + if (parser->tok == TOKEN_IDENT) { + name = util_strdup(parser_tokval(parser)); + /* parse on */ + if (!parser_next(parser)) { + ast_delete(var); + mem_d(name); + parseerror(parser, "error after variable or field declaration"); + return NULL; + } + } + + /* now this may be an array */ + if (parser->tok == '[') { + wasarray = true; + var = parse_arraysize(parser, var); + if (!var) { + if (name) mem_d(name); + return NULL; + } + } + + /* This is the point where we can turn it into a field */ + if (isfield) { + /* turn it into a field if desired */ + tmp = ast_value_new(ctx, "", TYPE_FIELD); + tmp->expression.next = (ast_expression*)var; + var = tmp; + } + + /* now there may be function parens again */ + if (parser->tok == '(' && OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) + parseerror(parser, "C-style function syntax is not allowed in -std=qcc"); + if (parser->tok == '(' && wasarray) + parseerror(parser, "arrays as part of a return type is not supported"); + while (parser->tok == '(') { + var = parse_parameter_list(parser, var); + if (!var) { + if (name) mem_d(name); + return NULL; + } + } + + /* finally name it */ + if (name) { + if (!ast_value_set_name(var, name)) { + ast_delete(var); + mem_d(name); + parseerror(parser, "internal error: failed to set name"); + return NULL; + } + /* free the name, ast_value_set_name duplicates */ + mem_d(name); + } + + return var; +} + +static bool parse_typedef(parser_t *parser) +{ + ast_value *typevar, *oldtype; + ast_expression *old; + + typevar = parse_typename(parser, NULL, NULL); + + if (!typevar) + return false; + + if ( (old = parser_find_var(parser, typevar->name)) ) { + parseerror(parser, "cannot define a type with the same name as a variable: %s\n" + " -> `%s` has been declared here: %s:%i", + typevar->name, ast_ctx(old).file, ast_ctx(old).line); + ast_delete(typevar); + return false; + } + + if ( (oldtype = parser_find_typedef(parser, typevar->name, vec_last(parser->_blocktypedefs))) ) { + parseerror(parser, "type `%s` has already been declared here: %s:%i", + typevar->name, ast_ctx(oldtype).file, ast_ctx(oldtype).line); + ast_delete(typevar); + return false; + } + + vec_push(parser->_typedefs, typevar); + util_htset(vec_last(parser->typedefs), typevar->name, typevar); + + if (parser->tok != ';') { + parseerror(parser, "expected semicolon after typedef"); + return false; + } + if (!parser_next(parser)) { + parseerror(parser, "parse error after typedef"); + return false; + } + + return true; +} + +static const char *cvq_to_str(int cvq) { + switch (cvq) { + case CV_NONE: return "none"; + case CV_VAR: return "`var`"; + case CV_CONST: return "`const`"; + default: return ""; + } +} + +static bool parser_check_qualifiers(parser_t *parser, const ast_value *var, const ast_value *proto) +{ + bool av, ao; + if (proto->cvq != var->cvq) { + if (!(proto->cvq == CV_CONST && var->cvq == CV_NONE && + !OPTS_FLAG(INITIALIZED_NONCONSTANTS) && + parser->tok == '=')) + { + return !parsewarning(parser, WARN_DIFFERENT_QUALIFIERS, + "`%s` declared with different qualifiers: %s\n" + " -> previous declaration here: %s:%i uses %s", + var->name, cvq_to_str(var->cvq), + ast_ctx(proto).file, ast_ctx(proto).line, + cvq_to_str(proto->cvq)); + } + } + av = (var ->expression.flags & AST_FLAG_NORETURN); + ao = (proto->expression.flags & AST_FLAG_NORETURN); + if (!av != !ao) { + return !parsewarning(parser, WARN_DIFFERENT_ATTRIBUTES, + "`%s` declared with different attributes%s\n" + " -> previous declaration here: %s:%i", + var->name, (av ? ": noreturn" : ""), + ast_ctx(proto).file, ast_ctx(proto).line, + (ao ? ": noreturn" : "")); + } + return true; +} + +static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool is_static, uint32_t qflags, char *vstring) +{ + ast_value *var; + ast_value *proto; + ast_expression *old; + bool was_end; + size_t i; + + ast_value *basetype = NULL; + bool retval = true; + bool isparam = false; + bool isvector = false; + bool cleanvar = true; + bool wasarray = false; + + ast_member *me[3] = { NULL, NULL, NULL }; + + if (!localblock && is_static) + parseerror(parser, "`static` qualifier is not supported in global scope"); + + /* get the first complete variable */ + var = parse_typename(parser, &basetype, cached_typedef); + if (!var) { + if (basetype) + ast_delete(basetype); + return false; + } + + while (true) { + proto = NULL; + wasarray = false; + + /* Part 0: finish the type */ + if (parser->tok == '(') { + if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) + parseerror(parser, "C-style function syntax is not allowed in -std=qcc"); + var = parse_parameter_list(parser, var); + if (!var) { + retval = false; + goto cleanup; + } + } + /* we only allow 1-dimensional arrays */ + if (parser->tok == '[') { + wasarray = true; + var = parse_arraysize(parser, var); + if (!var) { + retval = false; + goto cleanup; + } + } + if (parser->tok == '(' && wasarray) { + parseerror(parser, "arrays as part of a return type is not supported"); + /* we'll still parse the type completely for now */ + } + /* for functions returning functions */ + while (parser->tok == '(') { + if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) + parseerror(parser, "C-style function syntax is not allowed in -std=qcc"); + var = parse_parameter_list(parser, var); + if (!var) { + retval = false; + goto cleanup; + } + } + + var->cvq = qualifier; + var->expression.flags |= qflags; + + /* + * store the vstring back to var for alias and + * deprecation messages. + */ + if (var->expression.flags & AST_FLAG_DEPRECATED || + var->expression.flags & AST_FLAG_ALIAS) + var->desc = vstring; + + /* Part 1: + * check for validity: (end_sys_..., multiple-definitions, prototypes, ...) + * Also: if there was a prototype, `var` will be deleted and set to `proto` which + * is then filled with the previous definition and the parameter-names replaced. + */ + if (!strcmp(var->name, "nil")) { + if (OPTS_FLAG(UNTYPED_NIL)) { + if (!localblock || !OPTS_FLAG(PERMISSIVE)) + parseerror(parser, "name `nil` not allowed (try -fpermissive)"); + } else + (void)!parsewarning(parser, WARN_RESERVED_NAMES, "variable name `nil` is reserved"); + } + if (!localblock) { + /* Deal with end_sys_ vars */ + was_end = false; + if (!strcmp(var->name, "end_sys_globals")) { + var->uses++; + parser->crc_globals = vec_size(parser->globals); + was_end = true; + } + else if (!strcmp(var->name, "end_sys_fields")) { + var->uses++; + parser->crc_fields = vec_size(parser->fields); + was_end = true; + } + if (was_end && var->expression.vtype == TYPE_FIELD) { + if (parsewarning(parser, WARN_END_SYS_FIELDS, + "global '%s' hint should not be a field", + parser_tokval(parser))) + { + retval = false; + goto cleanup; + } + } + + if (!nofields && var->expression.vtype == TYPE_FIELD) + { + /* deal with field declarations */ + old = parser_find_field(parser, var->name); + if (old) { + if (parsewarning(parser, WARN_FIELD_REDECLARED, "field `%s` already declared here: %s:%i", + var->name, ast_ctx(old).file, (int)ast_ctx(old).line)) + { + retval = false; + goto cleanup; + } + ast_delete(var); + var = NULL; + goto skipvar; + /* + parseerror(parser, "field `%s` already declared here: %s:%i", + var->name, ast_ctx(old).file, ast_ctx(old).line); + retval = false; + goto cleanup; + */ + } + if ((OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC || OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_FTEQCC) && + (old = parser_find_global(parser, var->name))) + { + parseerror(parser, "cannot declare a field and a global of the same name with -std=qcc"); + parseerror(parser, "field `%s` already declared here: %s:%i", + var->name, ast_ctx(old).file, ast_ctx(old).line); + retval = false; + goto cleanup; + } + } + else + { + /* deal with other globals */ + old = parser_find_global(parser, var->name); + if (old && var->expression.vtype == TYPE_FUNCTION && old->vtype == TYPE_FUNCTION) + { + /* This is a function which had a prototype */ + if (!ast_istype(old, ast_value)) { + parseerror(parser, "internal error: prototype is not an ast_value"); + retval = false; + goto cleanup; + } + proto = (ast_value*)old; + proto->desc = var->desc; + if (!ast_compare_type((ast_expression*)proto, (ast_expression*)var)) { + parseerror(parser, "conflicting types for `%s`, previous declaration was here: %s:%i", + proto->name, + ast_ctx(proto).file, ast_ctx(proto).line); + retval = false; + goto cleanup; + } + /* we need the new parameter-names */ + for (i = 0; i < vec_size(proto->expression.params); ++i) + ast_value_set_name(proto->expression.params[i], var->expression.params[i]->name); + if (!parser_check_qualifiers(parser, var, proto)) { + retval = false; + if (proto->desc) + mem_d(proto->desc); + proto = NULL; + goto cleanup; + } + proto->expression.flags |= var->expression.flags; + ast_delete(var); + var = proto; + } + else + { + /* other globals */ + if (old) { + if (parsewarning(parser, WARN_DOUBLE_DECLARATION, + "global `%s` already declared here: %s:%i", + var->name, ast_ctx(old).file, ast_ctx(old).line)) + { + retval = false; + goto cleanup; + } + proto = (ast_value*)old; + if (!ast_istype(old, ast_value)) { + parseerror(parser, "internal error: not an ast_value"); + retval = false; + proto = NULL; + goto cleanup; + } + if (!parser_check_qualifiers(parser, var, proto)) { + retval = false; + proto = NULL; + goto cleanup; + } + proto->expression.flags |= var->expression.flags; + ast_delete(var); + var = proto; + } + if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC && + (old = parser_find_field(parser, var->name))) + { + parseerror(parser, "cannot declare a field and a global of the same name with -std=qcc"); + parseerror(parser, "global `%s` already declared here: %s:%i", + var->name, ast_ctx(old).file, ast_ctx(old).line); + retval = false; + goto cleanup; + } + } + } + } + else /* it's not a global */ + { + old = parser_find_local(parser, var->name, vec_size(parser->variables)-1, &isparam); + if (old && !isparam) { + parseerror(parser, "local `%s` already declared here: %s:%i", + var->name, ast_ctx(old).file, (int)ast_ctx(old).line); + retval = false; + goto cleanup; + } + old = parser_find_local(parser, var->name, 0, &isparam); + if (old && isparam) { + if (parsewarning(parser, WARN_LOCAL_SHADOWS, + "local `%s` is shadowing a parameter", var->name)) + { + parseerror(parser, "local `%s` already declared here: %s:%i", + var->name, ast_ctx(old).file, (int)ast_ctx(old).line); + retval = false; + goto cleanup; + } + if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_GMQCC) { + ast_delete(var); + var = NULL; + goto skipvar; + } + } + } + + /* in a noref section we simply bump the usecount */ + if (noref || parser->noref) + var->uses++; + + /* Part 2: + * Create the global/local, and deal with vector types. + */ + if (!proto) { + if (var->expression.vtype == TYPE_VECTOR) + isvector = true; + else if (var->expression.vtype == TYPE_FIELD && + var->expression.next->vtype == TYPE_VECTOR) + isvector = true; + + if (isvector) { + if (!create_vector_members(var, me)) { + retval = false; + goto cleanup; + } + } + + if (!localblock) { + /* deal with global variables, fields, functions */ + if (!nofields && var->expression.vtype == TYPE_FIELD && parser->tok != '=') { + var->isfield = true; + vec_push(parser->fields, (ast_expression*)var); + util_htset(parser->htfields, var->name, var); + if (isvector) { + for (i = 0; i < 3; ++i) { + vec_push(parser->fields, (ast_expression*)me[i]); + util_htset(parser->htfields, me[i]->name, me[i]); + } + } + } + else { + if (!(var->expression.flags & AST_FLAG_ALIAS)) { + parser_addglobal(parser, var->name, (ast_expression*)var); + if (isvector) { + for (i = 0; i < 3; ++i) { + parser_addglobal(parser, me[i]->name, (ast_expression*)me[i]); + } + } + } else { + ast_expression *find = parser_find_global(parser, var->desc); + + if (!find) { + compile_error(parser_ctx(parser), "undeclared variable `%s` for alias `%s`", var->desc, var->name); + return false; + } + + if (var->expression.vtype != find->vtype) { + char ty1[1024]; + char ty2[1024]; + + ast_type_to_string(find, ty1, sizeof(ty1)); + ast_type_to_string((ast_expression*)var, ty2, sizeof(ty2)); + + compile_error(parser_ctx(parser), "incompatible types `%s` and `%s` for alias `%s`", + ty1, ty2, var->name + ); + return false; + } + + /* + * add alias to aliases table and to corrector + * so corrections can apply for aliases as well. + */ + util_htset(parser->aliases, var->name, find); + + /* + * add to corrector so corrections can work + * even for aliases too. + */ + correct_add ( + vec_last(parser->correct_variables), + &vec_last(parser->correct_variables_score), + var->name + ); + + /* generate aliases for vector components */ + if (isvector) { + char *buffer[3]; + + util_asprintf(&buffer[0], "%s_x", var->desc); + util_asprintf(&buffer[1], "%s_y", var->desc); + util_asprintf(&buffer[2], "%s_z", var->desc); + + util_htset(parser->aliases, me[0]->name, parser_find_global(parser, buffer[0])); + util_htset(parser->aliases, me[1]->name, parser_find_global(parser, buffer[1])); + util_htset(parser->aliases, me[2]->name, parser_find_global(parser, buffer[2])); + + mem_d(buffer[0]); + mem_d(buffer[1]); + mem_d(buffer[2]); + + /* + * add to corrector so corrections can work + * even for aliases too. + */ + correct_add ( + vec_last(parser->correct_variables), + &vec_last(parser->correct_variables_score), + me[0]->name + ); + correct_add ( + vec_last(parser->correct_variables), + &vec_last(parser->correct_variables_score), + me[1]->name + ); + correct_add ( + vec_last(parser->correct_variables), + &vec_last(parser->correct_variables_score), + me[2]->name + ); + } + } + } + } else { + if (is_static) { + /* a static adds itself to be generated like any other global + * but is added to the local namespace instead + */ + char *defname = NULL; + size_t prefix_len, ln; + + ln = strlen(parser->function->name); + vec_append(defname, ln, parser->function->name); + + vec_append(defname, 2, "::"); + /* remember the length up to here */ + prefix_len = vec_size(defname); + + /* Add it to the local scope */ + util_htset(vec_last(parser->variables), var->name, (void*)var); + + /* corrector */ + correct_add ( + vec_last(parser->correct_variables), + &vec_last(parser->correct_variables_score), + var->name + ); + + /* now rename the global */ + ln = strlen(var->name); + vec_append(defname, ln, var->name); + ast_value_set_name(var, defname); + + /* push it to the to-be-generated globals */ + vec_push(parser->globals, (ast_expression*)var); + + /* same game for the vector members */ + if (isvector) { + for (i = 0; i < 3; ++i) { + util_htset(vec_last(parser->variables), me[i]->name, (void*)(me[i])); + + /* corrector */ + correct_add( + vec_last(parser->correct_variables), + &vec_last(parser->correct_variables_score), + me[i]->name + ); + + vec_shrinkto(defname, prefix_len); + ln = strlen(me[i]->name); + vec_append(defname, ln, me[i]->name); + ast_member_set_name(me[i], defname); + + vec_push(parser->globals, (ast_expression*)me[i]); + } + } + vec_free(defname); + } else { + vec_push(localblock->locals, var); + parser_addlocal(parser, var->name, (ast_expression*)var); + if (isvector) { + for (i = 0; i < 3; ++i) { + parser_addlocal(parser, me[i]->name, (ast_expression*)me[i]); + ast_block_collect(localblock, (ast_expression*)me[i]); + } + } + } + } + } + me[0] = me[1] = me[2] = NULL; + cleanvar = false; + /* Part 2.2 + * deal with arrays + */ + if (var->expression.vtype == TYPE_ARRAY) { + char name[1024]; + util_snprintf(name, sizeof(name), "%s##SET", var->name); + if (!parser_create_array_setter(parser, var, name)) + goto cleanup; + util_snprintf(name, sizeof(name), "%s##GET", var->name); + if (!parser_create_array_getter(parser, var, var->expression.next, name)) + goto cleanup; + } + else if (!localblock && !nofields && + var->expression.vtype == TYPE_FIELD && + var->expression.next->vtype == TYPE_ARRAY) + { + char name[1024]; + ast_expression *telem; + ast_value *tfield; + ast_value *array = (ast_value*)var->expression.next; + + if (!ast_istype(var->expression.next, ast_value)) { + parseerror(parser, "internal error: field element type must be an ast_value"); + goto cleanup; + } + + util_snprintf(name, sizeof(name), "%s##SETF", var->name); + if (!parser_create_array_field_setter(parser, array, name)) + goto cleanup; + + telem = ast_type_copy(ast_ctx(var), array->expression.next); + tfield = ast_value_new(ast_ctx(var), "<.type>", TYPE_FIELD); + tfield->expression.next = telem; + util_snprintf(name, sizeof(name), "%s##GETFP", var->name); + if (!parser_create_array_getter(parser, array, (ast_expression*)tfield, name)) { + ast_delete(tfield); + goto cleanup; + } + ast_delete(tfield); + } + +skipvar: + if (parser->tok == ';') { + ast_delete(basetype); + if (!parser_next(parser)) { + parseerror(parser, "error after variable declaration"); + return false; + } + return true; + } + + if (parser->tok == ',') + goto another; + + /* + if (!var || (!localblock && !nofields && basetype->expression.vtype == TYPE_FIELD)) { + */ + if (!var) { + parseerror(parser, "missing comma or semicolon while parsing variables"); + break; + } + + if (localblock && OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) { + if (parsewarning(parser, WARN_LOCAL_CONSTANTS, + "initializing expression turns variable `%s` into a constant in this standard", + var->name) ) + { + break; + } + } + + if (parser->tok != '{' || var->expression.vtype != TYPE_FUNCTION) { + if (parser->tok != '=') { + parseerror(parser, "missing semicolon or initializer, got: `%s`", parser_tokval(parser)); + break; + } + + if (!parser_next(parser)) { + parseerror(parser, "error parsing initializer"); + break; + } + } + else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) { + parseerror(parser, "expected '=' before function body in this standard"); + } + + if (parser->tok == '#') { + ast_function *func = NULL; + ast_value *number = NULL; + float fractional; + float integral; + int builtin_num; + + if (localblock) { + parseerror(parser, "cannot declare builtins within functions"); + break; + } + if (var->expression.vtype != TYPE_FUNCTION) { + parseerror(parser, "unexpected builtin number, '%s' is not a function", var->name); + break; + } + if (!parser_next(parser)) { + parseerror(parser, "expected builtin number"); + break; + } + + if (OPTS_FLAG(EXPRESSIONS_FOR_BUILTINS)) { + number = (ast_value*)parse_expression_leave(parser, true, false, false); + if (!number) { + parseerror(parser, "builtin number expected"); + break; + } + if (!ast_istype(number, ast_value) || !number->hasvalue || number->cvq != CV_CONST) + { + ast_unref(number); + parseerror(parser, "builtin number must be a compile time constant"); + break; + } + if (number->expression.vtype == TYPE_INTEGER) + builtin_num = number->constval.vint; + else if (number->expression.vtype == TYPE_FLOAT) + builtin_num = number->constval.vfloat; + else { + ast_unref(number); + parseerror(parser, "builtin number must be an integer constant"); + break; + } + ast_unref(number); + + fractional = modff(builtin_num, &integral); + if (builtin_num < 0 || fractional != 0) { + parseerror(parser, "builtin number must be an integer greater than zero"); + break; + } + + /* we only want the integral part anyways */ + builtin_num = integral; + } else if (parser->tok == TOKEN_INTCONST) { + builtin_num = parser_token(parser)->constval.i; + } else { + parseerror(parser, "builtin number must be a compile time constant"); + break; + } + + if (var->hasvalue) { + (void)!parsewarning(parser, WARN_DOUBLE_DECLARATION, + "builtin `%s` has already been defined\n" + " -> previous declaration here: %s:%i", + var->name, ast_ctx(var).file, (int)ast_ctx(var).line); + } + else + { + func = ast_function_new(ast_ctx(var), var->name, var); + if (!func) { + parseerror(parser, "failed to allocate function for `%s`", var->name); + break; + } + vec_push(parser->functions, func); + + func->builtin = -builtin_num-1; + } + + if (OPTS_FLAG(EXPRESSIONS_FOR_BUILTINS) + ? (parser->tok != ',' && parser->tok != ';') + : (!parser_next(parser))) + { + parseerror(parser, "expected comma or semicolon"); + if (func) + ast_function_delete(func); + var->constval.vfunc = NULL; + break; + } + } + else if (var->expression.vtype == TYPE_ARRAY && parser->tok == '{') + { + if (localblock) { + /* Note that fteqcc and most others don't even *have* + * local arrays, so this is not a high priority. + */ + parseerror(parser, "TODO: initializers for local arrays"); + break; + } + /* +static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue, bool with_labels); +*/ + parseerror(parser, "TODO: initializing global arrays is not supported yet!"); + break; + } + else if (var->expression.vtype == TYPE_FUNCTION && (parser->tok == '{' || parser->tok == '[')) + { + if (localblock) { + parseerror(parser, "cannot declare functions within functions"); + break; + } + + if (proto) + ast_ctx(proto) = parser_ctx(parser); + + if (!parse_function_body(parser, var)) + break; + ast_delete(basetype); + for (i = 0; i < vec_size(parser->gotos); ++i) + parseerror(parser, "undefined label: `%s`", parser->gotos[i]->name); + vec_free(parser->gotos); + vec_free(parser->labels); + return true; + } else { + ast_expression *cexp; + ast_value *cval; + + cexp = parse_expression_leave(parser, true, false, false); + if (!cexp) + break; + + if (!localblock) { + cval = (ast_value*)cexp; + if (cval != parser->nil && + (!ast_istype(cval, ast_value) || ((!cval->hasvalue || cval->cvq != CV_CONST) && !cval->isfield)) + ) + { + parseerror(parser, "cannot initialize a global constant variable with a non-constant expression"); + } + else + { + if (!OPTS_FLAG(INITIALIZED_NONCONSTANTS) && + qualifier != CV_VAR) + { + var->cvq = CV_CONST; + } + if (cval == parser->nil) + var->expression.flags |= AST_FLAG_INITIALIZED; + else + { + var->hasvalue = true; + if (cval->expression.vtype == TYPE_STRING) + var->constval.vstring = parser_strdup(cval->constval.vstring); + else if (cval->expression.vtype == TYPE_FIELD) + var->constval.vfield = cval; + else + memcpy(&var->constval, &cval->constval, sizeof(var->constval)); + ast_unref(cval); + } + } + } else { + int cvq; + shunt sy = { NULL, NULL, NULL, NULL }; + cvq = var->cvq; + var->cvq = CV_NONE; + vec_push(sy.out, syexp(ast_ctx(var), (ast_expression*)var)); + vec_push(sy.out, syexp(ast_ctx(cexp), (ast_expression*)cexp)); + vec_push(sy.ops, syop(ast_ctx(var), parser->assign_op)); + if (!parser_sy_apply_operator(parser, &sy)) + ast_unref(cexp); + else { + if (vec_size(sy.out) != 1 && vec_size(sy.ops) != 0) + parseerror(parser, "internal error: leaked operands"); + if (!ast_block_add_expr(localblock, (ast_expression*)sy.out[0].out)) + break; + } + vec_free(sy.out); + vec_free(sy.ops); + vec_free(sy.argc); + var->cvq = cvq; + } + } + +another: + if (parser->tok == ',') { + if (!parser_next(parser)) { + parseerror(parser, "expected another variable"); + break; + } + + if (parser->tok != TOKEN_IDENT) { + parseerror(parser, "expected another variable"); + break; + } + var = ast_value_copy(basetype); + cleanvar = true; + ast_value_set_name(var, parser_tokval(parser)); + if (!parser_next(parser)) { + parseerror(parser, "error parsing variable declaration"); + break; + } + continue; + } + + if (parser->tok != ';') { + parseerror(parser, "missing semicolon after variables"); + break; + } + + if (!parser_next(parser)) { + parseerror(parser, "parse error after variable declaration"); + break; + } + + ast_delete(basetype); + return true; + } + + if (cleanvar && var) + ast_delete(var); + ast_delete(basetype); + return false; + +cleanup: + ast_delete(basetype); + if (cleanvar && var) + ast_delete(var); + if (me[0]) ast_member_delete(me[0]); + if (me[1]) ast_member_delete(me[1]); + if (me[2]) ast_member_delete(me[2]); + return retval; +} + +static bool parser_global_statement(parser_t *parser) +{ + int cvq = CV_WRONG; + bool noref = false; + bool is_static = false; + uint32_t qflags = 0; + ast_value *istype = NULL; + char *vstring = NULL; + + if (parser->tok == TOKEN_IDENT) + istype = parser_find_typedef(parser, parser_tokval(parser), 0); + + if (istype || parser->tok == TOKEN_TYPENAME || parser->tok == '.') + { + return parse_variable(parser, NULL, false, CV_NONE, istype, false, false, 0, NULL); + } + else if (parse_qualifiers(parser, false, &cvq, &noref, &is_static, &qflags, &vstring)) + { + if (cvq == CV_WRONG) + return false; + return parse_variable(parser, NULL, true, cvq, NULL, noref, is_static, qflags, vstring); + } + else if (parser->tok == TOKEN_IDENT && !strcmp(parser_tokval(parser), "enum")) + { + return parse_enum(parser); + } + else if (parser->tok == TOKEN_KEYWORD) + { + if (!strcmp(parser_tokval(parser), "typedef")) { + if (!parser_next(parser)) { + parseerror(parser, "expected type definition after 'typedef'"); + return false; + } + return parse_typedef(parser); + } + parseerror(parser, "unrecognized keyword `%s`", parser_tokval(parser)); + return false; + } + else if (parser->tok == '#') + { + return parse_pragma(parser); + } + else if (parser->tok == '$') + { + if (!parser_next(parser)) { + parseerror(parser, "parse error"); + return false; + } + } + else + { + parseerror(parser, "unexpected token: `%s`", parser->lex->tok.value); + return false; + } + return true; +} + +static uint16_t progdefs_crc_sum(uint16_t old, const char *str) +{ + return util_crc16(old, str, strlen(str)); +} + +static void progdefs_crc_file(const char *str) +{ + /* write to progdefs.h here */ + (void)str; +} + +static uint16_t progdefs_crc_both(uint16_t old, const char *str) +{ + old = progdefs_crc_sum(old, str); + progdefs_crc_file(str); + return old; +} + +static void generate_checksum(parser_t *parser) +{ + uint16_t crc = 0xFFFF; + size_t i; + ast_value *value; + + crc = progdefs_crc_both(crc, "\n/* file generated by qcc, do not modify */\n\ntypedef struct\n{"); + crc = progdefs_crc_sum(crc, "\tint\tpad[28];\n"); + /* + progdefs_crc_file("\tint\tpad;\n"); + progdefs_crc_file("\tint\tofs_return[3];\n"); + progdefs_crc_file("\tint\tofs_parm0[3];\n"); + progdefs_crc_file("\tint\tofs_parm1[3];\n"); + progdefs_crc_file("\tint\tofs_parm2[3];\n"); + progdefs_crc_file("\tint\tofs_parm3[3];\n"); + progdefs_crc_file("\tint\tofs_parm4[3];\n"); + progdefs_crc_file("\tint\tofs_parm5[3];\n"); + progdefs_crc_file("\tint\tofs_parm6[3];\n"); + progdefs_crc_file("\tint\tofs_parm7[3];\n"); + */ + for (i = 0; i < parser->crc_globals; ++i) { + if (!ast_istype(parser->globals[i], ast_value)) + continue; + value = (ast_value*)(parser->globals[i]); + switch (value->expression.vtype) { + case TYPE_FLOAT: crc = progdefs_crc_both(crc, "\tfloat\t"); break; + case TYPE_VECTOR: crc = progdefs_crc_both(crc, "\tvec3_t\t"); break; + case TYPE_STRING: crc = progdefs_crc_both(crc, "\tstring_t\t"); break; + case TYPE_FUNCTION: crc = progdefs_crc_both(crc, "\tfunc_t\t"); break; + default: + crc = progdefs_crc_both(crc, "\tint\t"); + break; + } + crc = progdefs_crc_both(crc, value->name); + crc = progdefs_crc_both(crc, ";\n"); + } + crc = progdefs_crc_both(crc, "} globalvars_t;\n\ntypedef struct\n{\n"); + for (i = 0; i < parser->crc_fields; ++i) { + if (!ast_istype(parser->fields[i], ast_value)) + continue; + value = (ast_value*)(parser->fields[i]); + switch (value->expression.next->vtype) { + case TYPE_FLOAT: crc = progdefs_crc_both(crc, "\tfloat\t"); break; + case TYPE_VECTOR: crc = progdefs_crc_both(crc, "\tvec3_t\t"); break; + case TYPE_STRING: crc = progdefs_crc_both(crc, "\tstring_t\t"); break; + case TYPE_FUNCTION: crc = progdefs_crc_both(crc, "\tfunc_t\t"); break; + default: + crc = progdefs_crc_both(crc, "\tint\t"); + break; + } + crc = progdefs_crc_both(crc, value->name); + crc = progdefs_crc_both(crc, ";\n"); + } + crc = progdefs_crc_both(crc, "} entvars_t;\n\n"); + + parser->code->crc = crc; +} + +parser_t *parser_create() +{ + parser_t *parser; + lex_ctx empty_ctx; + size_t i; + + parser = (parser_t*)mem_a(sizeof(parser_t)); + if (!parser) + return NULL; + + memset(parser, 0, sizeof(*parser)); + + if (!(parser->code = code_init())) { + mem_d(parser); + return NULL; + } + + for (i = 0; i < operator_count; ++i) { + if (operators[i].id == opid1('=')) { + parser->assign_op = operators+i; + break; + } + } + if (!parser->assign_op) { + printf("internal error: initializing parser: failed to find assign operator\n"); + mem_d(parser); + return NULL; + } + + vec_push(parser->variables, parser->htfields = util_htnew(PARSER_HT_SIZE)); + vec_push(parser->variables, parser->htglobals = util_htnew(PARSER_HT_SIZE)); + vec_push(parser->typedefs, util_htnew(TYPEDEF_HT_SIZE)); + vec_push(parser->_blocktypedefs, 0); + + parser->aliases = util_htnew(PARSER_HT_SIZE); + + parser->ht_imm_string = util_htnew(512); + + /* corrector */ + vec_push(parser->correct_variables, correct_trie_new()); + vec_push(parser->correct_variables_score, NULL); + + empty_ctx.file = ""; + empty_ctx.line = 0; + parser->nil = ast_value_new(empty_ctx, "nil", TYPE_NIL); + parser->nil->cvq = CV_CONST; + if (OPTS_FLAG(UNTYPED_NIL)) + util_htset(parser->htglobals, "nil", (void*)parser->nil); + + parser->max_param_count = 1; + + parser->const_vec[0] = ast_value_new(empty_ctx, "", TYPE_NOEXPR); + parser->const_vec[1] = ast_value_new(empty_ctx, "", TYPE_NOEXPR); + parser->const_vec[2] = ast_value_new(empty_ctx, "", TYPE_NOEXPR); + + if (OPTS_OPTION_BOOL(OPTION_ADD_INFO)) { + parser->reserved_version = ast_value_new(empty_ctx, "reserved:version", TYPE_STRING); + parser->reserved_version->cvq = CV_CONST; + parser->reserved_version->hasvalue = true; + parser->reserved_version->expression.flags |= AST_FLAG_INCLUDE_DEF; + parser->reserved_version->constval.vstring = util_strdup(GMQCC_FULL_VERSION_STRING); + } else { + parser->reserved_version = NULL; + } + + return parser; +} + +static bool parser_compile(parser_t *parser) +{ + /* initial lexer/parser state */ + parser->lex->flags.noops = true; + + if (parser_next(parser)) + { + while (parser->tok != TOKEN_EOF && parser->tok < TOKEN_ERROR) + { + if (!parser_global_statement(parser)) { + if (parser->tok == TOKEN_EOF) + parseerror(parser, "unexpected eof"); + else if (compile_errors) + parseerror(parser, "there have been errors, bailing out"); + lex_close(parser->lex); + parser->lex = NULL; + return false; + } + } + } else { + parseerror(parser, "parse error"); + lex_close(parser->lex); + parser->lex = NULL; + return false; + } + + lex_close(parser->lex); + parser->lex = NULL; + + return !compile_errors; +} + +bool parser_compile_file(parser_t *parser, const char *filename) +{ + parser->lex = lex_open(filename); + if (!parser->lex) { + con_err("failed to open file \"%s\"\n", filename); + return false; + } + return parser_compile(parser); +} + +bool parser_compile_string(parser_t *parser, const char *name, const char *str, size_t len) +{ + parser->lex = lex_open_string(str, len, name); + if (!parser->lex) { + con_err("failed to create lexer for string \"%s\"\n", name); + return false; + } + return parser_compile(parser); +} + +static void parser_remove_ast(parser_t *parser) +{ + size_t i; + if (parser->ast_cleaned) + return; + parser->ast_cleaned = true; + for (i = 0; i < vec_size(parser->accessors); ++i) { + ast_delete(parser->accessors[i]->constval.vfunc); + parser->accessors[i]->constval.vfunc = NULL; + ast_delete(parser->accessors[i]); + } + for (i = 0; i < vec_size(parser->functions); ++i) { + ast_delete(parser->functions[i]); + } + for (i = 0; i < vec_size(parser->imm_vector); ++i) { + ast_delete(parser->imm_vector[i]); + } + for (i = 0; i < vec_size(parser->imm_string); ++i) { + ast_delete(parser->imm_string[i]); + } + for (i = 0; i < vec_size(parser->imm_float); ++i) { + ast_delete(parser->imm_float[i]); + } + for (i = 0; i < vec_size(parser->fields); ++i) { + ast_delete(parser->fields[i]); + } + for (i = 0; i < vec_size(parser->globals); ++i) { + ast_delete(parser->globals[i]); + } + vec_free(parser->accessors); + vec_free(parser->functions); + vec_free(parser->imm_vector); + vec_free(parser->imm_string); + util_htdel(parser->ht_imm_string); + vec_free(parser->imm_float); + vec_free(parser->globals); + vec_free(parser->fields); + + for (i = 0; i < vec_size(parser->variables); ++i) + util_htdel(parser->variables[i]); + vec_free(parser->variables); + vec_free(parser->_blocklocals); + vec_free(parser->_locals); + + /* corrector */ + for (i = 0; i < vec_size(parser->correct_variables); ++i) { + correct_del(parser->correct_variables[i], parser->correct_variables_score[i]); + } + vec_free(parser->correct_variables); + vec_free(parser->correct_variables_score); + + + for (i = 0; i < vec_size(parser->_typedefs); ++i) + ast_delete(parser->_typedefs[i]); + vec_free(parser->_typedefs); + for (i = 0; i < vec_size(parser->typedefs); ++i) + util_htdel(parser->typedefs[i]); + vec_free(parser->typedefs); + vec_free(parser->_blocktypedefs); + + vec_free(parser->_block_ctx); + + vec_free(parser->labels); + vec_free(parser->gotos); + vec_free(parser->breaks); + vec_free(parser->continues); + + ast_value_delete(parser->nil); + + ast_value_delete(parser->const_vec[0]); + ast_value_delete(parser->const_vec[1]); + ast_value_delete(parser->const_vec[2]); + + util_htdel(parser->aliases); + intrin_intrinsics_destroy(parser); +} + +void parser_cleanup(parser_t *parser) +{ + parser_remove_ast(parser); + code_cleanup(parser->code); + + mem_d(parser); +} + +bool parser_finish(parser_t *parser, const char *output) +{ + size_t i; + ir_builder *ir; + bool retval = true; + + if (compile_errors) { + con_out("*** there were compile errors\n"); + return false; + } + + ir = ir_builder_new("gmqcc_out"); + if (!ir) { + con_out("failed to allocate builder\n"); + return false; + } + + for (i = 0; i < vec_size(parser->fields); ++i) { + ast_value *field; + bool hasvalue; + if (!ast_istype(parser->fields[i], ast_value)) + continue; + field = (ast_value*)parser->fields[i]; + hasvalue = field->hasvalue; + field->hasvalue = false; + if (!ast_global_codegen((ast_value*)field, ir, true)) { + con_out("failed to generate field %s\n", field->name); + ir_builder_delete(ir); + return false; + } + if (hasvalue) { + ir_value *ifld; + ast_expression *subtype; + field->hasvalue = true; + subtype = field->expression.next; + ifld = ir_builder_create_field(ir, field->name, subtype->vtype); + if (subtype->vtype == TYPE_FIELD) + ifld->fieldtype = subtype->next->vtype; + else if (subtype->vtype == TYPE_FUNCTION) + ifld->outtype = subtype->next->vtype; + (void)!ir_value_set_field(field->ir_v, ifld); + } + } + for (i = 0; i < vec_size(parser->globals); ++i) { + ast_value *asvalue; + if (!ast_istype(parser->globals[i], ast_value)) + continue; + asvalue = (ast_value*)(parser->globals[i]); + if (!asvalue->uses && !asvalue->hasvalue && asvalue->expression.vtype != TYPE_FUNCTION) { + retval = retval && !compile_warning(ast_ctx(asvalue), WARN_UNUSED_VARIABLE, + "unused global: `%s`", asvalue->name); + } + if (!ast_global_codegen(asvalue, ir, false)) { + con_out("failed to generate global %s\n", asvalue->name); + ir_builder_delete(ir); + return false; + } + } + /* Build function vararg accessor ast tree now before generating + * immediates, because the accessors may add new immediates + */ + for (i = 0; i < vec_size(parser->functions); ++i) { + ast_function *f = parser->functions[i]; + if (f->varargs) { + if (parser->max_param_count > vec_size(f->vtype->expression.params)) { + f->varargs->expression.count = parser->max_param_count - vec_size(f->vtype->expression.params); + if (!parser_create_array_setter_impl(parser, f->varargs)) { + con_out("failed to generate vararg setter for %s\n", f->name); + ir_builder_delete(ir); + return false; + } + if (!parser_create_array_getter_impl(parser, f->varargs)) { + con_out("failed to generate vararg getter for %s\n", f->name); + ir_builder_delete(ir); + return false; + } + } else { + ast_delete(f->varargs); + f->varargs = NULL; + } + } + } + /* Now we can generate immediates */ + for (i = 0; i < vec_size(parser->imm_float); ++i) { + if (!ast_global_codegen(parser->imm_float[i], ir, false)) { + con_out("failed to generate global %s\n", parser->imm_float[i]->name); + ir_builder_delete(ir); + return false; + } + } + for (i = 0; i < vec_size(parser->imm_string); ++i) { + if (!ast_global_codegen(parser->imm_string[i], ir, false)) { + con_out("failed to generate global %s\n", parser->imm_string[i]->name); + ir_builder_delete(ir); + return false; + } + } + for (i = 0; i < vec_size(parser->imm_vector); ++i) { + if (!ast_global_codegen(parser->imm_vector[i], ir, false)) { + con_out("failed to generate global %s\n", parser->imm_vector[i]->name); + ir_builder_delete(ir); + return false; + } + } + for (i = 0; i < vec_size(parser->globals); ++i) { + ast_value *asvalue; + if (!ast_istype(parser->globals[i], ast_value)) + continue; + asvalue = (ast_value*)(parser->globals[i]); + if (!(asvalue->expression.flags & AST_FLAG_INITIALIZED)) + { + if (asvalue->cvq == CV_CONST && !asvalue->hasvalue) + (void)!compile_warning(ast_ctx(asvalue), WARN_UNINITIALIZED_CONSTANT, + "uninitialized constant: `%s`", + asvalue->name); + else if ((asvalue->cvq == CV_NONE || asvalue->cvq == CV_CONST) && !asvalue->hasvalue) + (void)!compile_warning(ast_ctx(asvalue), WARN_UNINITIALIZED_GLOBAL, + "uninitialized global: `%s`", + asvalue->name); + } + if (!ast_generate_accessors(asvalue, ir)) { + ir_builder_delete(ir); + return false; + } + } + for (i = 0; i < vec_size(parser->fields); ++i) { + ast_value *asvalue; + asvalue = (ast_value*)(parser->fields[i]->next); + + if (!ast_istype((ast_expression*)asvalue, ast_value)) + continue; + if (asvalue->expression.vtype != TYPE_ARRAY) + continue; + if (!ast_generate_accessors(asvalue, ir)) { + ir_builder_delete(ir); + return false; + } + } + if (parser->reserved_version && + !ast_global_codegen(parser->reserved_version, ir, false)) + { + con_out("failed to generate reserved::version"); + ir_builder_delete(ir); + return false; + } + for (i = 0; i < vec_size(parser->functions); ++i) { + ast_function *f = parser->functions[i]; + if (!ast_function_codegen(f, ir)) { + con_out("failed to generate function %s\n", f->name); + ir_builder_delete(ir); + return false; + } + } + + generate_checksum(parser); + if (OPTS_OPTION_BOOL(OPTION_DUMP)) + ir_builder_dump(ir, con_out); + for (i = 0; i < vec_size(parser->functions); ++i) { + if (!ir_function_finalize(parser->functions[i]->ir_func)) { + con_out("failed to finalize function %s\n", parser->functions[i]->name); + ir_builder_delete(ir); + return false; + } + } + parser_remove_ast(parser); + + if (compile_Werrors) { + con_out("*** there were warnings treated as errors\n"); + compile_show_werrors(); + retval = false; + } + + if (retval) { + if (OPTS_OPTION_BOOL(OPTION_DUMPFIN)) + ir_builder_dump(ir, con_out); + + if (!ir_builder_generate(parser->code, ir, output)) { + con_out("*** failed to generate output file\n"); + ir_builder_delete(ir); + return false; + } + } + ir_builder_delete(ir); + return retval; +} diff --git a/misc/source/gmqcc-src/stat.c b/misc/source/gmqcc-src/stat.c new file mode 100644 index 00000000..21447eb3 --- /dev/null +++ b/misc/source/gmqcc-src/stat.c @@ -0,0 +1,628 @@ +/* + * Copyright (C) 2012, 2013 + * Dale Weiler + * Wolfgang Bumiller + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include + +#include "gmqcc.h" + +/* + * GMQCC performs tons of allocations, constructions, and crazyness + * all around. When trying to optimizes systems, or just get fancy + * statistics out of the compiler, it's often printf mess. This file + * implements the statistics system of the compiler. I.E the allocator + * we use to track allocations, and other systems of interest. + */ +#define ST_SIZE 1024 + +typedef struct stat_mem_block_s { + const char *file; + size_t line; + size_t size; + struct stat_mem_block_s *next; + struct stat_mem_block_s *prev; +} stat_mem_block_t; + +typedef struct { + size_t key; + size_t value; +} stat_size_entry_t, **stat_size_table_t; + +static uint64_t stat_mem_allocated = 0; +static uint64_t stat_mem_deallocated = 0; +static uint64_t stat_mem_allocated_total = 0; +static uint64_t stat_mem_deallocated_total = 0; +static uint64_t stat_mem_high = 0; +static uint64_t stat_mem_peak = 0; +static uint64_t stat_used_strdups = 0; +static uint64_t stat_used_vectors = 0; +static uint64_t stat_used_hashtables = 0; +static uint64_t stat_type_vectors = 0; +static uint64_t stat_type_hashtables = 0; +static stat_size_table_t stat_size_vectors = NULL; +static stat_size_table_t stat_size_hashtables = NULL; +static stat_mem_block_t *stat_mem_block_root = NULL; + +/* + * A tiny size_t key-value hashtbale for tracking vector and hashtable + * sizes. We can use it for other things too, if we need to. This is + * very TIGHT, and efficent in terms of space though. + */ +static stat_size_table_t stat_size_new(void) { + return (stat_size_table_t)memset( + mem_a(sizeof(stat_size_entry_t*) * ST_SIZE), + 0, ST_SIZE * sizeof(stat_size_entry_t*) + ); +} + +static void stat_size_del(stat_size_table_t table) { + size_t i = 0; + for (; i < ST_SIZE; i++) if(table[i]) mem_d(table[i]); + mem_d(table); +} + +static stat_size_entry_t *stat_size_get(stat_size_table_t table, size_t key) { + size_t hash = (key % ST_SIZE); + while (table[hash] && table[hash]->key != key) + hash = (hash + 1) % ST_SIZE; + return table[hash]; +} +static void stat_size_put(stat_size_table_t table, size_t key, size_t value) { + size_t hash = (key % ST_SIZE); + while (table[hash] && table[hash]->key != key) + hash = (hash + 1) % ST_SIZE; + table[hash] = (stat_size_entry_t*)mem_a(sizeof(stat_size_entry_t)); + table[hash]->key = key; + table[hash]->value = value; +} + +/* + * A basic header of information wrapper allocator. Simply stores + * information as a header, returns the memory + 1 past it, can be + * retrieved again with - 1. Where type is stat_mem_block_t*. + */ +void *stat_mem_allocate(size_t size, size_t line, const char *file) { + stat_mem_block_t *info = (stat_mem_block_t*)malloc(sizeof(stat_mem_block_t) + size); + void *data = (void*)(info + 1); + + if(!info) + return NULL; + + info->line = line; + info->size = size; + info->file = file; + info->prev = NULL; + info->next = stat_mem_block_root; + + if (stat_mem_block_root) + stat_mem_block_root->prev = info; + + stat_mem_block_root = info; + stat_mem_allocated += size; + stat_mem_high += size; + stat_mem_allocated_total ++; + + if (stat_mem_high > stat_mem_peak) + stat_mem_peak = stat_mem_high; + + return data; +} + +void stat_mem_deallocate(void *ptr) { + stat_mem_block_t *info = NULL; + + if (!ptr) + return; + + info = ((stat_mem_block_t*)ptr - 1); + + stat_mem_deallocated += info->size; + stat_mem_high -= info->size; + stat_mem_deallocated_total ++; + + if (info->prev) info->prev->next = info->next; + if (info->next) info->next->prev = info->prev; + + /* move ahead */ + if (info == stat_mem_block_root) + stat_mem_block_root = info->next; + + free(info); +} + +void *stat_mem_reallocate(void *ptr, size_t size, size_t line, const char *file) { + stat_mem_block_t *oldinfo = NULL; + stat_mem_block_t *newinfo; + + if (!ptr) + return stat_mem_allocate(size, line, file); + + /* stay consistent with glic */ + if (!size) { + stat_mem_deallocate(ptr); + return NULL; + } + + oldinfo = ((stat_mem_block_t*)ptr - 1); + newinfo = ((stat_mem_block_t*)malloc(sizeof(stat_mem_block_t) + size)); + + if (!newinfo) { + stat_mem_deallocate(ptr); + return NULL; + } + + memcpy(newinfo+1, oldinfo+1, oldinfo->size); + + if (oldinfo->prev) oldinfo->prev->next = oldinfo->next; + if (oldinfo->next) oldinfo->next->prev = oldinfo->prev; + + /* move ahead */ + if (oldinfo == stat_mem_block_root) + stat_mem_block_root = oldinfo->next; + + newinfo->line = line; + newinfo->size = size; + newinfo->file = file; + newinfo->prev = NULL; + newinfo->next = stat_mem_block_root; + + if (stat_mem_block_root) + stat_mem_block_root->prev = newinfo; + + stat_mem_block_root = newinfo; + stat_mem_allocated -= oldinfo->size; + stat_mem_high -= oldinfo->size; + stat_mem_allocated += newinfo->size; + stat_mem_high += newinfo->size; + + if (stat_mem_high > stat_mem_peak) + stat_mem_peak = stat_mem_high; + + free(oldinfo); + + return newinfo + 1; +} + +/* + * strdup does it's own malloc, we need to track malloc. We don't want + * to overwrite malloc though, infact, we can't really hook it at all + * without library specific assumptions. So we re implement strdup. + */ +char *stat_mem_strdup(const char *src, size_t line, const char *file, bool empty) { + size_t len = 0; + char *ptr = NULL; + + if (!src) + return NULL; + + len = strlen(src); + if (((!empty) ? len : true) && (ptr = (char*)stat_mem_allocate(len + 1, line, file))) { + memcpy(ptr, src, len); + ptr[len] = '\0'; + } + + stat_used_strdups ++; + return ptr; +} + +/* + * The reallocate function for resizing vectors. + */ +void _util_vec_grow(void **a, size_t i, size_t s) { + vector_t *d = vec_meta(*a); + size_t m = 0; + stat_size_entry_t *e = NULL; + void *p = NULL; + + if (*a) { + m = 2 * d->allocated + i; + p = mem_r(d, s * m + sizeof(vector_t)); + } else { + m = i + 1; + p = mem_a(s * m + sizeof(vector_t)); + ((vector_t*)p)->used = 0; + stat_used_vectors++; + } + + if (!stat_size_vectors) + stat_size_vectors = stat_size_new(); + + if ((e = stat_size_get(stat_size_vectors, s))) { + e->value ++; + } else { + stat_size_put(stat_size_vectors, s, 1); /* start off with 1 */ + stat_type_vectors++; + } + + *a = (vector_t*)p + 1; + vec_meta(*a)->allocated = m; +} + +/* + * Hash table for generic data, based on dynamic memory allocations + * all around. This is the internal interface, please look for + * EXPOSED INTERFACE comment below + */ +typedef struct hash_node_t { + char *key; /* the key for this node in table */ + void *value; /* pointer to the data as void* */ + struct hash_node_t *next; /* next node (linked list) */ +} hash_node_t; + +GMQCC_INLINE size_t util_hthash(hash_table_t *ht, const char *key) { + const uint32_t mix = 0x5BD1E995; + const uint32_t rot = 24; + size_t size = strlen(key); + uint32_t hash = 0x1EF0 /* LICRC TAB */ ^ size; + uint32_t alias = 0; + const unsigned char *data = (const unsigned char*)key; + + while (size >= 4) { + alias = (data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24)); + alias *= mix; + alias ^= alias >> rot; + alias *= mix; + + hash *= mix; + hash ^= alias; + + data += 4; + size -= 4; + } + + switch (size) { + case 3: hash ^= data[2] << 16; + case 2: hash ^= data[1] << 8; + case 1: hash ^= data[0]; + hash *= mix; + } + + hash ^= hash >> 13; + hash *= mix; + hash ^= hash >> 15; + + return (size_t) (hash % ht->size); +} + +static hash_node_t *_util_htnewpair(const char *key, void *value) { + hash_node_t *node; + if (!(node = (hash_node_t*)mem_a(sizeof(hash_node_t)))) + return NULL; + + if (!(node->key = util_strdupe(key))) { + mem_d(node); + return NULL; + } + + node->value = value; + node->next = NULL; + + return node; +} + +/* + * EXPOSED INTERFACE for the hashtable implementation + * util_htnew(size) -- to make a new hashtable + * util_htset(table, key, value, sizeof(value)) -- to set something in the table + * util_htget(table, key) -- to get something from the table + * util_htdel(table) -- to delete the table + */ +hash_table_t *util_htnew(size_t size) { + hash_table_t *hashtable = NULL; + stat_size_entry_t *find = NULL; + + if (size < 1) + return NULL; + + if (!stat_size_hashtables) + stat_size_hashtables = stat_size_new(); + + if (!(hashtable = (hash_table_t*)mem_a(sizeof(hash_table_t)))) + return NULL; + + if (!(hashtable->table = (hash_node_t**)mem_a(sizeof(hash_node_t*) * size))) { + mem_d(hashtable); + return NULL; + } + + if ((find = stat_size_get(stat_size_hashtables, size))) + find->value++; + else { + stat_type_hashtables++; + stat_size_put(stat_size_hashtables, size, 1); + } + + hashtable->size = size; + memset(hashtable->table, 0, sizeof(hash_node_t*) * size); + + stat_used_hashtables++; + return hashtable; +} + +void util_htseth(hash_table_t *ht, const char *key, size_t bin, void *value) { + hash_node_t *newnode = NULL; + hash_node_t *next = NULL; + hash_node_t *last = NULL; + + next = ht->table[bin]; + + while (next && next->key && strcmp(key, next->key) > 0) + last = next, next = next->next; + + /* already in table, do a replace */ + if (next && next->key && strcmp(key, next->key) == 0) { + next->value = value; + } else { + /* not found, grow a pair man :P */ + newnode = _util_htnewpair(key, value); + if (next == ht->table[bin]) { + newnode->next = next; + ht->table[bin] = newnode; + } else if (!next) { + last->next = newnode; + } else { + newnode->next = next; + last->next = newnode; + } + } +} + +void util_htset(hash_table_t *ht, const char *key, void *value) { + util_htseth(ht, key, util_hthash(ht, key), value); +} + +void *util_htgeth(hash_table_t *ht, const char *key, size_t bin) { + hash_node_t *pair = ht->table[bin]; + + while (pair && pair->key && strcmp(key, pair->key) > 0) + pair = pair->next; + + if (!pair || !pair->key || strcmp(key, pair->key) != 0) + return NULL; + + return pair->value; +} + +void *util_htget(hash_table_t *ht, const char *key) { + return util_htgeth(ht, key, util_hthash(ht, key)); +} + +void *code_util_str_htgeth(hash_table_t *ht, const char *key, size_t bin) { + hash_node_t *pair; + size_t len, keylen; + int cmp; + + keylen = strlen(key); + + pair = ht->table[bin]; + while (pair && pair->key) { + len = strlen(pair->key); + if (len < keylen) { + pair = pair->next; + continue; + } + if (keylen == len) { + cmp = strcmp(key, pair->key); + if (cmp == 0) + return pair->value; + if (cmp < 0) + return NULL; + pair = pair->next; + continue; + } + cmp = strcmp(key, pair->key + len - keylen); + if (cmp == 0) { + uintptr_t up = (uintptr_t)pair->value; + up += len - keylen; + return (void*)up; + } + pair = pair->next; + } + return NULL; +} + +/* + * Free all allocated data in a hashtable, this is quite the amount + * of work. + */ +void util_htrem(hash_table_t *ht, void (*callback)(void *data)) { + size_t i = 0; + for (; i < ht->size; i++) { + hash_node_t *n = ht->table[i]; + hash_node_t *p; + + /* free in list */ + while (n) { + if (n->key) + mem_d(n->key); + if (callback) + callback(n->value); + p = n; + n = n->next; + mem_d(p); + } + + } + /* free table */ + mem_d(ht->table); + mem_d(ht); +} + +void util_htrmh(hash_table_t *ht, const char *key, size_t bin, void (*cb)(void*)) { + hash_node_t **pair = &ht->table[bin]; + hash_node_t *tmp; + + while (*pair && (*pair)->key && strcmp(key, (*pair)->key) > 0) + pair = &(*pair)->next; + + tmp = *pair; + if (!tmp || !tmp->key || strcmp(key, tmp->key) != 0) + return; + + if (cb) + (*cb)(tmp->value); + + *pair = tmp->next; + mem_d(tmp->key); + mem_d(tmp); +} + +void util_htrm(hash_table_t *ht, const char *key, void (*cb)(void*)) { + util_htrmh(ht, key, util_hthash(ht, key), cb); +} + +void util_htdel(hash_table_t *ht) { + util_htrem(ht, NULL); +} + +/* + * The following functions below implement printing / dumping of statistical + * information. + */ +static void stat_dump_mem_contents(stat_mem_block_t *memory, uint16_t cols) { + uint32_t i, j; + for (i = 0; i < memory->size + ((memory->size % cols) ? (cols - memory->size % cols) : 0); i++) { + if (i % cols == 0) con_out(" 0x%06X: ", i); + if (i < memory->size) con_out("%02X " , 0xFF & ((unsigned char*)(memory + 1))[i]); + else con_out(" "); + + if ((uint16_t)(i % cols) == (cols - 1)) { + for (j = i - (cols - 1); j <= i; j++) { + con_out("%c", + (j >= memory->size) + ? ' ' + : (isprint(((unsigned char*)(memory + 1))[j])) + ? 0xFF & ((unsigned char*)(memory + 1)) [j] + : '.' + ); + } + con_out("\n"); + } + } +} + +static void stat_dump_mem_leaks(void) { + stat_mem_block_t *info; + for (info = stat_mem_block_root; info; info = info->next) { + con_out("lost: %u (bytes) at %s:%u\n", + info->size, + info->file, + info->line + ); + + stat_dump_mem_contents(info, OPTS_OPTION_U16(OPTION_MEMDUMPCOLS)); + } +} + +static void stat_dump_mem_info(void) { + con_out("Memory information:\n\ + Total allocations: %llu\n\ + Total deallocations: %llu\n\ + Total allocated: %f (MB)\n\ + Total deallocated: %f (MB)\n\ + Total peak memory: %f (MB)\n\ + Total leaked memory: %f (MB) in %llu allocations\n", + stat_mem_allocated_total, + stat_mem_deallocated_total, + (float)(stat_mem_allocated) / 1048576.0f, + (float)(stat_mem_deallocated) / 1048576.0f, + (float)(stat_mem_peak) / 1048576.0f, + (float)(stat_mem_allocated - stat_mem_deallocated) / 1048576.0f, + stat_mem_allocated_total - stat_mem_deallocated_total + ); +} + +static void stat_dump_stats_table(stat_size_table_t table, const char *string, uint64_t *size) { + size_t i,j; + + if (!table) + return; + + for (i = 0, j = 1; i < ST_SIZE; i++) { + stat_size_entry_t *entry; + + if (!(entry = table[i])) + continue; + + con_out(string, (unsigned)j, (unsigned)entry->key, (unsigned)entry->value); + j++; + + if (size) + *size += entry->key * entry->value; + } +} + +void stat_info() { + if (OPTS_OPTION_BOOL(OPTION_DEBUG)) + stat_dump_mem_leaks(); + + if (OPTS_OPTION_BOOL(OPTION_DEBUG) || + OPTS_OPTION_BOOL(OPTION_MEMCHK)) + stat_dump_mem_info(); + + if (OPTS_OPTION_BOOL(OPTION_MEMCHK) || + OPTS_OPTION_BOOL(OPTION_STATISTICS)) { + uint64_t mem = 0; + + con_out("\nAdditional Statistics:\n\ + Total vectors allocated: %llu\n\ + Total string duplicates: %llu\n\ + Total hashtables allocated: %llu\n\ + Total unique vector sizes: %llu\n", + stat_used_vectors, + stat_used_strdups, + stat_used_hashtables, + stat_type_vectors + ); + + stat_dump_stats_table ( + stat_size_vectors, + " %2u| # of %4u byte vectors: %u\n", + &mem + ); + + con_out ( + " Total unique hashtable sizes: %llu\n", + stat_type_hashtables + ); + + stat_dump_stats_table ( + stat_size_hashtables, + " %2u| # of %4u element hashtables: %u\n", + NULL + ); + + con_out ( + " Total vector memory: %f (MB)\n", + (float)(mem) / 1048576.0f + ); + } + + if (stat_size_vectors) + stat_size_del(stat_size_vectors); + if (stat_size_hashtables) + stat_size_del(stat_size_hashtables); +} +#undef ST_SIZE diff --git a/misc/source/gmqcc-src/syntax/README b/misc/source/gmqcc-src/syntax/README new file mode 100644 index 00000000..5a5c4b4c --- /dev/null +++ b/misc/source/gmqcc-src/syntax/README @@ -0,0 +1,24 @@ +Here exists some syntax highlighting configuration files for various +text editors. Inside each directory exists some documentaiton on how +you can install the configuration file correctly. + +Currently the supported text editors: + geany + kate + kwrite - uses kate syntax highlighting + kdevelop - uses kate syntax highlighting + QtCreator - supports kate syntax highlighting + gtksourceview - main source viewer in GNOME + gedit - uses gtksourceview + sandy - uses gtksourceview + nano + jedit + + +Other text editors we plan to provide syntax highlighting configuration +files for (but never got around to figuring out) + vim + emacs + +If your text editor is not supported and you'd like to create syntax +highlighting support for it, don't hesitate to share it with us. diff --git a/misc/source/gmqcc-src/syntax/geany/README b/misc/source/gmqcc-src/syntax/geany/README new file mode 100644 index 00000000..c527d3fa --- /dev/null +++ b/misc/source/gmqcc-src/syntax/geany/README @@ -0,0 +1,8 @@ +To use the geany syntax highlighting install filetypes.qc to the syntax +directory for geany. + +# Can be installed globally to +/usr/share/geany/ + +# Can be installed locally to +~/.config/geany/filedefs/ diff --git a/misc/source/gmqcc-src/syntax/geany/filetypes.qc b/misc/source/gmqcc-src/syntax/geany/filetypes.qc new file mode 100644 index 00000000..d84bb7e4 --- /dev/null +++ b/misc/source/gmqcc-src/syntax/geany/filetypes.qc @@ -0,0 +1,55 @@ +[styling] +default=default +comment=comment +commentline=comment_line +commentdoc=comment_doc +preprocessorcomment=comment +number=number_1 +word=keyword_1 +word2=keyword_2 +string=string_1 +stringraw=string_2 +character=character +uuid=other +preprocessor=preprocessor +operator=operator +identifier=identifier_1 +stringeol=string_eol +verbatim=string_2 +regex=regex +commentlinedoc=comment_line_doc +commentdockeyword=comment_doc_keyword +commentdockeyworderror=comment_doc_keyword_error +globalclass=class +tripleverbatim=string_2 +hashquotedstring=string_2 + +[keywords] +primary=break case const continue string default do else enum float for goto if return switch typedef void while false nil true +secondary= +docComment= + +[lexer_properties] +styling.within.preprocessor=1 +lexer.cpp.track.preprocessor=0 +preprocessor.symbol.$(file.patterns.cpp)=# +preprocessor.start.$(file.patterns.cpp)=if ifdef ifndef +preprocessor.middle.$(file.patterns.cpp)=else elif +preprocessor.end.$(file.patterns.cpp)=endif + +[settings] +extension=qc +comment_single=// +comment_open=/* +comment_close=*/ +comment_use_indent=true +context_action_cmd= + +[indentation] +width=4 +type=0 + +[build_settings] +compiler=gmqcc -Wall "%f" -o "%e" +linker= +run_cmd=qcvm "./%e" diff --git a/misc/source/gmqcc-src/syntax/gtksourceview/README b/misc/source/gmqcc-src/syntax/gtksourceview/README new file mode 100644 index 00000000..73a6072b --- /dev/null +++ b/misc/source/gmqcc-src/syntax/gtksourceview/README @@ -0,0 +1,5 @@ +To use the gtksourceview syntax highlighting install qc.lang to the syntax +directory for gtksourceview + +# Can be installed globally to +/usr/share/gtksourceview-[version]/language-specs/ diff --git a/misc/source/gmqcc-src/syntax/gtksourceview/qc.lang b/misc/source/gmqcc-src/syntax/gtksourceview/qc.lang new file mode 100644 index 00000000..827290f3 --- /dev/null +++ b/misc/source/gmqcc-src/syntax/gtksourceview/qc.lang @@ -0,0 +1,173 @@ + + + + *.qc + // + /* + */ + + + +