/* sketch.y Copyright (C) 2005,2006,2007 Eugene K. Ressler, Jr. This file is part of Sketch, a small, simple system for making 3d drawings with LaTeX and the PSTricks or TikZ package. Sketch is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. Sketch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Sketch; see the file COPYING.txt. If not, see http://www.gnu.org/copyleft */ %{ #include #include #if defined(_WIN32) #include #if !defined(alloca) #define alloca _alloca #endif #define YYSTACK_USE_ALLOCA 1 // turn of warning about unused goto label in bison skeleton #pragma warning(disable:4102) #endif #include "parse.h" #include "expr.h" #include "bsp.h" #include "global.h" int yylex(void); void yyerror (char *s) /* Called by yyparse on error */ { extern SRC_LINE line; err(line, "%s", s); } static SYMBOL_TABLE *sym_tab; static BSP_TREE bsp; static FILE *yyout; // exported parse tree and global environment static OBJECT *objects; %} %union { char *str; FLOAT flt; POINT_3D pt; VECTOR_3D vec; TRANSFORM xf; EXPR_VAL exv; SYMBOL_NAME name; SYMBOL_NAME_NODE *name_list; OBJECT *obj; OPTS *opts; int bool; int index; } %token ID PAREN_ID BRACKET_ID %token DBL_BRACKET_ID CURLY_ID ANGLE_ID %token BRACKET_ID_LIST %token NUM %token OPTS_STR SPECIAL %token TICK %token THEN DEF EMPTY_ANGLE %token DOTS LINE CURVE POLYGON REPEAT SWEEP PUT SPECIAL %token TRANSLATE ROTATE SCALE PROJECT PERSPECTIVE VIEW %token SQRT SIN COS ATAN2 UNIT INVERSE %token GLOBAL SET PICTUREBOX FRAME CAMERA %token LANGUAGE PSTRICKS TIKZ LaTeX ConTeXt %type tagged_defs %type options %type scalar scalar_expr opt_baseline %type point point_expr %type vector vector_expr %type transform transform_expr %type expr %type defs_and_decls rev_defs_and_decls decl def_or_decl %type defable points rev_points transforms rev_transforms %type opt_star %type output_language comma_macro_package graphics_language macro_package %right THEN %left '-' '+' %left '*' '/' '.' %left NEG /* negation--unary minus */ %right '^' /* exponentiation */ %left TICK /* point and vector indexing */ %% input : defs_and_decls global_decl_block { objects = $1; } /* sets global_env as a side effect */ global_decl_block : GLOBAL '{' global_decls '}' | /* empty */ ; global_decls : global_decls global_decl | global_decl ; global_decl : SET OPTS_STR { set_global_env_opts(global_env, $2, line); } | PICTUREBOX '[' scalar_expr ']' { set_global_baseline(global_env, $3, line); } | PICTUREBOX opt_baseline point point { set_global_baseline(global_env, $2, line); set_global_env_extent(global_env, $3, $4, line); } | CAMERA transform_expr { set_global_env_camera(global_env, $2, line); } | FRAME { set_global_env_frame(global_env, NULL, line); } | FRAME OPTS_STR { set_global_env_frame(global_env, $2, line); } | LANGUAGE output_language { set_global_output_language(global_env, $2, line); } | def ; output_language : graphics_language comma_macro_package { $$ = $1 | $2; } ; graphics_language : PSTRICKS { $$ = GEOL_PSTRICKS; } | TIKZ { $$ = GEOL_TIKZ; } ; comma_macro_package : ',' macro_package { $$ = $2; } | /* empty */ { $$ = GEOL_LATEX; } ; macro_package : LaTeX { $$ = GEOL_LATEX; } | ConTeXt { $$ = GEOL_CONTEXT; } ; opt_baseline : '[' scalar_expr ']' { $$ = $2; } | /* empty */ { $$ = NO_BASELINE; } ; defs_and_decls : rev_defs_and_decls { $$ = sibling_reverse($1); } ; rev_defs_and_decls : rev_defs_and_decls def_or_decl { $$ = cat_objects($2, $1); } | def_or_decl { $$ = $1; } ; def_or_decl : def { $$ = NULL; } | decl { $$ = $1; } ; /* slightly strange rules are to avoid inherited attributes */ def : DEF ID defable { new_symbol(sym_tab, $2, 0, $3, line); } | tagged_defs EMPTY_ANGLE defable { new_symbol(sym_tab, $1, 0, $3, line); } | DEF ID EMPTY_ANGLE { new_symbol(sym_tab, $2, 0, new_tag_def(), line); } ; tagged_defs : DEF ID ANGLE_ID defable { strcpy($$, new_symbol(sym_tab, $2, $3, $4, line) ? "" : $2); } | tagged_defs ANGLE_ID defable { strcpy($$, new_symbol(sym_tab, $1, $2, $3, line) ? "" : $1); } ; defable : expr { $$ = object_from_expr(&$1); } | decl { $$ = $1; } | OPTS_STR { $$ = new_opts_def($1, line); } ; decl : DOTS options points { $$ = new_dots($2, $3); } | LINE options points { $$ = new_line($2, $3); } | CURVE options points { $$ = new_curve($2, $3); } | POLYGON options points { $$ = new_polygon($2, $3); } | SWEEP options '{' scalar_expr opt_star ',' transforms '}' point { $$ = new_sweep($2, $4, $5, $7, new_point_def($9)); } | SWEEP options '{' scalar_expr opt_star ',' transforms '}' decl { $$ = new_sweep($2, $4, $5, $7, $9); } | REPEAT '{' scalar_expr ',' transforms '}' decl { $$ = new_repeat($3, $5, $7); } | PUT '{' transform_expr '}' decl { $$ = new_compound($3, $5); } | SPECIAL options points { $$ = new_special($1, $2, $3, line); } | SPECIAL options { $$ = new_special($1, $2, new_point_def(origin_3d), line); } | CURLY_ID { look_up_drawable(sym_tab, &$$, line, $1); } | '{' { sym_tab = new_scope(sym_tab); } defs_and_decls { sym_tab = old_scope(sym_tab); } '}' { if ($3 == NULL) err(line, "no drawables in compound declaration"); $$ = $3; } ; opt_star : EMPTY_ANGLE { $$ = 1; } | /* empty */ { $$ = 0; } ; options : OPTS_STR { $$ = new_opts($1, line); } | BRACKET_ID { look_up_opts(sym_tab, &$$, line, $1); } | BRACKET_ID_LIST { look_up_multiple_opts(sym_tab, &$$, line, $1); delete_symbol_name_list(&$1); } | /* empty */ { $$ = NULL; } ; points : rev_points { $$ = sibling_reverse($1); } ; rev_points : rev_points point { $$ = cat_objects(new_point_def($2), $1); } | point { $$ = new_point_def($1); } ; transforms : rev_transforms { $$ = sibling_reverse($1); } ; rev_transforms : rev_transforms ',' transform_expr { $$ = cat_objects(new_transform_def($3), $1); } | transform_expr { $$ = new_transform_def($1); } ; expr : scalar { set_float(&$$, $1); } | point { set_point(&$$, $1); } | vector { set_vector(&$$, $1); } | transform { set_transform(&$$, $1); } | expr '+' expr { do_add(&$$, &$1, &$3, line); } | expr '-' expr { do_sub(&$$, &$1, &$3, line); } | expr '*' expr { do_mul(&$$, &$1, &$3, line); } | expr '/' expr { do_dvd(&$$, &$1, &$3, line); } | expr '.' expr { do_dot(&$$, &$1, &$3, line); } | expr THEN expr { do_thn(&$$, &$1, &$3, line); } | '|' expr '|' { do_mag(&$$, &$2, line); } | '-' expr %prec NEG { do_neg(&$$, &$2, line); } | expr '^' expr { do_pwr(&$$, &$1, &$3, line); } | '(' expr ')' { $$ = $2; } | UNIT expr ')' { do_unit(&$$, &$2, line); } | SQRT expr ')' { do_sqrt(&$$, &$2, line); } | SIN expr ')' { do_sin(&$$, &$2, line); } | COS expr ')' { do_cos(&$$, &$2, line); } | ATAN2 expr ',' expr ')' { do_atan2(&$$, &$2, &$4, line); } | expr TICK { do_index(&$$, &$1, $2, line); } ; scalar : NUM { $$ = $1; } | ID { look_up_scalar(sym_tab, &$$, line, $1); } ; scalar_expr : expr { coerce_to_float(&$1, &$$, line); } ; point : '(' scalar_expr ',' scalar_expr ',' scalar_expr ')' { $$[X] = $2; $$[Y] = $4; $$[Z] = $6; } | '(' scalar_expr ',' scalar_expr ')' { $$[X] = $2; $$[Y] = $4; $$[Z] = 0; } | PAREN_ID { look_up_point(sym_tab, $$, line, $1); } ; point_expr : expr { coerce_to_point(&$1, $$, line); } ; vector : '[' scalar_expr ',' scalar_expr ',' scalar_expr ']' { $$[X] = $2; $$[Y] = $4; $$[Z] = $6; } | '[' scalar_expr ',' scalar_expr ']' { $$[X] = $2; $$[Y] = $4; $$[Z] = 0; } | BRACKET_ID { look_up_vector(sym_tab, $$, line, $1); } vector_expr : expr { coerce_to_vector(&$1, $$, line); } ; transform : '[' '[' scalar_expr ',' scalar_expr ',' scalar_expr',' scalar_expr ']' '[' scalar_expr ',' scalar_expr ',' scalar_expr',' scalar_expr ']' '[' scalar_expr ',' scalar_expr ',' scalar_expr',' scalar_expr ']' '[' scalar_expr ',' scalar_expr ',' scalar_expr',' scalar_expr ']' ']' { // transform is column major while elements are row major $$[0] = $3; $$[4] = $5; $$[8] = $7; $$[12] = $9; $$[1] = $12; $$[5] = $14; $$[9] = $16; $$[13] = $18; $$[2] = $21; $$[6] = $23; $$[10] = $25; $$[14] = $27; $$[3] = $30; $$[7] = $32; $$[11] = $34; $$[15] = $36; } | ROTATE scalar_expr ')' { set_angle_axis_rot_about_point($$, $2 * (PI/180), 0, 0); } | ROTATE scalar_expr ',' expr ')' { if (EXPR_TYPE_IS(&$4, E_POINT)) set_angle_axis_rot_about_point($$, $2 * (PI/180), $4.val.pt, 0); else if (EXPR_TYPE_IS(&$4, E_VECTOR)) set_angle_axis_rot_about_point($$, $2 * (PI/180), 0, $4.val.vec); else err(line, "expected point or vector rotation parameter, and it's a %s", expr_val_type_str[$4.tag]); } | ROTATE scalar_expr ',' point_expr ',' vector_expr ')' { set_angle_axis_rot_about_point($$, $2 * (PI/180), $4, $6); } | TRANSLATE vector_expr ')' { set_translation($$, $2[X], $2[Y], $2[Z]); } | SCALE expr ')' { if ($2.tag == E_FLOAT) { FLOAT s = $2.val.flt; set_scale($$, s, s, s); } else if ($2.tag == E_VECTOR) { VECTOR v = $2.val.vec; set_scale($$, v[X], v[Y], v[Z]); } else { err(line, "expected scalar or vector scale parameter, and it's a %s", expr_val_type_str[$2.tag]); set_ident($$); } } | PROJECT ')' { set_parallel_projection($$); } | PROJECT scalar_expr ')' { set_perspective_projection($$, $2); } | PERSPECTIVE scalar_expr ')' { set_perspective_transform($$, $2); } | VIEW point_expr ',' expr ',' vector_expr ')' { if ($4.tag == E_VECTOR) set_view_transform($$, $2, $4.val.vec, $6); else if ($4.tag == E_POINT) set_view_transform_with_look_at($$, $2, $4.val.pt, $6); else err(line, "expected point or vector view parameter, and it's a %s", expr_val_type_str[$4.tag]); } | VIEW point_expr ',' expr ')' { if ($4.tag == E_VECTOR) set_view_transform($$, $2, $4.val.vec, NULL); else if ($4.tag == E_POINT) set_view_transform_with_look_at($$, $2, $4.val.pt, NULL); else err(line, "expected point or vector view parameter, and it's a %s", expr_val_type_str[$4.tag]); } | VIEW point_expr ')' { set_view_transform($$, $2, NULL, NULL); } | INVERSE transform_expr ')' { do_inverse($$, $2, line); } | DBL_BRACKET_ID { look_up_transform(sym_tab, $$, line, $1); } ; transform_expr : expr { coerce_to_transform(&$1, $$, line); } ; %% int parse(SYMBOL_TABLE *st) { int ret; objects = NULL; sym_tab = st; ret = yyparse(); // should set sym_tab back to NULL sym_tab = old_scope(sym_tab); if (sym_tab) die(no_line, "zombie symbol table"); return ret; } OBJECT *parsed_objects(void) { return objects; }