pax_global_header00006660000000000000000000000064147605622140014521gustar00rootroot0000000000000052 comment=3949bd29ee53b7b7503b649625f1660ebc0f19c8 kinnison-pikchr-4a1b52a/000077500000000000000000000000001476056221400152305ustar00rootroot00000000000000kinnison-pikchr-4a1b52a/.envrc000066400000000000000000000000101476056221400163350ustar00rootroot00000000000000use nix kinnison-pikchr-4a1b52a/.github/000077500000000000000000000000001476056221400165705ustar00rootroot00000000000000kinnison-pikchr-4a1b52a/.github/workflows/000077500000000000000000000000001476056221400206255ustar00rootroot00000000000000kinnison-pikchr-4a1b52a/.github/workflows/main.yaml000066400000000000000000000061321476056221400224370ustar00rootroot00000000000000name: "main" on: pull_request: branches: - "*" push: branches: - "*" jobs: hygiene: name: Hygiene runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest] toolchain: [stable, beta, nightly] include: - os: macos-latest toolchain: stable - os: windows-latest toolchain: stable defaults: run: shell: bash steps: - name: Acquire source code uses: actions/checkout@v2 - name: Acquire Rust toolchain uses: actions-rs/toolchain@v1 with: toolchain: ${{ matrix.toolchain }} override: true profile: minimal components: rustfmt, clippy id: toolchain - name: Cache cargo registry uses: actions/cache@v1 with: path: ~/.cargo/registry key: checks-${{ runner.os }}-cargo-registry-trimmed - name: Cache cargo git trees uses: actions/cache@v1 with: path: ~/.cargo/git key: checks-${{ runner.os }}-cargo-gits-trimmed - name: Cache cargo build uses: actions/cache@v1 with: path: target key: checks-${{ runner.os }}-cargo-target-dir-${{ steps.toolchain.outputs.rustc_hash }} - name: "Run clippy" uses: actions-rs/cargo@v1 with: command: clippy args: --all --tests -- -D clippy::all -D warnings - name: "Run formatting check" uses: actions-rs/cargo@v1 with: command: fmt args: --all -- --check build: name: "Build/Test" runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest] toolchain: [nightly, beta, stable] include: - os: macos-latest toolchain: stable - os: windows-latest toolchain: stable defaults: run: shell: bash steps: - name: Acquire source code uses: actions/checkout@v2 - name: Acquire Rust toolchain uses: actions-rs/toolchain@v1 with: toolchain: ${{ matrix.toolchain }} override: true profile: minimal id: toolchain - name: Cache cargo registry uses: actions/cache@v1 with: path: ~/.cargo/registry key: checks-${{ runner.os }}-cargo-registry-trimmed - name: Cache cargo git trees uses: actions/cache@v1 with: path: ~/.cargo/git key: checks-${{ runner.os }}-cargo-gits-trimmed - name: Cache cargo build uses: actions/cache@v1 with: path: target key: checks-${{ runner.os }}-cargo-target-dir-${{ steps.toolchain.outputs.rustc_hash }} - name: "Set backtraces on" run: echo "RUST_BACKTRACE=1" >> "$GITHUB_ENV" - name: "Run build" uses: actions-rs/cargo@v1 with: command: build args: --all - name: "Run tests" uses: actions-rs/cargo@v1 with: command: test args: --all kinnison-pikchr-4a1b52a/.gitignore000066400000000000000000000000231476056221400172130ustar00rootroot00000000000000/target Cargo.lock kinnison-pikchr-4a1b52a/Cargo.toml000066400000000000000000000006671476056221400171710ustar00rootroot00000000000000[package] name = "pikchr" version = "0.1.4" authors = ["Daniel Silverstone "] edition = "2021" description = "PIC-like diagramming language to SVG converter" repository = "https://github.com/kinnison/pikchr" readme = "README.md" keywords = ["markdown", "md", "html", "svg", "pic"] license = "MIT OR Apache-2.0" [dependencies] libc = "0.2" [build-dependencies] cc = "1.2" [workspace] members = ["pikchr-cli"] kinnison-pikchr-4a1b52a/LICENSE-APACHE.txt000066400000000000000000000261361476056221400200020ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. kinnison-pikchr-4a1b52a/LICENSE-MIT.txt000066400000000000000000000020561476056221400175050ustar00rootroot00000000000000Copyright (c) 2015 The markdown.rs Developers 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. kinnison-pikchr-4a1b52a/README.md000066400000000000000000000021651476056221400165130ustar00rootroot00000000000000Pikchr - Diagram renderer ========================= Taken from the [pikchr](https://pikchr.org/home/doc/trunk/homepage.md) homepage: > Pikchr (pronounced like "picture") is a [PIC][1]-like markup > language for diagrams in technical documentation. Pikchr is > designed to be embedded in [fenced code blocks][2] of > Markdown (or in similar mechanisms in other markup languages) > to provide a convenient means of showing diagrams. > > [1]: https://en.wikipedia.org/wiki/Pic_language > [2]: https://spec.commonmark.org/0.29/#fenced-code-blocks This crate wrappers the `pikchr.c` version downloaded from that website on the 28th February 2025. You can use it as follows: ```rust use pikchr::{Pikchr, PikchrFlags}; let piccy = Pikchr::render( diagram_str, None, PikchrFlags::default()).unwrap(); println!("{}", piccy); ``` There is a little helper program that reads a Pikchr file named on the command line and renders it as SVG to the standard output: ~~~sh cargo run -p pikchr-cli -q foo.pikchr > foo.svg ~~~ You can install it with `cargo install pikchr-cli` (`cargo install --path=pikchr-cli` from the source tree). kinnison-pikchr-4a1b52a/build.rs000066400000000000000000000001131476056221400166700ustar00rootroot00000000000000fn main() { cc::Build::new().file("src/pikchr.c").compile("pikchr"); } kinnison-pikchr-4a1b52a/pikchr-cli/000077500000000000000000000000001476056221400172555ustar00rootroot00000000000000kinnison-pikchr-4a1b52a/pikchr-cli/Cargo.toml000066400000000000000000000007571476056221400212160ustar00rootroot00000000000000[package] name = "pikchr-cli" version = "0.1.2" edition = "2021" description = "PIC-like diagramming language to SVG converter" repository = "https://github.com/kinnison/pikchr" readme = "../README.md" keywords = ["markdown", "md", "html", "svg", "pic"] license = "MIT OR Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] clap = { version = "4.3.21", features = ["derive"] } pikchr = { version = "0.1.2", path = ".." } kinnison-pikchr-4a1b52a/pikchr-cli/src/000077500000000000000000000000001476056221400200445ustar00rootroot00000000000000kinnison-pikchr-4a1b52a/pikchr-cli/src/main.rs000066400000000000000000000013241476056221400213360ustar00rootroot00000000000000use std::path::PathBuf; use clap::Parser; #[derive(Debug, Clone, Parser)] #[command(author, version, about, long_about=None)] struct Args { /// Pikchr file to convert to SVG pikchr: PathBuf, } fn main() { let args = Args::parse(); if let Err(e) = fallible_main(&args) { eprintln!("ERROR: {}", e); std::process::exit(1); } } fn fallible_main(args: &Args) -> Result<(), Box> { let markup = std::fs::read(&args.pikchr)?; let markup = String::from_utf8_lossy(&markup); let mut flags = pikchr::PikchrFlags::default(); flags.generate_plain_errors(); let image = pikchr::Pikchr::render(&markup, None, flags)?; print!("{}", image); Ok(()) } kinnison-pikchr-4a1b52a/shell.nix000066400000000000000000000001301476056221400170510ustar00rootroot00000000000000{ pkgs ? import { } }: pkgs.mkShell { buildInputs = with pkgs; [ stdenv ]; } kinnison-pikchr-4a1b52a/src/000077500000000000000000000000001476056221400160175ustar00rootroot00000000000000kinnison-pikchr-4a1b52a/src/lib.rs000066400000000000000000000305161476056221400171400ustar00rootroot00000000000000//! Pikchr image creation binding //! //! This crate provides a binding for the //! [`pikchr`](https://pikchr.org/home/doc/trunk/homepage.md) diagramming //! language. Using this crate you can convert PIC-like markup //! into SVG diagrams trivially. If you are embedding into HTML then //! you can have any errors generated as HTML, otherwise errors are //! generated as plain text. //! //! The main interface is the [`Pikchr`] struct, specifically its //! [`Pikchr::render`] function. //! //! ``` //! use pikchr::{Pikchr, PikchrFlags}; //! //! let INPUT = r#" //! arrow right 200% "Markdown" "Source" //! box rad 10px "Markdown" "Formatter" "(docs.rs/markdown)" fit //! arrow right 200% "HTML+SVG" "Output" //! arrow <-> down 70% from last box.s //! box same "Pikchr" "Formatter" "(docs.rs/pikchr)" fit //! "#; //! //! let pic = Pikchr::render(INPUT, None, PikchrFlags::default()).unwrap(); //! //! println!("{}", pic); //! ``` //! MarkdownSourceMarkdownFormatter(docs.rs/markdown)HTML+SVGOutputPikchrFormatter(docs.rs/pikchr) use libc::{c_char, c_int, c_uint, c_void, free}; use std::ffi::{CStr, CString}; use std::fmt; use std::ops::Deref; pub mod raw { use libc::{c_char, c_int, c_uint}; extern "C" { /// The main interface. Invoke this routine to translate PIKCHR source /// text into SVG. The SVG is returned in a buffer obtained from malloc(). /// The caller is responsible for freeing the buffer. /// /// If an error occurs, *pnWidth is filled with a negative number and /// the return buffer contains error message text instead of SVG. By /// default, the error message is HTML encoded. However, error messages /// come out as plaintext if the PIKCHR_PLAINTEXT_ERRORS flag is included /// as one of the bits in the mFlags parameter. /// /// - `zText`: Input PIKCHR source text. zero-terminated /// - `zClass`: Add class="%s" to markup /// - `mFlags`: Flags used to influence rendering behavior /// - `pnWidth`: OUT: Write width of here, if not NULL /// - `pnHeight`: OUT: Write height here, if not NULL #[allow(non_snake_case)] pub fn pikchr( zText: *const c_char, zClass: *const c_char, mFlags: c_uint, pnWidth: *mut c_int, pnHeight: *mut c_int, ) -> *mut c_char; } /// Include PIKCHR_PLAINTEXT_ERRORS among the bits of mFlags on the 3rd /// argument to pikchr() in order to cause error message text to come out /// as text/plain instead of as text/html pub const PIKCHR_PLAINTEXT_ERRORS: c_uint = 0x0001; /// Alter colour choices to make diagrams more suitable for rendering in /// a dark settings such as dark-mode web pages. pub const PIKCHR_DARK_MODE: c_uint = 0x0002; } /// Flags for converting pikchr source /// /// You can construct a default set of flags using the [`std::default::Default`] trait /// /// The default flags will generate plain text errors and light-mode diagrams #[derive(Copy, Clone)] pub struct PikchrFlags { plain_errors: bool, dark_mode: bool, } impl PikchrFlags { /// Return whether or not plain text errors will be generated /// /// ``` /// # use pikchr::PikchrFlags; /// let flags = PikchrFlags::default(); /// assert!(flags.plain_errors()) /// ``` pub fn plain_errors(&self) -> bool { self.plain_errors } /// Request plain text errors be generated /// /// ``` /// # use pikchr::PikchrFlags; /// let mut flags = PikchrFlags::default(); /// flags.generate_plain_errors(); /// assert!(flags.plain_errors()); /// ``` pub fn generate_plain_errors(&mut self) -> &mut PikchrFlags { self.plain_errors = true; self } /// Request help encoded errors be generated /// /// ``` /// # use pikchr::PikchrFlags; /// let mut flags = PikchrFlags::default(); /// flags.generate_html_errors(); /// assert!(!flags.plain_errors()); /// ``` pub fn generate_html_errors(&mut self) -> &mut PikchrFlags { self.plain_errors = false; self } /// Return whether or not dark mode will be used for images /// /// ``` /// # use pikchr::PikchrFlags; /// let flags = PikchrFlags::default(); /// assert!(!flags.dark_mode()); /// ``` pub fn dark_mode(&self) -> bool { self.dark_mode } /// Set the dark-mode flag /// /// ``` /// # use pikchr::PikchrFlags; /// let mut flags = PikchrFlags::default(); /// flags.use_dark_mode(); /// assert!(flags.dark_mode()); /// ``` pub fn use_dark_mode(&mut self) -> &mut PikchrFlags { self.dark_mode = true; self } /// Clear the dark-mode flag /// /// ``` /// # use pikchr::PikchrFlags; /// let mut flags = PikchrFlags::default(); /// flags.use_dark_mode(); /// flags.clear_dark_mode(); /// assert!(!flags.dark_mode()); /// ``` pub fn clear_dark_mode(&mut self) -> &mut PikchrFlags { self.dark_mode = false; self } } impl From for c_uint { fn from(val: PikchrFlags) -> c_uint { let mut ret: c_uint = 0; if val.plain_errors { ret |= raw::PIKCHR_PLAINTEXT_ERRORS; } if val.dark_mode { ret |= raw::PIKCHR_DARK_MODE; } ret } } impl std::default::Default for PikchrFlags { fn default() -> Self { Self { plain_errors: true, dark_mode: false, } } } /// A rendered pikchr diagram /// /// Pikchr renders diagrams as SVG. This SVG is a given width /// and height. The Pikchr derefs to the SVG string, or you /// can access it explicitly. The width and height are accessible /// as plain numbers. pub struct Pikchr { rendered: *const c_char, width: c_int, height: c_int, } impl Drop for Pikchr { fn drop(&mut self) { if self.rendered.is_null() { unsafe { free(self.rendered as *mut c_void); } self.rendered = std::ptr::null(); } } } impl Deref for Pikchr { type Target = str; fn deref(&self) -> &Self::Target { // We're assuming a Pikchr instance can only // be constructed from valid utf8 and thus can // only contain valid utf8 unsafe { let cstr = CStr::from_ptr(self.rendered); std::str::from_utf8_unchecked(cstr.to_bytes()) } } } impl fmt::Display for Pikchr { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt.write_str(self) } } impl Pikchr { /// Render some input pikchr source as an SVG /// /// You can convert arbitrary pikchr source into an SVG using this function. /// The class name is optional, and the flags field controls the generation /// of errors. Since pikchr does not have a structured error format, the /// returned error is simply a string. /// /// ``` /// # use pikchr::{Pikchr, PikchrFlags}; /// let image = Pikchr::render(r#" /// arrow right 200% "Markdown" "Source" /// box rad 10px "Markdown" "Formatter" "(markdown.c)" fit /// arrow right 200% "HTML+SVG" "Output" /// arrow <-> down 70% from last box.s /// box same "Pikchr" "Formatter" "(pikchr.c)" fit"#, /// None, PikchrFlags::default()) /// .unwrap(); /// assert!(image.contains(", flags: PikchrFlags) -> Result { let mut width: c_int = 0; let mut height: c_int = 0; let source = CString::new(source).map_err(|e| format!("{:?}", e))?; let class = class .map(CString::new) .transpose() .map_err(|e| format!("{:?}", e))?; let res: *mut c_char = unsafe { raw::pikchr( source.as_ptr() as *const c_char, class .map(|s| s.as_ptr() as *const c_char) .unwrap_or(std::ptr::null()), flags.into(), &mut width as *mut c_int, &mut height as *mut c_int, ) }; if width < 0 { let err = unsafe { CStr::from_ptr(res) }; let err = err.to_bytes(); let err = String::from_utf8_lossy(err).into_owned(); unsafe { free(res as *mut c_void); } Err(err) } else { Ok(Pikchr { rendered: res, width, height, }) } } /// Retrieve the width of this Pikchr /// /// ``` /// # use pikchr::{Pikchr, PikchrFlags}; /// # let pic = Pikchr::render(r#"arrow right 200% "Markdown" "Source""#, /// # None, PikchrFlags::default()).unwrap(); /// println!("Picture is {} pixels wide", pic.width()); /// ``` pub fn width(&self) -> isize { self.width as isize } /// Retrieve the height of this Pikchr /// /// ``` /// # use pikchr::{Pikchr, PikchrFlags}; /// # let pic = Pikchr::render(r#"arrow right 200% "Markdown" "Source""#, /// # None, PikchrFlags::default()).unwrap(); /// println!("Picture is {} pixels tall", pic.height()); /// ``` pub fn height(&self) -> isize { self.height as isize } /// Retrieve the rendered pikchr (same as dereferencing) /// /// ``` /// # use pikchr::{Pikchr, PikchrFlags}; /// # let pic = Pikchr::render(r#"arrow right 200% "Makdown" "Source""#, /// # None, PikchrFlags::default()).unwrap(); /// println!("Picture content:\n{}", pic.rendered()); /// ``` pub fn rendered(&self) -> &str { self } } #[cfg(test)] mod tests { use super::*; #[test] fn validate_diagram() { const SOURCE: &str = r#"arrow right 200% "Markdown" "Source""#; const OUTPUT: &str = r#" Markdown Source "#; let flags = PikchrFlags::default(); let p = Pikchr::render(SOURCE, None, flags).unwrap(); assert_eq!(OUTPUT, p.rendered()); } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������kinnison-pikchr-4a1b52a/src/pikchr.c����������������������������������������������������������������0000664�0000000�0000000�00001052032�14760562214�0017446�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* This file is automatically generated by Lemon from input grammar ** source file "pikchr.y". */ /* ** Zero-Clause BSD license: ** ** Copyright (C) 2020-09-01 by D. Richard Hipp ** ** Permission to use, copy, modify, and/or distribute this software for ** any purpose with or without fee is hereby granted. ** **************************************************************************** ** ** This software translates a PIC-inspired diagram language into SVG. ** ** PIKCHR (pronounced like "picture") is *mostly* backwards compatible ** with legacy PIC, though some features of legacy PIC are removed ** (for example, the "sh" command is removed for security) and ** many enhancements are added. ** ** PIKCHR is designed for use in an internet facing web environment. ** In particular, PIKCHR is designed to safely generate benign SVG from ** source text that provided by a hostile agent. ** ** This code was originally written by D. Richard Hipp using documentation ** from prior PIC implementations but without reference to prior code. ** All of the code in this project is original. ** ** This file implements a C-language subroutine that accepts a string ** of PIKCHR language text and generates a second string of SVG output that ** renders the drawing defined by the input. Space to hold the returned ** string is obtained from malloc() and should be freed by the caller. ** NULL might be returned if there is a memory allocation error. ** ** If there are errors in the PIKCHR input, the output will consist of an ** error message and the original PIKCHR input text (inside of
...
). ** ** The subroutine implemented by this file is intended to be stand-alone. ** It uses no external routines other than routines commonly found in ** the standard C library. ** **************************************************************************** ** COMPILING: ** ** The original source text is a mixture of C99 and "Lemon" ** (See https://sqlite.org/src/file/doc/lemon.html). Lemon is an LALR(1) ** parser generator program, similar to Yacc. The grammar of the ** input language is specified in Lemon. C-code is attached. Lemon ** runs to generate a single output file ("pikchr.c") which is then ** compiled to generate the Pikchr library. This header comment is ** preserved in the Lemon output, so you might be reading this in either ** the generated "pikchr.c" file that is output by Lemon, or in the ** "pikchr.y" source file that is input into Lemon. If you make changes, ** you should change the input source file "pikchr.y", not the ** Lemon-generated output file. ** ** Basic compilation steps: ** ** lemon pikchr.y ** cc pikchr.c -o pikchr.o ** ** Add -DPIKCHR_SHELL to add a main() routine that reads input files ** and sends them through Pikchr, for testing. Add -DPIKCHR_FUZZ for ** -fsanitizer=fuzzer testing. ** **************************************************************************** ** IMPLEMENTATION NOTES (for people who want to understand the internal ** operation of this software, perhaps to extend the code or to fix bugs): ** ** Each call to pikchr() uses a single instance of the Pik structure to ** track its internal state. The Pik structure lives for the duration ** of the pikchr() call. ** ** The input is a sequence of objects or "statements". Each statement is ** parsed into a PObj object. These are stored on an extensible array ** called PList. All parameters to each PObj are computed as the ** object is parsed. (Hence, the parameters to a PObj may only refer ** to prior statements.) Once the PObj is completely assembled, it is ** added to the end of a PList and never changes thereafter - except, ** PObj objects that are part of a "[...]" block might have their ** absolute position shifted when the outer [...] block is positioned. ** But apart from this repositioning, PObj objects are unchanged once ** they are added to the list. The order of statements on a PList does ** not change. ** ** After all input has been parsed, the top-level PList is walked to ** generate output. Sub-lists resulting from [...] blocks are scanned ** as they are encountered. All input must be collected and parsed ahead ** of output generation because the size and position of statements must be ** known in order to compute a bounding box on the output. ** ** Each PObj is on a "layer". (The common case is that all PObj's are ** on a single layer, but multiple layers are possible.) A separate pass ** is made through the list for each layer. ** ** After all output is generated, the Pik object and all the PList ** and PObj objects are deallocated and the generated output string is ** returned. Upon any error, the Pik.nErr flag is set, processing quickly ** stops, and the stack unwinds. No attempt is made to continue reading ** input after an error. ** ** Most statements begin with a class name like "box" or "arrow" or "move". ** There is a class named "text" which is used for statements that begin ** with a string literal. You can also specify the "text" class. ** A Sublist ("[...]") is a single object that contains a pointer to ** its substatements, all gathered onto a separate PList object. ** ** Variables go into PVar objects that form a linked list. ** ** Each PObj has zero or one names. Input constructs that attempt ** to assign a new name from an older name, for example: ** ** Abc: Abc + (0.5cm, 0) ** ** Statements like these generate a new "noop" object at the specified ** place and with the given name. As place-names are searched by scanning ** the list in reverse order, this has the effect of overriding the "Abc" ** name when referenced by subsequent objects. */ #include #include #include #include #include #include #define count(X) (sizeof(X)/sizeof(X[0])) #ifndef M_PI # define M_PI 3.1415926535897932385 #endif /* ** Typesafe version of ctype.h macros. Cygwin requires this, I'm told. */ #define IsUpper(X) isupper((unsigned char)(X)) #define IsLower(X) islower((unsigned char)(X)) #define ToLower(X) tolower((unsigned char)(X)) #define IsDigit(X) isdigit((unsigned char)(X)) #define IsXDigit(X) isxdigit((unsigned char)(X)) #define IsSpace(X) isspace((unsigned char)(X)) #define IsAlnum(X) isalnum((unsigned char)(X)) /* Limit the number of tokens in a single script to avoid run-away ** macro expansion attacks. See forum post ** https://pikchr.org/home/forumpost/ef8684c6955a411a */ #ifndef PIKCHR_TOKEN_LIMIT # define PIKCHR_TOKEN_LIMIT 100000 #endif /* Tag intentionally unused parameters with this macro to prevent ** compiler warnings with -Wextra */ #define UNUSED_PARAMETER(X) (void)(X) typedef struct Pik Pik; /* Complete parsing context */ typedef struct PToken PToken; /* A single token */ typedef struct PObj PObj; /* A single diagram object */ typedef struct PList PList; /* A list of diagram objects */ typedef struct PClass PClass; /* Description of statements types */ typedef double PNum; /* Numeric value */ typedef struct PRel PRel; /* Absolute or percentage value */ typedef struct PPoint PPoint; /* A position in 2-D space */ typedef struct PVar PVar; /* script-defined variable */ typedef struct PBox PBox; /* A bounding box */ typedef struct PMacro PMacro; /* A "define" macro */ /* Compass points */ #define CP_N 1 #define CP_NE 2 #define CP_E 3 #define CP_SE 4 #define CP_S 5 #define CP_SW 6 #define CP_W 7 #define CP_NW 8 #define CP_C 9 /* .center or .c */ #define CP_END 10 /* .end */ #define CP_START 11 /* .start */ /* Heading angles corresponding to compass points */ static const PNum pik_hdg_angle[] = { /* none */ 0.0, /* N */ 0.0, /* NE */ 45.0, /* E */ 90.0, /* SE */ 135.0, /* S */ 180.0, /* SW */ 225.0, /* W */ 270.0, /* NW */ 315.0, /* C */ 0.0, }; /* Built-in functions */ #define FN_ABS 0 #define FN_COS 1 #define FN_INT 2 #define FN_MAX 3 #define FN_MIN 4 #define FN_SIN 5 #define FN_SQRT 6 /* Text position and style flags. Stored in PToken.eCode so limited ** to 15 bits. */ #define TP_LJUST 0x0001 /* left justify...... */ #define TP_RJUST 0x0002 /* ...Right justify */ #define TP_JMASK 0x0003 /* Mask for justification bits */ #define TP_ABOVE2 0x0004 /* Position text way above PObj.ptAt */ #define TP_ABOVE 0x0008 /* Position text above PObj.ptAt */ #define TP_CENTER 0x0010 /* On the line */ #define TP_BELOW 0x0020 /* Position text below PObj.ptAt */ #define TP_BELOW2 0x0040 /* Position text way below PObj.ptAt */ #define TP_VMASK 0x007c /* Mask for text positioning flags */ #define TP_BIG 0x0100 /* Larger font */ #define TP_SMALL 0x0200 /* Smaller font */ #define TP_XTRA 0x0400 /* Amplify TP_BIG or TP_SMALL */ #define TP_SZMASK 0x0700 /* Font size mask */ #define TP_ITALIC 0x1000 /* Italic font */ #define TP_BOLD 0x2000 /* Bold font */ #define TP_MONO 0x4000 /* Monospace font family */ #define TP_FMASK 0x7000 /* Mask for font style */ #define TP_ALIGN 0x8000 /* Rotate to align with the line */ /* An object to hold a position in 2-D space */ struct PPoint { PNum x, y; /* X and Y coordinates */ }; static const PPoint cZeroPoint = {0.0,0.0}; /* A bounding box */ struct PBox { PPoint sw, ne; /* Lower-left and top-right corners */ }; /* An Absolute or a relative distance. The absolute distance ** is stored in rAbs and the relative distance is stored in rRel. ** Usually, one or the other will be 0.0. When using a PRel to ** update an existing value, the computation is usually something ** like this: ** ** value = PRel.rAbs + value*PRel.rRel ** */ struct PRel { PNum rAbs; /* Absolute value */ PNum rRel; /* Value relative to current value */ }; /* A variable created by the ID = EXPR construct of the PIKCHR script ** ** PIKCHR (and PIC) scripts do not use many varaibles, so it is reasonable ** to store them all on a linked list. */ struct PVar { const char *zName; /* Name of the variable */ PNum val; /* Value of the variable */ PVar *pNext; /* Next variable in a list of them all */ }; /* A single token in the parser input stream */ struct PToken { const char *z; /* Pointer to the token text */ unsigned int n; /* Length of the token in bytes */ short int eCode; /* Auxiliary code */ unsigned char eType; /* The numeric parser code */ unsigned char eEdge; /* Corner value for corner keywords */ }; /* Return negative, zero, or positive if pToken is less than, equal to ** or greater than the zero-terminated string z[] */ static int pik_token_eq(PToken *pToken, const char *z){ int c = strncmp(pToken->z,z,pToken->n); if( c==0 && z[pToken->n]!=0 ) c = -1; return c; } /* Extra token types not generated by LEMON but needed by the ** tokenizer */ #define T_PARAMETER 253 /* $1, $2, ..., $9 */ #define T_WHITESPACE 254 /* Whitespace or comments */ #define T_ERROR 255 /* Any text that is not a valid token */ /* Directions of movement */ #define DIR_RIGHT 0 #define DIR_DOWN 1 #define DIR_LEFT 2 #define DIR_UP 3 #define ValidDir(X) ((X)>=0 && (X)<=3) #define IsUpDown(X) (((X)&1)==1) #define IsLeftRight(X) (((X)&1)==0) /* Bitmask for the various attributes for PObj. These bits are ** collected in PObj.mProp and PObj.mCalc to check for constraint ** errors. */ #define A_WIDTH 0x0001 #define A_HEIGHT 0x0002 #define A_RADIUS 0x0004 #define A_THICKNESS 0x0008 #define A_DASHED 0x0010 /* Includes "dotted" */ #define A_FILL 0x0020 #define A_COLOR 0x0040 #define A_ARROW 0x0080 #define A_FROM 0x0100 #define A_CW 0x0200 #define A_AT 0x0400 #define A_TO 0x0800 /* one or more movement attributes */ #define A_FIT 0x1000 /* A single graphics object */ struct PObj { const PClass *type; /* Object type or class */ PToken errTok; /* Reference token for error messages */ PPoint ptAt; /* Reference point for the object */ PPoint ptEnter, ptExit; /* Entry and exit points */ PList *pSublist; /* Substructure for [...] objects */ char *zName; /* Name assigned to this statement */ PNum w; /* "width" property */ PNum h; /* "height" property */ PNum rad; /* "radius" property */ PNum sw; /* "thickness" property. (Mnemonic: "stroke width")*/ PNum dotted; /* "dotted" property. <=0.0 for off */ PNum dashed; /* "dashed" property. <=0.0 for off */ PNum fill; /* "fill" property. Negative for off */ PNum color; /* "color" property */ PPoint with; /* Position constraint from WITH clause */ char eWith; /* Type of heading point on WITH clause */ char cw; /* True for clockwise arc */ char larrow; /* Arrow at beginning (<- or <->) */ char rarrow; /* Arrow at end (-> or <->) */ char bClose; /* True if "close" is seen */ char bChop; /* True if "chop" is seen */ char bAltAutoFit; /* Always send both h and w into xFit() */ unsigned char nTxt; /* Number of text values */ unsigned mProp; /* Masks of properties set so far */ unsigned mCalc; /* Values computed from other constraints */ PToken aTxt[5]; /* Text with .eCode holding TP flags */ int iLayer; /* Rendering order */ int inDir, outDir; /* Entry and exit directions */ int nPath; /* Number of path points */ PPoint *aPath; /* Array of path points */ PObj *pFrom, *pTo; /* End-point objects of a path */ PBox bbox; /* Bounding box */ }; /* A list of graphics objects */ struct PList { int n; /* Number of statements in the list */ int nAlloc; /* Allocated slots in a[] */ PObj **a; /* Pointers to individual objects */ }; /* A macro definition */ struct PMacro { PMacro *pNext; /* Next in the list */ PToken macroName; /* Name of the macro */ PToken macroBody; /* Body of the macro */ int inUse; /* Do not allow recursion */ }; /* Each call to the pikchr() subroutine uses an instance of the following ** object to pass around context to all of its subroutines. */ struct Pik { unsigned nErr; /* Number of errors seen */ unsigned nToken; /* Number of tokens parsed */ PToken sIn; /* Input Pikchr-language text */ char *zOut; /* Result accumulates here */ unsigned int nOut; /* Bytes written to zOut[] so far */ unsigned int nOutAlloc; /* Space allocated to zOut[] */ unsigned char eDir; /* Current direction */ unsigned int mFlags; /* Flags passed to pikchr() */ PObj *cur; /* Object under construction */ PObj *lastRef; /* Last object references by name */ PList *list; /* Object list under construction */ PMacro *pMacros; /* List of all defined macros */ PVar *pVar; /* Application-defined variables */ PBox bbox; /* Bounding box around all statements */ /* Cache of layout values. <=0.0 for unknown... */ PNum rScale; /* Multiply to convert inches to pixels */ PNum fontScale; /* Scale fonts by this percent */ PNum charWidth; /* Character width */ PNum charHeight; /* Character height */ PNum wArrow; /* Width of arrowhead at the fat end */ PNum hArrow; /* Ht of arrowhead - dist from tip to fat end */ char bLayoutVars; /* True if cache is valid */ char thenFlag; /* True if "then" seen */ char samePath; /* aTPath copied by "same" */ const char *zClass; /* Class name for the */ int wSVG, hSVG; /* Width and height of the */ int fgcolor; /* foreground color value, or -1 for none */ int bgcolor; /* background color value, or -1 for none */ /* Paths for lines are constructed here first, then transferred into ** the PObj object at the end: */ int nTPath; /* Number of entries on aTPath[] */ int mTPath; /* For last entry, 1: x set, 2: y set */ PPoint aTPath[1000]; /* Path under construction */ /* Error contexts */ unsigned int nCtx; /* Number of error contexts */ PToken aCtx[10]; /* Nested error contexts */ }; /* Include PIKCHR_PLAINTEXT_ERRORS among the bits of mFlags on the 3rd ** argument to pikchr() in order to cause error message text to come out ** as text/plain instead of as text/html */ #define PIKCHR_PLAINTEXT_ERRORS 0x0001 /* Include PIKCHR_DARK_MODE among the mFlag bits to invert colors. */ #define PIKCHR_DARK_MODE 0x0002 /* ** The behavior of an object class is defined by an instance of ** this structure. This is the "virtual method" table. */ struct PClass { const char *zName; /* Name of class */ char isLine; /* True if a line class */ char eJust; /* Use box-style text justification */ void (*xInit)(Pik*,PObj*); /* Initializer */ void (*xNumProp)(Pik*,PObj*,PToken*); /* Value change notification */ void (*xCheck)(Pik*,PObj*); /* Checks to do after parsing */ PPoint (*xChop)(Pik*,PObj*,PPoint*); /* Chopper */ PPoint (*xOffset)(Pik*,PObj*,int); /* Offset from .c to edge point */ void (*xFit)(Pik*,PObj*,PNum w,PNum h); /* Size to fit text */ void (*xRender)(Pik*,PObj*); /* Render */ }; /* Forward declarations */ static void pik_append(Pik*, const char*,int); static void pik_append_text(Pik*,const char*,int,int); static void pik_append_num(Pik*,const char*,PNum); static void pik_append_point(Pik*,const char*,PPoint*); static void pik_append_x(Pik*,const char*,PNum,const char*); static void pik_append_y(Pik*,const char*,PNum,const char*); static void pik_append_xy(Pik*,const char*,PNum,PNum); static void pik_append_dis(Pik*,const char*,PNum,const char*); static void pik_append_arc(Pik*,PNum,PNum,PNum,PNum); static void pik_append_clr(Pik*,const char*,PNum,const char*,int); static void pik_append_style(Pik*,PObj*,int); static void pik_append_txt(Pik*,PObj*, PBox*); static void pik_draw_arrowhead(Pik*,PPoint*pFrom,PPoint*pTo,PObj*); static void pik_chop(PPoint*pFrom,PPoint*pTo,PNum); static void pik_error(Pik*,PToken*,const char*); static void pik_elist_free(Pik*,PList*); static void pik_elem_free(Pik*,PObj*); static void pik_render(Pik*,PList*); static PList *pik_elist_append(Pik*,PList*,PObj*); static PObj *pik_elem_new(Pik*,PToken*,PToken*,PList*); static void pik_set_direction(Pik*,int); static void pik_elem_setname(Pik*,PObj*,PToken*); static int pik_round(PNum); static void pik_set_var(Pik*,PToken*,PNum,PToken*); static PNum pik_value(Pik*,const char*,int,int*); static int pik_value_int(Pik*,const char*,int,int*); static PNum pik_lookup_color(Pik*,PToken*); static PNum pik_get_var(Pik*,PToken*); static PNum pik_atof(PToken*); static void pik_after_adding_attributes(Pik*,PObj*); static void pik_elem_move(PObj*,PNum dx, PNum dy); static void pik_elist_move(PList*,PNum dx, PNum dy); static void pik_set_numprop(Pik*,PToken*,PRel*); static void pik_set_clrprop(Pik*,PToken*,PNum); static void pik_set_dashed(Pik*,PToken*,PNum*); static void pik_then(Pik*,PToken*,PObj*); static void pik_add_direction(Pik*,PToken*,PRel*); static void pik_move_hdg(Pik*,PRel*,PToken*,PNum,PToken*,PToken*); static void pik_evenwith(Pik*,PToken*,PPoint*); static void pik_set_from(Pik*,PObj*,PToken*,PPoint*); static void pik_add_to(Pik*,PObj*,PToken*,PPoint*); static void pik_close_path(Pik*,PToken*); static void pik_set_at(Pik*,PToken*,PPoint*,PToken*); static short int pik_nth_value(Pik*,PToken*); static PObj *pik_find_nth(Pik*,PObj*,PToken*); static PObj *pik_find_byname(Pik*,PObj*,PToken*); static PPoint pik_place_of_elem(Pik*,PObj*,PToken*); static int pik_bbox_isempty(PBox*); static int pik_bbox_contains_point(PBox*,PPoint*); static void pik_bbox_init(PBox*); static void pik_bbox_addbox(PBox*,PBox*); static void pik_bbox_add_xy(PBox*,PNum,PNum); static void pik_bbox_addellipse(PBox*,PNum x,PNum y,PNum rx,PNum ry); static void pik_add_txt(Pik*,PToken*,int); static int pik_text_length(const PToken *pToken, const int isMonospace); static void pik_size_to_fit(Pik*,PToken*,int); static int pik_text_position(int,PToken*); static PNum pik_property_of(PObj*,PToken*); static PNum pik_func(Pik*,PToken*,PNum,PNum); static PPoint pik_position_between(PNum x, PPoint p1, PPoint p2); static PPoint pik_position_at_angle(PNum dist, PNum r, PPoint pt); static PPoint pik_position_at_hdg(PNum dist, PToken *pD, PPoint pt); static void pik_same(Pik *p, PObj*, PToken*); static PPoint pik_nth_vertex(Pik *p, PToken *pNth, PToken *pErr, PObj *pObj); static PToken pik_next_semantic_token(PToken *pThis); static void pik_compute_layout_settings(Pik*); static void pik_behind(Pik*,PObj*); static PObj *pik_assert(Pik*,PNum,PToken*,PNum); static PObj *pik_position_assert(Pik*,PPoint*,PToken*,PPoint*); static PNum pik_dist(PPoint*,PPoint*); static void pik_add_macro(Pik*,PToken *pId,PToken *pCode); #line 535 "pikchr.c" /**************** End of %include directives **********************************/ /* These constants specify the various numeric values for terminal symbols. ***************** Begin token definitions *************************************/ #ifndef T_ID #define T_ID 1 #define T_EDGEPT 2 #define T_OF 3 #define T_PLUS 4 #define T_MINUS 5 #define T_STAR 6 #define T_SLASH 7 #define T_PERCENT 8 #define T_UMINUS 9 #define T_EOL 10 #define T_ASSIGN 11 #define T_PLACENAME 12 #define T_COLON 13 #define T_ASSERT 14 #define T_LP 15 #define T_EQ 16 #define T_RP 17 #define T_DEFINE 18 #define T_CODEBLOCK 19 #define T_FILL 20 #define T_COLOR 21 #define T_THICKNESS 22 #define T_PRINT 23 #define T_STRING 24 #define T_COMMA 25 #define T_CLASSNAME 26 #define T_LB 27 #define T_RB 28 #define T_UP 29 #define T_DOWN 30 #define T_LEFT 31 #define T_RIGHT 32 #define T_CLOSE 33 #define T_CHOP 34 #define T_FROM 35 #define T_TO 36 #define T_THEN 37 #define T_HEADING 38 #define T_GO 39 #define T_AT 40 #define T_WITH 41 #define T_SAME 42 #define T_AS 43 #define T_FIT 44 #define T_BEHIND 45 #define T_UNTIL 46 #define T_EVEN 47 #define T_DOT_E 48 #define T_HEIGHT 49 #define T_WIDTH 50 #define T_RADIUS 51 #define T_DIAMETER 52 #define T_DOTTED 53 #define T_DASHED 54 #define T_CW 55 #define T_CCW 56 #define T_LARROW 57 #define T_RARROW 58 #define T_LRARROW 59 #define T_INVIS 60 #define T_THICK 61 #define T_THIN 62 #define T_SOLID 63 #define T_CENTER 64 #define T_LJUST 65 #define T_RJUST 66 #define T_ABOVE 67 #define T_BELOW 68 #define T_ITALIC 69 #define T_BOLD 70 #define T_MONO 71 #define T_ALIGNED 72 #define T_BIG 73 #define T_SMALL 74 #define T_AND 75 #define T_LT 76 #define T_GT 77 #define T_ON 78 #define T_WAY 79 #define T_BETWEEN 80 #define T_THE 81 #define T_NTH 82 #define T_VERTEX 83 #define T_TOP 84 #define T_BOTTOM 85 #define T_START 86 #define T_END 87 #define T_IN 88 #define T_THIS 89 #define T_DOT_U 90 #define T_LAST 91 #define T_NUMBER 92 #define T_FUNC1 93 #define T_FUNC2 94 #define T_DIST 95 #define T_DOT_XY 96 #define T_X 97 #define T_Y 98 #define T_DOT_L 99 #endif /**************** End token definitions ***************************************/ /* The next sections is a series of control #defines. ** various aspects of the generated parser. ** YYCODETYPE is the data type used to store the integer codes ** that represent terminal and non-terminal symbols. ** "unsigned char" is used if there are fewer than ** 256 symbols. Larger types otherwise. ** YYNOCODE is a number of type YYCODETYPE that is not used for ** any terminal or nonterminal symbol. ** YYFALLBACK If defined, this indicates that one or more tokens ** (also known as: "terminal symbols") have fall-back ** values which should be used if the original symbol ** would not parse. This permits keywords to sometimes ** be used as identifiers, for example. ** YYACTIONTYPE is the data type used for "action codes" - numbers ** that indicate what to do in response to the next ** token. ** pik_parserTOKENTYPE is the data type used for minor type for terminal ** symbols. Background: A "minor type" is a semantic ** value associated with a terminal or non-terminal ** symbols. For example, for an "ID" terminal symbol, ** the minor type might be the name of the identifier. ** Each non-terminal can have a different minor type. ** Terminal symbols all have the same minor type, though. ** This macros defines the minor type for terminal ** symbols. ** YYMINORTYPE is the data type used for all minor types. ** This is typically a union of many types, one of ** which is pik_parserTOKENTYPE. The entry in the union ** for terminal symbols is called "yy0". ** YYSTACKDEPTH is the maximum depth of the parser's stack. If ** zero the stack is dynamically sized using realloc() ** pik_parserARG_SDECL A static variable declaration for the %extra_argument ** pik_parserARG_PDECL A parameter declaration for the %extra_argument ** pik_parserARG_PARAM Code to pass %extra_argument as a subroutine parameter ** pik_parserARG_STORE Code to store %extra_argument into yypParser ** pik_parserARG_FETCH Code to extract %extra_argument from yypParser ** pik_parserCTX_* As pik_parserARG_ except for %extra_context ** YYREALLOC Name of the realloc() function to use ** YYFREE Name of the free() function to use ** YYDYNSTACK True if stack space should be extended on heap ** YYERRORSYMBOL is the code number of the error symbol. If not ** defined, then do no error processing. ** YYNSTATE the combined number of states. ** YYNRULE the number of rules in the grammar ** YYNTOKEN Number of terminal symbols ** YY_MAX_SHIFT Maximum value for shift actions ** YY_MIN_SHIFTREDUCE Minimum value for shift-reduce actions ** YY_MAX_SHIFTREDUCE Maximum value for shift-reduce actions ** YY_ERROR_ACTION The yy_action[] code for syntax error ** YY_ACCEPT_ACTION The yy_action[] code for accept ** YY_NO_ACTION The yy_action[] code for no-op ** YY_MIN_REDUCE Minimum value for reduce actions ** YY_MAX_REDUCE Maximum value for reduce actions ** YY_MIN_DSTRCTR Minimum symbol value that has a destructor ** YY_MAX_DSTRCTR Maximum symbol value that has a destructor */ #ifndef INTERFACE # define INTERFACE 1 #endif /************* Begin control #defines *****************************************/ #define YYCODETYPE unsigned char #define YYNOCODE 136 #define YYACTIONTYPE unsigned short int #define pik_parserTOKENTYPE PToken typedef union { int yyinit; pik_parserTOKENTYPE yy0; PNum yy21; PPoint yy63; PRel yy72; PObj* yy162; short int yy188; PList* yy235; } YYMINORTYPE; #ifndef YYSTACKDEPTH #define YYSTACKDEPTH 100 #endif #define pik_parserARG_SDECL #define pik_parserARG_PDECL #define pik_parserARG_PARAM #define pik_parserARG_FETCH #define pik_parserARG_STORE #define YYREALLOC realloc #define YYFREE free #define YYDYNSTACK 0 #define pik_parserCTX_SDECL Pik *p; #define pik_parserCTX_PDECL ,Pik *p #define pik_parserCTX_PARAM ,p #define pik_parserCTX_FETCH Pik *p=yypParser->p; #define pik_parserCTX_STORE yypParser->p=p; #define YYFALLBACK 1 #define YYNSTATE 164 #define YYNRULE 156 #define YYNRULE_WITH_ACTION 116 #define YYNTOKEN 100 #define YY_MAX_SHIFT 163 #define YY_MIN_SHIFTREDUCE 287 #define YY_MAX_SHIFTREDUCE 442 #define YY_ERROR_ACTION 443 #define YY_ACCEPT_ACTION 444 #define YY_NO_ACTION 445 #define YY_MIN_REDUCE 446 #define YY_MAX_REDUCE 601 #define YY_MIN_DSTRCTR 100 #define YY_MAX_DSTRCTR 103 /************* End control #defines *******************************************/ #define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0]))) /* Define the yytestcase() macro to be a no-op if is not already defined ** otherwise. ** ** Applications can choose to define yytestcase() in the %include section ** to a macro that can assist in verifying code coverage. For production ** code the yytestcase() macro should be turned off. But it is useful ** for testing. */ #ifndef yytestcase # define yytestcase(X) #endif /* Macro to determine if stack space has the ability to grow using ** heap memory. */ #if YYSTACKDEPTH<=0 || YYDYNSTACK # define YYGROWABLESTACK 1 #else # define YYGROWABLESTACK 0 #endif /* Guarantee a minimum number of initial stack slots. */ #if YYSTACKDEPTH<=0 # undef YYSTACKDEPTH # define YYSTACKDEPTH 2 /* Need a minimum stack size */ #endif /* Next are the tables used to determine what action to take based on the ** current state and lookahead token. These tables are used to implement ** functions that take a state number and lookahead value and return an ** action integer. ** ** Suppose the action integer is N. Then the action is determined as ** follows ** ** 0 <= N <= YY_MAX_SHIFT Shift N. That is, push the lookahead ** token onto the stack and goto state N. ** ** N between YY_MIN_SHIFTREDUCE Shift to an arbitrary state then ** and YY_MAX_SHIFTREDUCE reduce by rule N-YY_MIN_SHIFTREDUCE. ** ** N == YY_ERROR_ACTION A syntax error has occurred. ** ** N == YY_ACCEPT_ACTION The parser accepts its input. ** ** N == YY_NO_ACTION No such action. Denotes unused ** slots in the yy_action[] table. ** ** N between YY_MIN_REDUCE Reduce by rule N-YY_MIN_REDUCE ** and YY_MAX_REDUCE ** ** The action table is constructed as a single large table named yy_action[]. ** Given state S and lookahead X, the action is computed as either: ** ** (A) N = yy_action[ yy_shift_ofst[S] + X ] ** (B) N = yy_default[S] ** ** The (A) formula is preferred. The B formula is used instead if ** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X. ** ** The formulas above are for computing the action when the lookahead is ** a terminal symbol. If the lookahead is a non-terminal (as occurs after ** a reduce action) then the yy_reduce_ofst[] array is used in place of ** the yy_shift_ofst[] array. ** ** The following are the tables generated in this section: ** ** yy_action[] A single table containing all actions. ** yy_lookahead[] A table containing the lookahead for each entry in ** yy_action. Used to detect hash collisions. ** yy_shift_ofst[] For each state, the offset into yy_action for ** shifting terminals. ** yy_reduce_ofst[] For each state, the offset into yy_action for ** shifting non-terminals after a reduce. ** yy_default[] Default action for each state. ** *********** Begin parsing tables **********************************************/ #define YY_ACTTAB_COUNT (1313) static const YYACTIONTYPE yy_action[] = { /* 0 */ 575, 495, 161, 119, 25, 452, 29, 74, 129, 148, /* 10 */ 575, 492, 161, 119, 453, 113, 120, 161, 119, 530, /* 20 */ 427, 428, 339, 559, 81, 30, 560, 561, 575, 64, /* 30 */ 63, 62, 61, 322, 323, 9, 8, 33, 149, 32, /* 40 */ 7, 71, 127, 38, 335, 66, 48, 37, 28, 339, /* 50 */ 339, 339, 339, 425, 426, 340, 341, 342, 343, 344, /* 60 */ 345, 346, 347, 348, 474, 528, 161, 119, 577, 77, /* 70 */ 577, 73, 306, 148, 474, 533, 161, 119, 112, 113, /* 80 */ 120, 161, 119, 128, 427, 428, 339, 31, 81, 531, /* 90 */ 161, 119, 474, 35, 330, 378, 158, 322, 323, 9, /* 100 */ 8, 33, 149, 32, 7, 71, 127, 328, 335, 66, /* 110 */ 579, 378, 158, 339, 339, 339, 339, 425, 426, 340, /* 120 */ 341, 342, 343, 344, 345, 346, 347, 348, 394, 435, /* 130 */ 46, 59, 60, 64, 63, 62, 61, 357, 36, 376, /* 140 */ 54, 51, 2, 47, 403, 13, 297, 411, 412, 413, /* 150 */ 414, 80, 162, 308, 79, 133, 310, 126, 441, 440, /* 160 */ 118, 123, 83, 404, 405, 406, 408, 80, 84, 308, /* 170 */ 79, 299, 411, 412, 413, 414, 118, 69, 350, 350, /* 180 */ 350, 350, 350, 350, 350, 350, 350, 350, 350, 62, /* 190 */ 61, 434, 64, 63, 62, 61, 313, 398, 399, 427, /* 200 */ 428, 339, 380, 157, 64, 63, 62, 61, 122, 106, /* 210 */ 535, 436, 437, 438, 439, 298, 375, 391, 117, 393, /* 220 */ 155, 154, 153, 394, 435, 49, 59, 60, 339, 339, /* 230 */ 339, 339, 425, 426, 376, 3, 4, 2, 64, 63, /* 240 */ 62, 61, 156, 156, 156, 394, 379, 159, 59, 60, /* 250 */ 76, 67, 535, 441, 440, 5, 102, 6, 535, 42, /* 260 */ 131, 535, 69, 107, 301, 302, 303, 394, 305, 15, /* 270 */ 59, 60, 120, 161, 119, 446, 463, 424, 376, 423, /* 280 */ 1, 42, 397, 78, 78, 36, 434, 11, 394, 435, /* 290 */ 356, 59, 60, 12, 152, 139, 432, 14, 16, 376, /* 300 */ 18, 65, 2, 138, 106, 430, 436, 437, 438, 439, /* 310 */ 44, 375, 19, 117, 393, 155, 154, 153, 441, 440, /* 320 */ 142, 140, 64, 63, 62, 61, 106, 20, 68, 376, /* 330 */ 359, 107, 23, 375, 45, 117, 393, 155, 154, 153, /* 340 */ 120, 161, 119, 55, 463, 114, 26, 57, 106, 147, /* 350 */ 146, 434, 569, 58, 392, 375, 43, 117, 393, 155, /* 360 */ 154, 153, 152, 384, 64, 63, 62, 61, 382, 106, /* 370 */ 383, 436, 437, 438, 439, 377, 375, 70, 117, 393, /* 380 */ 155, 154, 153, 160, 39, 22, 21, 445, 142, 140, /* 390 */ 64, 63, 62, 61, 24, 17, 145, 141, 431, 108, /* 400 */ 445, 445, 445, 391, 445, 445, 375, 445, 117, 445, /* 410 */ 445, 55, 74, 445, 148, 445, 445, 147, 146, 124, /* 420 */ 113, 120, 161, 119, 43, 445, 445, 142, 140, 64, /* 430 */ 63, 62, 61, 445, 394, 445, 445, 59, 60, 64, /* 440 */ 63, 62, 61, 149, 445, 376, 445, 445, 42, 445, /* 450 */ 55, 445, 391, 22, 21, 445, 147, 146, 445, 445, /* 460 */ 52, 445, 24, 43, 145, 141, 431, 394, 445, 445, /* 470 */ 59, 60, 64, 63, 62, 61, 445, 445, 376, 132, /* 480 */ 130, 42, 445, 445, 445, 355, 156, 156, 156, 445, /* 490 */ 445, 445, 22, 21, 445, 394, 473, 445, 59, 60, /* 500 */ 445, 24, 445, 145, 141, 431, 376, 445, 107, 42, /* 510 */ 64, 63, 62, 61, 445, 106, 445, 120, 161, 119, /* 520 */ 445, 478, 375, 354, 117, 393, 155, 154, 153, 445, /* 530 */ 394, 143, 473, 59, 60, 64, 63, 62, 61, 152, /* 540 */ 445, 376, 445, 445, 42, 445, 445, 445, 106, 64, /* 550 */ 63, 62, 61, 445, 445, 375, 50, 117, 393, 155, /* 560 */ 154, 153, 445, 394, 144, 445, 59, 60, 445, 445, /* 570 */ 53, 72, 445, 148, 376, 445, 106, 42, 125, 113, /* 580 */ 120, 161, 119, 375, 445, 117, 393, 155, 154, 153, /* 590 */ 394, 445, 445, 59, 60, 445, 445, 445, 445, 445, /* 600 */ 445, 102, 149, 445, 42, 445, 74, 445, 148, 445, /* 610 */ 445, 106, 445, 497, 113, 120, 161, 119, 375, 445, /* 620 */ 117, 393, 155, 154, 153, 394, 445, 445, 59, 60, /* 630 */ 445, 445, 88, 445, 445, 445, 376, 149, 445, 40, /* 640 */ 445, 120, 161, 119, 106, 445, 445, 435, 110, 110, /* 650 */ 445, 375, 445, 117, 393, 155, 154, 153, 394, 445, /* 660 */ 445, 59, 60, 152, 85, 445, 445, 445, 445, 376, /* 670 */ 445, 106, 41, 120, 161, 119, 441, 440, 375, 445, /* 680 */ 117, 393, 155, 154, 153, 448, 454, 29, 445, 445, /* 690 */ 74, 450, 148, 75, 88, 152, 445, 496, 113, 120, /* 700 */ 161, 119, 163, 120, 161, 119, 106, 27, 445, 434, /* 710 */ 111, 111, 445, 375, 445, 117, 393, 155, 154, 153, /* 720 */ 445, 149, 445, 445, 445, 152, 74, 445, 148, 436, /* 730 */ 437, 438, 439, 490, 113, 120, 161, 119, 445, 106, /* 740 */ 121, 447, 454, 29, 445, 445, 375, 450, 117, 393, /* 750 */ 155, 154, 153, 445, 445, 445, 445, 149, 163, 74, /* 760 */ 445, 148, 444, 27, 445, 445, 484, 113, 120, 161, /* 770 */ 119, 445, 445, 445, 74, 445, 148, 445, 445, 445, /* 780 */ 445, 483, 113, 120, 161, 119, 74, 445, 148, 86, /* 790 */ 149, 445, 445, 480, 113, 120, 161, 119, 120, 161, /* 800 */ 119, 445, 74, 445, 148, 149, 445, 445, 445, 134, /* 810 */ 113, 120, 161, 119, 74, 445, 148, 149, 445, 445, /* 820 */ 152, 517, 113, 120, 161, 119, 88, 64, 63, 62, /* 830 */ 61, 445, 445, 149, 445, 120, 161, 119, 445, 74, /* 840 */ 396, 148, 475, 445, 445, 149, 137, 113, 120, 161, /* 850 */ 119, 74, 445, 148, 445, 445, 445, 152, 525, 113, /* 860 */ 120, 161, 119, 445, 74, 445, 148, 445, 445, 445, /* 870 */ 149, 527, 113, 120, 161, 119, 445, 445, 445, 74, /* 880 */ 445, 148, 149, 445, 445, 445, 524, 113, 120, 161, /* 890 */ 119, 74, 445, 148, 98, 149, 445, 445, 526, 113, /* 900 */ 120, 161, 119, 120, 161, 119, 445, 74, 445, 148, /* 910 */ 149, 445, 445, 445, 523, 113, 120, 161, 119, 74, /* 920 */ 445, 148, 149, 445, 445, 152, 522, 113, 120, 161, /* 930 */ 119, 89, 64, 63, 62, 61, 445, 445, 149, 445, /* 940 */ 120, 161, 119, 445, 74, 395, 148, 445, 445, 445, /* 950 */ 149, 521, 113, 120, 161, 119, 74, 445, 148, 445, /* 960 */ 445, 445, 152, 520, 113, 120, 161, 119, 445, 74, /* 970 */ 445, 148, 445, 445, 445, 149, 519, 113, 120, 161, /* 980 */ 119, 445, 445, 445, 74, 445, 148, 149, 445, 445, /* 990 */ 445, 150, 113, 120, 161, 119, 74, 445, 148, 90, /* 1000 */ 149, 445, 445, 151, 113, 120, 161, 119, 120, 161, /* 1010 */ 119, 445, 74, 445, 148, 149, 445, 435, 445, 136, /* 1020 */ 113, 120, 161, 119, 74, 445, 148, 149, 445, 445, /* 1030 */ 152, 135, 113, 120, 161, 119, 64, 63, 62, 61, /* 1040 */ 445, 445, 445, 149, 445, 445, 441, 440, 445, 88, /* 1050 */ 445, 445, 445, 445, 445, 149, 445, 56, 120, 161, /* 1060 */ 119, 88, 445, 445, 10, 479, 479, 445, 445, 445, /* 1070 */ 120, 161, 119, 445, 445, 445, 445, 82, 445, 434, /* 1080 */ 152, 445, 445, 445, 466, 445, 34, 109, 447, 454, /* 1090 */ 29, 445, 152, 445, 450, 445, 445, 445, 107, 436, /* 1100 */ 437, 438, 439, 87, 445, 163, 445, 120, 161, 119, /* 1110 */ 27, 451, 120, 161, 119, 99, 445, 64, 63, 62, /* 1120 */ 61, 445, 100, 445, 120, 161, 119, 101, 445, 152, /* 1130 */ 391, 120, 161, 119, 152, 445, 120, 161, 119, 91, /* 1140 */ 445, 445, 445, 445, 445, 445, 152, 445, 120, 161, /* 1150 */ 119, 103, 445, 152, 92, 445, 445, 445, 152, 445, /* 1160 */ 120, 161, 119, 120, 161, 119, 93, 445, 445, 104, /* 1170 */ 152, 445, 445, 445, 445, 120, 161, 119, 120, 161, /* 1180 */ 119, 445, 152, 445, 94, 152, 445, 445, 445, 445, /* 1190 */ 445, 445, 105, 120, 161, 119, 445, 152, 445, 95, /* 1200 */ 152, 120, 161, 119, 445, 445, 445, 96, 120, 161, /* 1210 */ 119, 445, 445, 445, 445, 152, 120, 161, 119, 445, /* 1220 */ 445, 445, 445, 152, 445, 445, 445, 445, 445, 445, /* 1230 */ 152, 97, 445, 445, 549, 445, 445, 548, 152, 445, /* 1240 */ 120, 161, 119, 120, 161, 119, 120, 161, 119, 445, /* 1250 */ 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, /* 1260 */ 445, 445, 152, 547, 445, 152, 546, 445, 152, 115, /* 1270 */ 445, 445, 120, 161, 119, 120, 161, 119, 120, 161, /* 1280 */ 119, 116, 445, 445, 445, 445, 445, 445, 445, 445, /* 1290 */ 120, 161, 119, 445, 152, 445, 445, 152, 445, 445, /* 1300 */ 152, 445, 445, 445, 445, 445, 445, 445, 445, 445, /* 1310 */ 445, 445, 152, }; static const YYCODETYPE yy_lookahead[] = { /* 0 */ 0, 113, 114, 115, 134, 102, 103, 104, 106, 106, /* 10 */ 10, 113, 114, 115, 111, 112, 113, 114, 115, 106, /* 20 */ 20, 21, 22, 105, 24, 126, 108, 109, 28, 4, /* 30 */ 5, 6, 7, 33, 34, 35, 36, 37, 135, 39, /* 40 */ 40, 41, 42, 105, 44, 45, 108, 109, 107, 49, /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, /* 60 */ 60, 61, 62, 63, 0, 113, 114, 115, 130, 131, /* 70 */ 132, 104, 25, 106, 10, 113, 114, 115, 111, 112, /* 80 */ 113, 114, 115, 106, 20, 21, 22, 128, 24, 113, /* 90 */ 114, 115, 28, 129, 2, 26, 27, 33, 34, 35, /* 100 */ 36, 37, 135, 39, 40, 41, 42, 2, 44, 45, /* 110 */ 133, 26, 27, 49, 50, 51, 52, 53, 54, 55, /* 120 */ 56, 57, 58, 59, 60, 61, 62, 63, 1, 2, /* 130 */ 38, 4, 5, 4, 5, 6, 7, 17, 10, 12, /* 140 */ 4, 5, 15, 38, 1, 25, 17, 29, 30, 31, /* 150 */ 32, 24, 83, 26, 27, 12, 28, 14, 31, 32, /* 160 */ 91, 18, 116, 20, 21, 22, 23, 24, 116, 26, /* 170 */ 27, 19, 29, 30, 31, 32, 91, 3, 64, 65, /* 180 */ 66, 67, 68, 69, 70, 71, 72, 73, 74, 6, /* 190 */ 7, 64, 4, 5, 6, 7, 8, 97, 98, 20, /* 200 */ 21, 22, 26, 27, 4, 5, 6, 7, 1, 82, /* 210 */ 48, 84, 85, 86, 87, 17, 89, 17, 91, 92, /* 220 */ 93, 94, 95, 1, 2, 25, 4, 5, 49, 50, /* 230 */ 51, 52, 53, 54, 12, 16, 15, 15, 4, 5, /* 240 */ 6, 7, 20, 21, 22, 1, 26, 27, 4, 5, /* 250 */ 48, 43, 90, 31, 32, 40, 12, 40, 96, 15, /* 260 */ 47, 99, 88, 104, 20, 21, 22, 1, 24, 35, /* 270 */ 4, 5, 113, 114, 115, 0, 117, 41, 12, 41, /* 280 */ 13, 15, 17, 124, 125, 10, 64, 25, 1, 2, /* 290 */ 17, 4, 5, 75, 135, 81, 80, 3, 3, 12, /* 300 */ 3, 99, 15, 79, 82, 80, 84, 85, 86, 87, /* 310 */ 38, 89, 3, 91, 92, 93, 94, 95, 31, 32, /* 320 */ 2, 3, 4, 5, 6, 7, 82, 3, 3, 12, /* 330 */ 77, 104, 25, 89, 16, 91, 92, 93, 94, 95, /* 340 */ 113, 114, 115, 25, 117, 96, 15, 15, 82, 31, /* 350 */ 32, 64, 125, 15, 17, 89, 38, 91, 92, 93, /* 360 */ 94, 95, 135, 28, 4, 5, 6, 7, 28, 82, /* 370 */ 28, 84, 85, 86, 87, 12, 89, 3, 91, 92, /* 380 */ 93, 94, 95, 90, 11, 67, 68, 136, 2, 3, /* 390 */ 4, 5, 6, 7, 76, 35, 78, 79, 80, 82, /* 400 */ 136, 136, 136, 17, 136, 136, 89, 136, 91, 136, /* 410 */ 136, 25, 104, 136, 106, 136, 136, 31, 32, 111, /* 420 */ 112, 113, 114, 115, 38, 136, 136, 2, 3, 4, /* 430 */ 5, 6, 7, 136, 1, 136, 136, 4, 5, 4, /* 440 */ 5, 6, 7, 135, 136, 12, 136, 136, 15, 136, /* 450 */ 25, 136, 17, 67, 68, 136, 31, 32, 136, 136, /* 460 */ 25, 136, 76, 38, 78, 79, 80, 1, 136, 136, /* 470 */ 4, 5, 4, 5, 6, 7, 136, 136, 12, 46, /* 480 */ 47, 15, 136, 136, 136, 17, 20, 21, 22, 136, /* 490 */ 136, 136, 67, 68, 136, 1, 2, 136, 4, 5, /* 500 */ 136, 76, 136, 78, 79, 80, 12, 136, 104, 15, /* 510 */ 4, 5, 6, 7, 136, 82, 136, 113, 114, 115, /* 520 */ 136, 117, 89, 17, 91, 92, 93, 94, 95, 136, /* 530 */ 1, 2, 38, 4, 5, 4, 5, 6, 7, 135, /* 540 */ 136, 12, 136, 136, 15, 136, 136, 136, 82, 4, /* 550 */ 5, 6, 7, 136, 136, 89, 25, 91, 92, 93, /* 560 */ 94, 95, 136, 1, 2, 136, 4, 5, 136, 136, /* 570 */ 25, 104, 136, 106, 12, 136, 82, 15, 111, 112, /* 580 */ 113, 114, 115, 89, 136, 91, 92, 93, 94, 95, /* 590 */ 1, 136, 136, 4, 5, 136, 136, 136, 136, 136, /* 600 */ 136, 12, 135, 136, 15, 136, 104, 136, 106, 136, /* 610 */ 136, 82, 136, 111, 112, 113, 114, 115, 89, 136, /* 620 */ 91, 92, 93, 94, 95, 1, 136, 136, 4, 5, /* 630 */ 136, 136, 104, 136, 136, 136, 12, 135, 136, 15, /* 640 */ 136, 113, 114, 115, 82, 136, 136, 2, 120, 121, /* 650 */ 136, 89, 136, 91, 92, 93, 94, 95, 1, 136, /* 660 */ 136, 4, 5, 135, 104, 136, 136, 136, 136, 12, /* 670 */ 136, 82, 15, 113, 114, 115, 31, 32, 89, 136, /* 680 */ 91, 92, 93, 94, 95, 101, 102, 103, 136, 136, /* 690 */ 104, 107, 106, 48, 104, 135, 136, 111, 112, 113, /* 700 */ 114, 115, 118, 113, 114, 115, 82, 123, 136, 64, /* 710 */ 120, 121, 136, 89, 136, 91, 92, 93, 94, 95, /* 720 */ 136, 135, 136, 136, 136, 135, 104, 136, 106, 84, /* 730 */ 85, 86, 87, 111, 112, 113, 114, 115, 136, 82, /* 740 */ 100, 101, 102, 103, 136, 136, 89, 107, 91, 92, /* 750 */ 93, 94, 95, 136, 136, 136, 136, 135, 118, 104, /* 760 */ 136, 106, 122, 123, 136, 136, 111, 112, 113, 114, /* 770 */ 115, 136, 136, 136, 104, 136, 106, 136, 136, 136, /* 780 */ 136, 111, 112, 113, 114, 115, 104, 136, 106, 104, /* 790 */ 135, 136, 136, 111, 112, 113, 114, 115, 113, 114, /* 800 */ 115, 136, 104, 136, 106, 135, 136, 136, 136, 111, /* 810 */ 112, 113, 114, 115, 104, 136, 106, 135, 136, 136, /* 820 */ 135, 111, 112, 113, 114, 115, 104, 4, 5, 6, /* 830 */ 7, 136, 136, 135, 136, 113, 114, 115, 136, 104, /* 840 */ 17, 106, 120, 136, 136, 135, 111, 112, 113, 114, /* 850 */ 115, 104, 136, 106, 136, 136, 136, 135, 111, 112, /* 860 */ 113, 114, 115, 136, 104, 136, 106, 136, 136, 136, /* 870 */ 135, 111, 112, 113, 114, 115, 136, 136, 136, 104, /* 880 */ 136, 106, 135, 136, 136, 136, 111, 112, 113, 114, /* 890 */ 115, 104, 136, 106, 104, 135, 136, 136, 111, 112, /* 900 */ 113, 114, 115, 113, 114, 115, 136, 104, 136, 106, /* 910 */ 135, 136, 136, 136, 111, 112, 113, 114, 115, 104, /* 920 */ 136, 106, 135, 136, 136, 135, 111, 112, 113, 114, /* 930 */ 115, 104, 4, 5, 6, 7, 136, 136, 135, 136, /* 940 */ 113, 114, 115, 136, 104, 17, 106, 136, 136, 136, /* 950 */ 135, 111, 112, 113, 114, 115, 104, 136, 106, 136, /* 960 */ 136, 136, 135, 111, 112, 113, 114, 115, 136, 104, /* 970 */ 136, 106, 136, 136, 136, 135, 111, 112, 113, 114, /* 980 */ 115, 136, 136, 136, 104, 136, 106, 135, 136, 136, /* 990 */ 136, 111, 112, 113, 114, 115, 104, 136, 106, 104, /* 1000 */ 135, 136, 136, 111, 112, 113, 114, 115, 113, 114, /* 1010 */ 115, 136, 104, 136, 106, 135, 136, 2, 136, 111, /* 1020 */ 112, 113, 114, 115, 104, 136, 106, 135, 136, 136, /* 1030 */ 135, 111, 112, 113, 114, 115, 4, 5, 6, 7, /* 1040 */ 136, 136, 136, 135, 136, 136, 31, 32, 136, 104, /* 1050 */ 136, 136, 136, 136, 136, 135, 136, 25, 113, 114, /* 1060 */ 115, 104, 136, 136, 119, 120, 121, 136, 136, 136, /* 1070 */ 113, 114, 115, 136, 136, 136, 136, 120, 136, 64, /* 1080 */ 135, 136, 136, 136, 127, 136, 129, 100, 101, 102, /* 1090 */ 103, 136, 135, 136, 107, 136, 136, 136, 104, 84, /* 1100 */ 85, 86, 87, 104, 136, 118, 136, 113, 114, 115, /* 1110 */ 123, 117, 113, 114, 115, 104, 136, 4, 5, 6, /* 1120 */ 7, 136, 104, 136, 113, 114, 115, 104, 136, 135, /* 1130 */ 17, 113, 114, 115, 135, 136, 113, 114, 115, 104, /* 1140 */ 136, 136, 136, 136, 136, 136, 135, 136, 113, 114, /* 1150 */ 115, 104, 136, 135, 104, 136, 136, 136, 135, 136, /* 1160 */ 113, 114, 115, 113, 114, 115, 104, 136, 136, 104, /* 1170 */ 135, 136, 136, 136, 136, 113, 114, 115, 113, 114, /* 1180 */ 115, 136, 135, 136, 104, 135, 136, 136, 136, 136, /* 1190 */ 136, 136, 104, 113, 114, 115, 136, 135, 136, 104, /* 1200 */ 135, 113, 114, 115, 136, 136, 136, 104, 113, 114, /* 1210 */ 115, 136, 136, 136, 136, 135, 113, 114, 115, 136, /* 1220 */ 136, 136, 136, 135, 136, 136, 136, 136, 136, 136, /* 1230 */ 135, 104, 136, 136, 104, 136, 136, 104, 135, 136, /* 1240 */ 113, 114, 115, 113, 114, 115, 113, 114, 115, 136, /* 1250 */ 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, /* 1260 */ 136, 136, 135, 104, 136, 135, 104, 136, 135, 104, /* 1270 */ 136, 136, 113, 114, 115, 113, 114, 115, 113, 114, /* 1280 */ 115, 104, 136, 136, 136, 136, 136, 136, 136, 136, /* 1290 */ 113, 114, 115, 136, 135, 136, 136, 135, 136, 136, /* 1300 */ 135, 136, 136, 136, 136, 136, 136, 136, 136, 136, /* 1310 */ 136, 136, 135, 100, 100, 100, 100, 100, 100, 100, /* 1320 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, /* 1330 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, /* 1340 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, /* 1350 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, /* 1360 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, /* 1370 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, /* 1380 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, /* 1390 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, /* 1400 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, /* 1410 */ 100, 100, 100, }; #define YY_SHIFT_COUNT (163) #define YY_SHIFT_MIN (0) #define YY_SHIFT_MAX (1113) static const unsigned short int yy_shift_ofst[] = { /* 0 */ 143, 127, 222, 287, 287, 287, 287, 287, 287, 287, /* 10 */ 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, /* 20 */ 287, 287, 287, 287, 287, 287, 287, 244, 433, 266, /* 30 */ 244, 143, 494, 494, 0, 64, 143, 589, 266, 589, /* 40 */ 466, 466, 466, 529, 562, 266, 266, 266, 266, 266, /* 50 */ 266, 624, 266, 266, 657, 266, 266, 266, 266, 266, /* 60 */ 266, 266, 266, 266, 266, 179, 317, 317, 317, 317, /* 70 */ 317, 645, 318, 386, 425, 1015, 1015, 118, 47, 1313, /* 80 */ 1313, 1313, 1313, 114, 114, 200, 435, 129, 188, 234, /* 90 */ 360, 468, 531, 506, 545, 823, 1032, 928, 1113, 25, /* 100 */ 25, 25, 162, 25, 25, 25, 69, 25, 85, 128, /* 110 */ 92, 105, 120, 136, 100, 183, 183, 176, 220, 174, /* 120 */ 202, 275, 152, 207, 198, 219, 221, 208, 215, 217, /* 130 */ 236, 238, 213, 267, 265, 262, 218, 273, 216, 224, /* 140 */ 214, 225, 294, 295, 297, 272, 309, 324, 325, 249, /* 150 */ 253, 307, 249, 331, 332, 338, 337, 335, 340, 342, /* 160 */ 363, 293, 374, 373, }; #define YY_REDUCE_COUNT (82) #define YY_REDUCE_MIN (-130) #define YY_REDUCE_MAX (1177) static const short yy_reduce_ofst[] = { /* 0 */ 640, -97, -33, 308, 467, 502, 586, 622, 655, 670, /* 10 */ 682, 698, 710, 735, 747, 760, 775, 787, 803, 815, /* 20 */ 840, 852, 865, 880, 892, 908, 920, 159, 945, 957, /* 30 */ 227, 987, 528, 590, -62, -62, 584, 404, 722, 994, /* 40 */ 560, 685, 790, 827, 895, 999, 1011, 1018, 1023, 1035, /* 50 */ 1047, 1050, 1062, 1065, 1080, 1088, 1095, 1103, 1127, 1130, /* 60 */ 1133, 1159, 1162, 1165, 1177, -82, -112, -102, -48, -38, /* 70 */ -24, -23, -130, -130, -130, -98, -87, -59, -101, -41, /* 80 */ 46, 52, -36, }; static const YYACTIONTYPE yy_default[] = { /* 0 */ 449, 443, 443, 443, 443, 443, 443, 443, 443, 443, /* 10 */ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, /* 20 */ 443, 443, 443, 443, 443, 443, 443, 443, 473, 576, /* 30 */ 443, 449, 580, 485, 581, 581, 449, 443, 443, 443, /* 40 */ 443, 443, 443, 443, 443, 443, 443, 443, 477, 443, /* 50 */ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, /* 60 */ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, /* 70 */ 443, 443, 443, 443, 443, 443, 443, 443, 455, 470, /* 80 */ 508, 508, 576, 468, 493, 443, 443, 443, 471, 443, /* 90 */ 443, 443, 443, 443, 443, 443, 443, 443, 443, 488, /* 100 */ 486, 476, 459, 512, 511, 510, 443, 566, 443, 443, /* 110 */ 443, 443, 443, 588, 443, 545, 544, 540, 443, 532, /* 120 */ 529, 443, 443, 443, 443, 443, 443, 491, 443, 443, /* 130 */ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, /* 140 */ 443, 443, 443, 443, 443, 443, 443, 443, 443, 592, /* 150 */ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, /* 160 */ 443, 601, 443, 443, }; /********** End of lemon-generated parsing tables *****************************/ /* The next table maps tokens (terminal symbols) into fallback tokens. ** If a construct like the following: ** ** %fallback ID X Y Z. ** ** appears in the grammar, then ID becomes a fallback token for X, Y, ** and Z. Whenever one of the tokens X, Y, or Z is input to the parser ** but it does not parse, the type of the token is changed to ID and ** the parse is retried before an error is thrown. ** ** This feature can be used, for example, to cause some keywords in a language ** to revert to identifiers if they keyword does not apply in the context where ** it appears. */ #ifdef YYFALLBACK static const YYCODETYPE yyFallback[] = { 0, /* $ => nothing */ 0, /* ID => nothing */ 1, /* EDGEPT => ID */ 0, /* OF => nothing */ 0, /* PLUS => nothing */ 0, /* MINUS => nothing */ 0, /* STAR => nothing */ 0, /* SLASH => nothing */ 0, /* PERCENT => nothing */ 0, /* UMINUS => nothing */ 0, /* EOL => nothing */ 0, /* ASSIGN => nothing */ 0, /* PLACENAME => nothing */ 0, /* COLON => nothing */ 0, /* ASSERT => nothing */ 0, /* LP => nothing */ 0, /* EQ => nothing */ 0, /* RP => nothing */ 0, /* DEFINE => nothing */ 0, /* CODEBLOCK => nothing */ 0, /* FILL => nothing */ 0, /* COLOR => nothing */ 0, /* THICKNESS => nothing */ 0, /* PRINT => nothing */ 0, /* STRING => nothing */ 0, /* COMMA => nothing */ 0, /* CLASSNAME => nothing */ 0, /* LB => nothing */ 0, /* RB => nothing */ 0, /* UP => nothing */ 0, /* DOWN => nothing */ 0, /* LEFT => nothing */ 0, /* RIGHT => nothing */ 0, /* CLOSE => nothing */ 0, /* CHOP => nothing */ 0, /* FROM => nothing */ 0, /* TO => nothing */ 0, /* THEN => nothing */ 0, /* HEADING => nothing */ 0, /* GO => nothing */ 0, /* AT => nothing */ 0, /* WITH => nothing */ 0, /* SAME => nothing */ 0, /* AS => nothing */ 0, /* FIT => nothing */ 0, /* BEHIND => nothing */ 0, /* UNTIL => nothing */ 0, /* EVEN => nothing */ 0, /* DOT_E => nothing */ 0, /* HEIGHT => nothing */ 0, /* WIDTH => nothing */ 0, /* RADIUS => nothing */ 0, /* DIAMETER => nothing */ 0, /* DOTTED => nothing */ 0, /* DASHED => nothing */ 0, /* CW => nothing */ 0, /* CCW => nothing */ 0, /* LARROW => nothing */ 0, /* RARROW => nothing */ 0, /* LRARROW => nothing */ 0, /* INVIS => nothing */ 0, /* THICK => nothing */ 0, /* THIN => nothing */ 0, /* SOLID => nothing */ 0, /* CENTER => nothing */ 0, /* LJUST => nothing */ 0, /* RJUST => nothing */ 0, /* ABOVE => nothing */ 0, /* BELOW => nothing */ 0, /* ITALIC => nothing */ 0, /* BOLD => nothing */ 0, /* MONO => nothing */ 0, /* ALIGNED => nothing */ 0, /* BIG => nothing */ 0, /* SMALL => nothing */ 0, /* AND => nothing */ 0, /* LT => nothing */ 0, /* GT => nothing */ 0, /* ON => nothing */ 0, /* WAY => nothing */ 0, /* BETWEEN => nothing */ 0, /* THE => nothing */ 0, /* NTH => nothing */ 0, /* VERTEX => nothing */ 0, /* TOP => nothing */ 0, /* BOTTOM => nothing */ 0, /* START => nothing */ 0, /* END => nothing */ 0, /* IN => nothing */ 0, /* THIS => nothing */ 0, /* DOT_U => nothing */ 0, /* LAST => nothing */ 0, /* NUMBER => nothing */ 0, /* FUNC1 => nothing */ 0, /* FUNC2 => nothing */ 0, /* DIST => nothing */ 0, /* DOT_XY => nothing */ 0, /* X => nothing */ 0, /* Y => nothing */ 0, /* DOT_L => nothing */ }; #endif /* YYFALLBACK */ /* The following structure represents a single element of the ** parser's stack. Information stored includes: ** ** + The state number for the parser at this level of the stack. ** ** + The value of the token stored at this level of the stack. ** (In other words, the "major" token.) ** ** + The semantic value stored at this level of the stack. This is ** the information used by the action routines in the grammar. ** It is sometimes called the "minor" token. ** ** After the "shift" half of a SHIFTREDUCE action, the stateno field ** actually contains the reduce action for the second half of the ** SHIFTREDUCE. */ struct yyStackEntry { YYACTIONTYPE stateno; /* The state-number, or reduce action in SHIFTREDUCE */ YYCODETYPE major; /* The major token value. This is the code ** number for the token at this stack level */ YYMINORTYPE minor; /* The user-supplied minor token value. This ** is the value of the token */ }; typedef struct yyStackEntry yyStackEntry; /* The state of the parser is completely contained in an instance of ** the following structure */ struct yyParser { yyStackEntry *yytos; /* Pointer to top element of the stack */ #ifdef YYTRACKMAXSTACKDEPTH int yyhwm; /* High-water mark of the stack */ #endif #ifndef YYNOERRORRECOVERY int yyerrcnt; /* Shifts left before out of the error */ #endif pik_parserARG_SDECL /* A place to hold %extra_argument */ pik_parserCTX_SDECL /* A place to hold %extra_context */ yyStackEntry *yystackEnd; /* Last entry in the stack */ yyStackEntry *yystack; /* The parser stack */ yyStackEntry yystk0[YYSTACKDEPTH]; /* Initial stack space */ }; typedef struct yyParser yyParser; #include #ifndef NDEBUG #include static FILE *yyTraceFILE = 0; static char *yyTracePrompt = 0; #endif /* NDEBUG */ #ifndef NDEBUG /* ** Turn parser tracing on by giving a stream to which to write the trace ** and a prompt to preface each trace message. Tracing is turned off ** by making either argument NULL ** ** Inputs: **
    **
  • A FILE* to which trace output should be written. ** If NULL, then tracing is turned off. **
  • A prefix string written at the beginning of every ** line of trace output. If NULL, then tracing is ** turned off. **
** ** Outputs: ** None. */ void pik_parserTrace(FILE *TraceFILE, char *zTracePrompt){ yyTraceFILE = TraceFILE; yyTracePrompt = zTracePrompt; if( yyTraceFILE==0 ) yyTracePrompt = 0; else if( yyTracePrompt==0 ) yyTraceFILE = 0; } #endif /* NDEBUG */ #if defined(YYCOVERAGE) || !defined(NDEBUG) /* For tracing shifts, the names of all terminals and nonterminals ** are required. The following table supplies these names */ static const char *const yyTokenName[] = { /* 0 */ "$", /* 1 */ "ID", /* 2 */ "EDGEPT", /* 3 */ "OF", /* 4 */ "PLUS", /* 5 */ "MINUS", /* 6 */ "STAR", /* 7 */ "SLASH", /* 8 */ "PERCENT", /* 9 */ "UMINUS", /* 10 */ "EOL", /* 11 */ "ASSIGN", /* 12 */ "PLACENAME", /* 13 */ "COLON", /* 14 */ "ASSERT", /* 15 */ "LP", /* 16 */ "EQ", /* 17 */ "RP", /* 18 */ "DEFINE", /* 19 */ "CODEBLOCK", /* 20 */ "FILL", /* 21 */ "COLOR", /* 22 */ "THICKNESS", /* 23 */ "PRINT", /* 24 */ "STRING", /* 25 */ "COMMA", /* 26 */ "CLASSNAME", /* 27 */ "LB", /* 28 */ "RB", /* 29 */ "UP", /* 30 */ "DOWN", /* 31 */ "LEFT", /* 32 */ "RIGHT", /* 33 */ "CLOSE", /* 34 */ "CHOP", /* 35 */ "FROM", /* 36 */ "TO", /* 37 */ "THEN", /* 38 */ "HEADING", /* 39 */ "GO", /* 40 */ "AT", /* 41 */ "WITH", /* 42 */ "SAME", /* 43 */ "AS", /* 44 */ "FIT", /* 45 */ "BEHIND", /* 46 */ "UNTIL", /* 47 */ "EVEN", /* 48 */ "DOT_E", /* 49 */ "HEIGHT", /* 50 */ "WIDTH", /* 51 */ "RADIUS", /* 52 */ "DIAMETER", /* 53 */ "DOTTED", /* 54 */ "DASHED", /* 55 */ "CW", /* 56 */ "CCW", /* 57 */ "LARROW", /* 58 */ "RARROW", /* 59 */ "LRARROW", /* 60 */ "INVIS", /* 61 */ "THICK", /* 62 */ "THIN", /* 63 */ "SOLID", /* 64 */ "CENTER", /* 65 */ "LJUST", /* 66 */ "RJUST", /* 67 */ "ABOVE", /* 68 */ "BELOW", /* 69 */ "ITALIC", /* 70 */ "BOLD", /* 71 */ "MONO", /* 72 */ "ALIGNED", /* 73 */ "BIG", /* 74 */ "SMALL", /* 75 */ "AND", /* 76 */ "LT", /* 77 */ "GT", /* 78 */ "ON", /* 79 */ "WAY", /* 80 */ "BETWEEN", /* 81 */ "THE", /* 82 */ "NTH", /* 83 */ "VERTEX", /* 84 */ "TOP", /* 85 */ "BOTTOM", /* 86 */ "START", /* 87 */ "END", /* 88 */ "IN", /* 89 */ "THIS", /* 90 */ "DOT_U", /* 91 */ "LAST", /* 92 */ "NUMBER", /* 93 */ "FUNC1", /* 94 */ "FUNC2", /* 95 */ "DIST", /* 96 */ "DOT_XY", /* 97 */ "X", /* 98 */ "Y", /* 99 */ "DOT_L", /* 100 */ "statement_list", /* 101 */ "statement", /* 102 */ "unnamed_statement", /* 103 */ "basetype", /* 104 */ "expr", /* 105 */ "numproperty", /* 106 */ "edge", /* 107 */ "direction", /* 108 */ "dashproperty", /* 109 */ "colorproperty", /* 110 */ "locproperty", /* 111 */ "position", /* 112 */ "place", /* 113 */ "object", /* 114 */ "objectname", /* 115 */ "nth", /* 116 */ "textposition", /* 117 */ "rvalue", /* 118 */ "lvalue", /* 119 */ "even", /* 120 */ "relexpr", /* 121 */ "optrelexpr", /* 122 */ "document", /* 123 */ "print", /* 124 */ "prlist", /* 125 */ "pritem", /* 126 */ "prsep", /* 127 */ "attribute_list", /* 128 */ "savelist", /* 129 */ "alist", /* 130 */ "attribute", /* 131 */ "go", /* 132 */ "boolproperty", /* 133 */ "withclause", /* 134 */ "between", /* 135 */ "place2", }; #endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */ #ifndef NDEBUG /* For tracing reduce actions, the names of all rules are required. */ static const char *const yyRuleName[] = { /* 0 */ "document ::= statement_list", /* 1 */ "statement_list ::= statement", /* 2 */ "statement_list ::= statement_list EOL statement", /* 3 */ "statement ::=", /* 4 */ "statement ::= direction", /* 5 */ "statement ::= lvalue ASSIGN rvalue", /* 6 */ "statement ::= PLACENAME COLON unnamed_statement", /* 7 */ "statement ::= PLACENAME COLON position", /* 8 */ "statement ::= unnamed_statement", /* 9 */ "statement ::= print prlist", /* 10 */ "statement ::= ASSERT LP expr EQ expr RP", /* 11 */ "statement ::= ASSERT LP position EQ position RP", /* 12 */ "statement ::= DEFINE ID CODEBLOCK", /* 13 */ "rvalue ::= PLACENAME", /* 14 */ "pritem ::= FILL", /* 15 */ "pritem ::= COLOR", /* 16 */ "pritem ::= THICKNESS", /* 17 */ "pritem ::= rvalue", /* 18 */ "pritem ::= STRING", /* 19 */ "prsep ::= COMMA", /* 20 */ "unnamed_statement ::= basetype attribute_list", /* 21 */ "basetype ::= CLASSNAME", /* 22 */ "basetype ::= STRING textposition", /* 23 */ "basetype ::= LB savelist statement_list RB", /* 24 */ "savelist ::=", /* 25 */ "relexpr ::= expr", /* 26 */ "relexpr ::= expr PERCENT", /* 27 */ "optrelexpr ::=", /* 28 */ "attribute_list ::= relexpr alist", /* 29 */ "attribute ::= numproperty relexpr", /* 30 */ "attribute ::= dashproperty expr", /* 31 */ "attribute ::= dashproperty", /* 32 */ "attribute ::= colorproperty rvalue", /* 33 */ "attribute ::= go direction optrelexpr", /* 34 */ "attribute ::= go direction even position", /* 35 */ "attribute ::= CLOSE", /* 36 */ "attribute ::= CHOP", /* 37 */ "attribute ::= FROM position", /* 38 */ "attribute ::= TO position", /* 39 */ "attribute ::= THEN", /* 40 */ "attribute ::= THEN optrelexpr HEADING expr", /* 41 */ "attribute ::= THEN optrelexpr EDGEPT", /* 42 */ "attribute ::= GO optrelexpr HEADING expr", /* 43 */ "attribute ::= GO optrelexpr EDGEPT", /* 44 */ "attribute ::= AT position", /* 45 */ "attribute ::= SAME", /* 46 */ "attribute ::= SAME AS object", /* 47 */ "attribute ::= STRING textposition", /* 48 */ "attribute ::= FIT", /* 49 */ "attribute ::= BEHIND object", /* 50 */ "withclause ::= DOT_E edge AT position", /* 51 */ "withclause ::= edge AT position", /* 52 */ "numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS", /* 53 */ "boolproperty ::= CW", /* 54 */ "boolproperty ::= CCW", /* 55 */ "boolproperty ::= LARROW", /* 56 */ "boolproperty ::= RARROW", /* 57 */ "boolproperty ::= LRARROW", /* 58 */ "boolproperty ::= INVIS", /* 59 */ "boolproperty ::= THICK", /* 60 */ "boolproperty ::= THIN", /* 61 */ "boolproperty ::= SOLID", /* 62 */ "textposition ::=", /* 63 */ "textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL", /* 64 */ "position ::= expr COMMA expr", /* 65 */ "position ::= place PLUS expr COMMA expr", /* 66 */ "position ::= place MINUS expr COMMA expr", /* 67 */ "position ::= place PLUS LP expr COMMA expr RP", /* 68 */ "position ::= place MINUS LP expr COMMA expr RP", /* 69 */ "position ::= LP position COMMA position RP", /* 70 */ "position ::= LP position RP", /* 71 */ "position ::= expr between position AND position", /* 72 */ "position ::= expr LT position COMMA position GT", /* 73 */ "position ::= expr ABOVE position", /* 74 */ "position ::= expr BELOW position", /* 75 */ "position ::= expr LEFT OF position", /* 76 */ "position ::= expr RIGHT OF position", /* 77 */ "position ::= expr ON HEADING EDGEPT OF position", /* 78 */ "position ::= expr HEADING EDGEPT OF position", /* 79 */ "position ::= expr EDGEPT OF position", /* 80 */ "position ::= expr ON HEADING expr FROM position", /* 81 */ "position ::= expr HEADING expr FROM position", /* 82 */ "place ::= edge OF object", /* 83 */ "place2 ::= object", /* 84 */ "place2 ::= object DOT_E edge", /* 85 */ "place2 ::= NTH VERTEX OF object", /* 86 */ "object ::= nth", /* 87 */ "object ::= nth OF|IN object", /* 88 */ "objectname ::= THIS", /* 89 */ "objectname ::= PLACENAME", /* 90 */ "objectname ::= objectname DOT_U PLACENAME", /* 91 */ "nth ::= NTH CLASSNAME", /* 92 */ "nth ::= NTH LAST CLASSNAME", /* 93 */ "nth ::= LAST CLASSNAME", /* 94 */ "nth ::= LAST", /* 95 */ "nth ::= NTH LB RB", /* 96 */ "nth ::= NTH LAST LB RB", /* 97 */ "nth ::= LAST LB RB", /* 98 */ "expr ::= expr PLUS expr", /* 99 */ "expr ::= expr MINUS expr", /* 100 */ "expr ::= expr STAR expr", /* 101 */ "expr ::= expr SLASH expr", /* 102 */ "expr ::= MINUS expr", /* 103 */ "expr ::= PLUS expr", /* 104 */ "expr ::= LP expr RP", /* 105 */ "expr ::= LP FILL|COLOR|THICKNESS RP", /* 106 */ "expr ::= NUMBER", /* 107 */ "expr ::= ID", /* 108 */ "expr ::= FUNC1 LP expr RP", /* 109 */ "expr ::= FUNC2 LP expr COMMA expr RP", /* 110 */ "expr ::= DIST LP position COMMA position RP", /* 111 */ "expr ::= place2 DOT_XY X", /* 112 */ "expr ::= place2 DOT_XY Y", /* 113 */ "expr ::= object DOT_L numproperty", /* 114 */ "expr ::= object DOT_L dashproperty", /* 115 */ "expr ::= object DOT_L colorproperty", /* 116 */ "lvalue ::= ID", /* 117 */ "lvalue ::= FILL", /* 118 */ "lvalue ::= COLOR", /* 119 */ "lvalue ::= THICKNESS", /* 120 */ "rvalue ::= expr", /* 121 */ "print ::= PRINT", /* 122 */ "prlist ::= pritem", /* 123 */ "prlist ::= prlist prsep pritem", /* 124 */ "direction ::= UP", /* 125 */ "direction ::= DOWN", /* 126 */ "direction ::= LEFT", /* 127 */ "direction ::= RIGHT", /* 128 */ "optrelexpr ::= relexpr", /* 129 */ "attribute_list ::= alist", /* 130 */ "alist ::=", /* 131 */ "alist ::= alist attribute", /* 132 */ "attribute ::= boolproperty", /* 133 */ "attribute ::= WITH withclause", /* 134 */ "go ::= GO", /* 135 */ "go ::=", /* 136 */ "even ::= UNTIL EVEN WITH", /* 137 */ "even ::= EVEN WITH", /* 138 */ "dashproperty ::= DOTTED", /* 139 */ "dashproperty ::= DASHED", /* 140 */ "colorproperty ::= FILL", /* 141 */ "colorproperty ::= COLOR", /* 142 */ "position ::= place", /* 143 */ "between ::= WAY BETWEEN", /* 144 */ "between ::= BETWEEN", /* 145 */ "between ::= OF THE WAY BETWEEN", /* 146 */ "place ::= place2", /* 147 */ "edge ::= CENTER", /* 148 */ "edge ::= EDGEPT", /* 149 */ "edge ::= TOP", /* 150 */ "edge ::= BOTTOM", /* 151 */ "edge ::= START", /* 152 */ "edge ::= END", /* 153 */ "edge ::= RIGHT", /* 154 */ "edge ::= LEFT", /* 155 */ "object ::= objectname", }; #endif /* NDEBUG */ #if YYGROWABLESTACK /* ** Try to increase the size of the parser stack. Return the number ** of errors. Return 0 on success. */ static int yyGrowStack(yyParser *p){ int oldSize = 1 + (int)(p->yystackEnd - p->yystack); int newSize; int idx; yyStackEntry *pNew; newSize = oldSize*2 + 100; idx = (int)(p->yytos - p->yystack); if( p->yystack==p->yystk0 ){ pNew = YYREALLOC(0, newSize*sizeof(pNew[0])); if( pNew==0 ) return 1; memcpy(pNew, p->yystack, oldSize*sizeof(pNew[0])); }else{ pNew = YYREALLOC(p->yystack, newSize*sizeof(pNew[0])); if( pNew==0 ) return 1; } p->yystack = pNew; p->yytos = &p->yystack[idx]; #ifndef NDEBUG if( yyTraceFILE ){ fprintf(yyTraceFILE,"%sStack grows from %d to %d entries.\n", yyTracePrompt, oldSize, newSize); } #endif p->yystackEnd = &p->yystack[newSize-1]; return 0; } #endif /* YYGROWABLESTACK */ #if !YYGROWABLESTACK /* For builds that do no have a growable stack, yyGrowStack always ** returns an error. */ # define yyGrowStack(X) 1 #endif /* Datatype of the argument to the memory allocated passed as the ** second argument to pik_parserAlloc() below. This can be changed by ** putting an appropriate #define in the %include section of the input ** grammar. */ #ifndef YYMALLOCARGTYPE # define YYMALLOCARGTYPE size_t #endif /* Initialize a new parser that has already been allocated. */ void pik_parserInit(void *yypRawParser pik_parserCTX_PDECL){ yyParser *yypParser = (yyParser*)yypRawParser; pik_parserCTX_STORE #ifdef YYTRACKMAXSTACKDEPTH yypParser->yyhwm = 0; #endif yypParser->yystack = yypParser->yystk0; yypParser->yystackEnd = &yypParser->yystack[YYSTACKDEPTH-1]; #ifndef YYNOERRORRECOVERY yypParser->yyerrcnt = -1; #endif yypParser->yytos = yypParser->yystack; yypParser->yystack[0].stateno = 0; yypParser->yystack[0].major = 0; } #ifndef pik_parser_ENGINEALWAYSONSTACK /* ** This function allocates a new parser. ** The only argument is a pointer to a function which works like ** malloc. ** ** Inputs: ** A pointer to the function used to allocate memory. ** ** Outputs: ** A pointer to a parser. This pointer is used in subsequent calls ** to pik_parser and pik_parserFree. */ void *pik_parserAlloc(void *(*mallocProc)(YYMALLOCARGTYPE) pik_parserCTX_PDECL){ yyParser *yypParser; yypParser = (yyParser*)(*mallocProc)( (YYMALLOCARGTYPE)sizeof(yyParser) ); if( yypParser ){ pik_parserCTX_STORE pik_parserInit(yypParser pik_parserCTX_PARAM); } return (void*)yypParser; } #endif /* pik_parser_ENGINEALWAYSONSTACK */ /* The following function deletes the "minor type" or semantic value ** associated with a symbol. The symbol can be either a terminal ** or nonterminal. "yymajor" is the symbol code, and "yypminor" is ** a pointer to the value to be deleted. The code used to do the ** deletions is derived from the %destructor and/or %token_destructor ** directives of the input grammar. */ static void yy_destructor( yyParser *yypParser, /* The parser */ YYCODETYPE yymajor, /* Type code for object to destroy */ YYMINORTYPE *yypminor /* The object to be destroyed */ ){ pik_parserARG_FETCH pik_parserCTX_FETCH switch( yymajor ){ /* Here is inserted the actions which take place when a ** terminal or non-terminal is destroyed. This can happen ** when the symbol is popped from the stack during a ** reduce or during error processing or when a parser is ** being destroyed before it is finished parsing. ** ** Note: during a reduce, the only symbols destroyed are those ** which appear on the RHS of the rule, but which are *not* used ** inside the C code. */ /********* Begin destructor definitions ***************************************/ case 100: /* statement_list */ { #line 523 "pikchr.y" pik_elist_free(p,(yypminor->yy235)); #line 1789 "pikchr.c" } break; case 101: /* statement */ case 102: /* unnamed_statement */ case 103: /* basetype */ { #line 525 "pikchr.y" pik_elem_free(p,(yypminor->yy162)); #line 1798 "pikchr.c" } break; /********* End destructor definitions *****************************************/ default: break; /* If no destructor action specified: do nothing */ } } /* ** Pop the parser's stack once. ** ** If there is a destructor routine associated with the token which ** is popped from the stack, then call it. */ static void yy_pop_parser_stack(yyParser *pParser){ yyStackEntry *yytos; assert( pParser->yytos!=0 ); assert( pParser->yytos > pParser->yystack ); yytos = pParser->yytos--; #ifndef NDEBUG if( yyTraceFILE ){ fprintf(yyTraceFILE,"%sPopping %s\n", yyTracePrompt, yyTokenName[yytos->major]); } #endif yy_destructor(pParser, yytos->major, &yytos->minor); } /* ** Clear all secondary memory allocations from the parser */ void pik_parserFinalize(void *p){ yyParser *pParser = (yyParser*)p; /* In-lined version of calling yy_pop_parser_stack() for each ** element left in the stack */ yyStackEntry *yytos = pParser->yytos; while( yytos>pParser->yystack ){ #ifndef NDEBUG if( yyTraceFILE ){ fprintf(yyTraceFILE,"%sPopping %s\n", yyTracePrompt, yyTokenName[yytos->major]); } #endif if( yytos->major>=YY_MIN_DSTRCTR ){ yy_destructor(pParser, yytos->major, &yytos->minor); } yytos--; } #if YYGROWABLESTACK if( pParser->yystack!=pParser->yystk0 ) YYFREE(pParser->yystack); #endif } #ifndef pik_parser_ENGINEALWAYSONSTACK /* ** Deallocate and destroy a parser. Destructors are called for ** all stack elements before shutting the parser down. ** ** If the YYPARSEFREENEVERNULL macro exists (for example because it ** is defined in a %include section of the input grammar) then it is ** assumed that the input pointer is never NULL. */ void pik_parserFree( void *p, /* The parser to be deleted */ void (*freeProc)(void*) /* Function used to reclaim memory */ ){ #ifndef YYPARSEFREENEVERNULL if( p==0 ) return; #endif pik_parserFinalize(p); (*freeProc)(p); } #endif /* pik_parser_ENGINEALWAYSONSTACK */ /* ** Return the peak depth of the stack for a parser. */ #ifdef YYTRACKMAXSTACKDEPTH int pik_parserStackPeak(void *p){ yyParser *pParser = (yyParser*)p; return pParser->yyhwm; } #endif /* This array of booleans keeps track of the parser statement ** coverage. The element yycoverage[X][Y] is set when the parser ** is in state X and has a lookahead token Y. In a well-tested ** systems, every element of this matrix should end up being set. */ #if defined(YYCOVERAGE) static unsigned char yycoverage[YYNSTATE][YYNTOKEN]; #endif /* ** Write into out a description of every state/lookahead combination that ** ** (1) has not been used by the parser, and ** (2) is not a syntax error. ** ** Return the number of missed state/lookahead combinations. */ #if defined(YYCOVERAGE) int pik_parserCoverage(FILE *out){ int stateno, iLookAhead, i; int nMissed = 0; for(stateno=0; statenoYY_MAX_SHIFT ) return stateno; assert( stateno <= YY_SHIFT_COUNT ); #if defined(YYCOVERAGE) yycoverage[stateno][iLookAhead] = 1; #endif do{ i = yy_shift_ofst[stateno]; assert( i>=0 ); assert( i<=YY_ACTTAB_COUNT ); assert( i+YYNTOKEN<=(int)YY_NLOOKAHEAD ); assert( iLookAhead!=YYNOCODE ); assert( iLookAhead < YYNTOKEN ); i += iLookAhead; assert( i<(int)YY_NLOOKAHEAD ); if( yy_lookahead[i]!=iLookAhead ){ #ifdef YYFALLBACK YYCODETYPE iFallback; /* Fallback token */ assert( iLookAhead %s\n", yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]); } #endif assert( yyFallback[iFallback]==0 ); /* Fallback loop must terminate */ iLookAhead = iFallback; continue; } #endif #ifdef YYWILDCARD { int j = i - iLookAhead + YYWILDCARD; assert( j<(int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])) ); if( yy_lookahead[j]==YYWILDCARD && iLookAhead>0 ){ #ifndef NDEBUG if( yyTraceFILE ){ fprintf(yyTraceFILE, "%sWILDCARD %s => %s\n", yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[YYWILDCARD]); } #endif /* NDEBUG */ return yy_action[j]; } } #endif /* YYWILDCARD */ return yy_default[stateno]; }else{ assert( i>=0 && i<(int)(sizeof(yy_action)/sizeof(yy_action[0])) ); return yy_action[i]; } }while(1); } /* ** Find the appropriate action for a parser given the non-terminal ** look-ahead token iLookAhead. */ static YYACTIONTYPE yy_find_reduce_action( YYACTIONTYPE stateno, /* Current state number */ YYCODETYPE iLookAhead /* The look-ahead token */ ){ int i; #ifdef YYERRORSYMBOL if( stateno>YY_REDUCE_COUNT ){ return yy_default[stateno]; } #else assert( stateno<=YY_REDUCE_COUNT ); #endif i = yy_reduce_ofst[stateno]; assert( iLookAhead!=YYNOCODE ); i += iLookAhead; #ifdef YYERRORSYMBOL if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){ return yy_default[stateno]; } #else assert( i>=0 && iyytos>yypParser->yystack ) yy_pop_parser_stack(yypParser); /* Here code is inserted which will execute if the parser ** stack every overflows */ /******** Begin %stack_overflow code ******************************************/ #line 557 "pikchr.y" pik_error(p, 0, "parser stack overflow"); #line 2036 "pikchr.c" /******** End %stack_overflow code ********************************************/ pik_parserARG_STORE /* Suppress warning about unused %extra_argument var */ pik_parserCTX_STORE } /* ** Print tracing information for a SHIFT action */ #ifndef NDEBUG static void yyTraceShift(yyParser *yypParser, int yyNewState, const char *zTag){ if( yyTraceFILE ){ if( yyNewStateyytos->major], yyNewState); }else{ fprintf(yyTraceFILE,"%s%s '%s', pending reduce %d\n", yyTracePrompt, zTag, yyTokenName[yypParser->yytos->major], yyNewState - YY_MIN_REDUCE); } } } #else # define yyTraceShift(X,Y,Z) #endif /* ** Perform a shift action. */ static void yy_shift( yyParser *yypParser, /* The parser to be shifted */ YYACTIONTYPE yyNewState, /* The new state to shift in */ YYCODETYPE yyMajor, /* The major token to shift in */ pik_parserTOKENTYPE yyMinor /* The minor token to shift in */ ){ yyStackEntry *yytos; yypParser->yytos++; #ifdef YYTRACKMAXSTACKDEPTH if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){ yypParser->yyhwm++; assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack) ); } #endif yytos = yypParser->yytos; if( yytos>yypParser->yystackEnd ){ if( yyGrowStack(yypParser) ){ yypParser->yytos--; yyStackOverflow(yypParser); return; } yytos = yypParser->yytos; assert( yytos <= yypParser->yystackEnd ); } if( yyNewState > YY_MAX_SHIFT ){ yyNewState += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE; } yytos->stateno = yyNewState; yytos->major = yyMajor; yytos->minor.yy0 = yyMinor; yyTraceShift(yypParser, yyNewState, "Shift"); } /* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side ** of that rule */ static const YYCODETYPE yyRuleInfoLhs[] = { 122, /* (0) document ::= statement_list */ 100, /* (1) statement_list ::= statement */ 100, /* (2) statement_list ::= statement_list EOL statement */ 101, /* (3) statement ::= */ 101, /* (4) statement ::= direction */ 101, /* (5) statement ::= lvalue ASSIGN rvalue */ 101, /* (6) statement ::= PLACENAME COLON unnamed_statement */ 101, /* (7) statement ::= PLACENAME COLON position */ 101, /* (8) statement ::= unnamed_statement */ 101, /* (9) statement ::= print prlist */ 101, /* (10) statement ::= ASSERT LP expr EQ expr RP */ 101, /* (11) statement ::= ASSERT LP position EQ position RP */ 101, /* (12) statement ::= DEFINE ID CODEBLOCK */ 117, /* (13) rvalue ::= PLACENAME */ 125, /* (14) pritem ::= FILL */ 125, /* (15) pritem ::= COLOR */ 125, /* (16) pritem ::= THICKNESS */ 125, /* (17) pritem ::= rvalue */ 125, /* (18) pritem ::= STRING */ 126, /* (19) prsep ::= COMMA */ 102, /* (20) unnamed_statement ::= basetype attribute_list */ 103, /* (21) basetype ::= CLASSNAME */ 103, /* (22) basetype ::= STRING textposition */ 103, /* (23) basetype ::= LB savelist statement_list RB */ 128, /* (24) savelist ::= */ 120, /* (25) relexpr ::= expr */ 120, /* (26) relexpr ::= expr PERCENT */ 121, /* (27) optrelexpr ::= */ 127, /* (28) attribute_list ::= relexpr alist */ 130, /* (29) attribute ::= numproperty relexpr */ 130, /* (30) attribute ::= dashproperty expr */ 130, /* (31) attribute ::= dashproperty */ 130, /* (32) attribute ::= colorproperty rvalue */ 130, /* (33) attribute ::= go direction optrelexpr */ 130, /* (34) attribute ::= go direction even position */ 130, /* (35) attribute ::= CLOSE */ 130, /* (36) attribute ::= CHOP */ 130, /* (37) attribute ::= FROM position */ 130, /* (38) attribute ::= TO position */ 130, /* (39) attribute ::= THEN */ 130, /* (40) attribute ::= THEN optrelexpr HEADING expr */ 130, /* (41) attribute ::= THEN optrelexpr EDGEPT */ 130, /* (42) attribute ::= GO optrelexpr HEADING expr */ 130, /* (43) attribute ::= GO optrelexpr EDGEPT */ 130, /* (44) attribute ::= AT position */ 130, /* (45) attribute ::= SAME */ 130, /* (46) attribute ::= SAME AS object */ 130, /* (47) attribute ::= STRING textposition */ 130, /* (48) attribute ::= FIT */ 130, /* (49) attribute ::= BEHIND object */ 133, /* (50) withclause ::= DOT_E edge AT position */ 133, /* (51) withclause ::= edge AT position */ 105, /* (52) numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */ 132, /* (53) boolproperty ::= CW */ 132, /* (54) boolproperty ::= CCW */ 132, /* (55) boolproperty ::= LARROW */ 132, /* (56) boolproperty ::= RARROW */ 132, /* (57) boolproperty ::= LRARROW */ 132, /* (58) boolproperty ::= INVIS */ 132, /* (59) boolproperty ::= THICK */ 132, /* (60) boolproperty ::= THIN */ 132, /* (61) boolproperty ::= SOLID */ 116, /* (62) textposition ::= */ 116, /* (63) textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL */ 111, /* (64) position ::= expr COMMA expr */ 111, /* (65) position ::= place PLUS expr COMMA expr */ 111, /* (66) position ::= place MINUS expr COMMA expr */ 111, /* (67) position ::= place PLUS LP expr COMMA expr RP */ 111, /* (68) position ::= place MINUS LP expr COMMA expr RP */ 111, /* (69) position ::= LP position COMMA position RP */ 111, /* (70) position ::= LP position RP */ 111, /* (71) position ::= expr between position AND position */ 111, /* (72) position ::= expr LT position COMMA position GT */ 111, /* (73) position ::= expr ABOVE position */ 111, /* (74) position ::= expr BELOW position */ 111, /* (75) position ::= expr LEFT OF position */ 111, /* (76) position ::= expr RIGHT OF position */ 111, /* (77) position ::= expr ON HEADING EDGEPT OF position */ 111, /* (78) position ::= expr HEADING EDGEPT OF position */ 111, /* (79) position ::= expr EDGEPT OF position */ 111, /* (80) position ::= expr ON HEADING expr FROM position */ 111, /* (81) position ::= expr HEADING expr FROM position */ 112, /* (82) place ::= edge OF object */ 135, /* (83) place2 ::= object */ 135, /* (84) place2 ::= object DOT_E edge */ 135, /* (85) place2 ::= NTH VERTEX OF object */ 113, /* (86) object ::= nth */ 113, /* (87) object ::= nth OF|IN object */ 114, /* (88) objectname ::= THIS */ 114, /* (89) objectname ::= PLACENAME */ 114, /* (90) objectname ::= objectname DOT_U PLACENAME */ 115, /* (91) nth ::= NTH CLASSNAME */ 115, /* (92) nth ::= NTH LAST CLASSNAME */ 115, /* (93) nth ::= LAST CLASSNAME */ 115, /* (94) nth ::= LAST */ 115, /* (95) nth ::= NTH LB RB */ 115, /* (96) nth ::= NTH LAST LB RB */ 115, /* (97) nth ::= LAST LB RB */ 104, /* (98) expr ::= expr PLUS expr */ 104, /* (99) expr ::= expr MINUS expr */ 104, /* (100) expr ::= expr STAR expr */ 104, /* (101) expr ::= expr SLASH expr */ 104, /* (102) expr ::= MINUS expr */ 104, /* (103) expr ::= PLUS expr */ 104, /* (104) expr ::= LP expr RP */ 104, /* (105) expr ::= LP FILL|COLOR|THICKNESS RP */ 104, /* (106) expr ::= NUMBER */ 104, /* (107) expr ::= ID */ 104, /* (108) expr ::= FUNC1 LP expr RP */ 104, /* (109) expr ::= FUNC2 LP expr COMMA expr RP */ 104, /* (110) expr ::= DIST LP position COMMA position RP */ 104, /* (111) expr ::= place2 DOT_XY X */ 104, /* (112) expr ::= place2 DOT_XY Y */ 104, /* (113) expr ::= object DOT_L numproperty */ 104, /* (114) expr ::= object DOT_L dashproperty */ 104, /* (115) expr ::= object DOT_L colorproperty */ 118, /* (116) lvalue ::= ID */ 118, /* (117) lvalue ::= FILL */ 118, /* (118) lvalue ::= COLOR */ 118, /* (119) lvalue ::= THICKNESS */ 117, /* (120) rvalue ::= expr */ 123, /* (121) print ::= PRINT */ 124, /* (122) prlist ::= pritem */ 124, /* (123) prlist ::= prlist prsep pritem */ 107, /* (124) direction ::= UP */ 107, /* (125) direction ::= DOWN */ 107, /* (126) direction ::= LEFT */ 107, /* (127) direction ::= RIGHT */ 121, /* (128) optrelexpr ::= relexpr */ 127, /* (129) attribute_list ::= alist */ 129, /* (130) alist ::= */ 129, /* (131) alist ::= alist attribute */ 130, /* (132) attribute ::= boolproperty */ 130, /* (133) attribute ::= WITH withclause */ 131, /* (134) go ::= GO */ 131, /* (135) go ::= */ 119, /* (136) even ::= UNTIL EVEN WITH */ 119, /* (137) even ::= EVEN WITH */ 108, /* (138) dashproperty ::= DOTTED */ 108, /* (139) dashproperty ::= DASHED */ 109, /* (140) colorproperty ::= FILL */ 109, /* (141) colorproperty ::= COLOR */ 111, /* (142) position ::= place */ 134, /* (143) between ::= WAY BETWEEN */ 134, /* (144) between ::= BETWEEN */ 134, /* (145) between ::= OF THE WAY BETWEEN */ 112, /* (146) place ::= place2 */ 106, /* (147) edge ::= CENTER */ 106, /* (148) edge ::= EDGEPT */ 106, /* (149) edge ::= TOP */ 106, /* (150) edge ::= BOTTOM */ 106, /* (151) edge ::= START */ 106, /* (152) edge ::= END */ 106, /* (153) edge ::= RIGHT */ 106, /* (154) edge ::= LEFT */ 113, /* (155) object ::= objectname */ }; /* For rule J, yyRuleInfoNRhs[J] contains the negative of the number ** of symbols on the right-hand side of that rule. */ static const signed char yyRuleInfoNRhs[] = { -1, /* (0) document ::= statement_list */ -1, /* (1) statement_list ::= statement */ -3, /* (2) statement_list ::= statement_list EOL statement */ 0, /* (3) statement ::= */ -1, /* (4) statement ::= direction */ -3, /* (5) statement ::= lvalue ASSIGN rvalue */ -3, /* (6) statement ::= PLACENAME COLON unnamed_statement */ -3, /* (7) statement ::= PLACENAME COLON position */ -1, /* (8) statement ::= unnamed_statement */ -2, /* (9) statement ::= print prlist */ -6, /* (10) statement ::= ASSERT LP expr EQ expr RP */ -6, /* (11) statement ::= ASSERT LP position EQ position RP */ -3, /* (12) statement ::= DEFINE ID CODEBLOCK */ -1, /* (13) rvalue ::= PLACENAME */ -1, /* (14) pritem ::= FILL */ -1, /* (15) pritem ::= COLOR */ -1, /* (16) pritem ::= THICKNESS */ -1, /* (17) pritem ::= rvalue */ -1, /* (18) pritem ::= STRING */ -1, /* (19) prsep ::= COMMA */ -2, /* (20) unnamed_statement ::= basetype attribute_list */ -1, /* (21) basetype ::= CLASSNAME */ -2, /* (22) basetype ::= STRING textposition */ -4, /* (23) basetype ::= LB savelist statement_list RB */ 0, /* (24) savelist ::= */ -1, /* (25) relexpr ::= expr */ -2, /* (26) relexpr ::= expr PERCENT */ 0, /* (27) optrelexpr ::= */ -2, /* (28) attribute_list ::= relexpr alist */ -2, /* (29) attribute ::= numproperty relexpr */ -2, /* (30) attribute ::= dashproperty expr */ -1, /* (31) attribute ::= dashproperty */ -2, /* (32) attribute ::= colorproperty rvalue */ -3, /* (33) attribute ::= go direction optrelexpr */ -4, /* (34) attribute ::= go direction even position */ -1, /* (35) attribute ::= CLOSE */ -1, /* (36) attribute ::= CHOP */ -2, /* (37) attribute ::= FROM position */ -2, /* (38) attribute ::= TO position */ -1, /* (39) attribute ::= THEN */ -4, /* (40) attribute ::= THEN optrelexpr HEADING expr */ -3, /* (41) attribute ::= THEN optrelexpr EDGEPT */ -4, /* (42) attribute ::= GO optrelexpr HEADING expr */ -3, /* (43) attribute ::= GO optrelexpr EDGEPT */ -2, /* (44) attribute ::= AT position */ -1, /* (45) attribute ::= SAME */ -3, /* (46) attribute ::= SAME AS object */ -2, /* (47) attribute ::= STRING textposition */ -1, /* (48) attribute ::= FIT */ -2, /* (49) attribute ::= BEHIND object */ -4, /* (50) withclause ::= DOT_E edge AT position */ -3, /* (51) withclause ::= edge AT position */ -1, /* (52) numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */ -1, /* (53) boolproperty ::= CW */ -1, /* (54) boolproperty ::= CCW */ -1, /* (55) boolproperty ::= LARROW */ -1, /* (56) boolproperty ::= RARROW */ -1, /* (57) boolproperty ::= LRARROW */ -1, /* (58) boolproperty ::= INVIS */ -1, /* (59) boolproperty ::= THICK */ -1, /* (60) boolproperty ::= THIN */ -1, /* (61) boolproperty ::= SOLID */ 0, /* (62) textposition ::= */ -2, /* (63) textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL */ -3, /* (64) position ::= expr COMMA expr */ -5, /* (65) position ::= place PLUS expr COMMA expr */ -5, /* (66) position ::= place MINUS expr COMMA expr */ -7, /* (67) position ::= place PLUS LP expr COMMA expr RP */ -7, /* (68) position ::= place MINUS LP expr COMMA expr RP */ -5, /* (69) position ::= LP position COMMA position RP */ -3, /* (70) position ::= LP position RP */ -5, /* (71) position ::= expr between position AND position */ -6, /* (72) position ::= expr LT position COMMA position GT */ -3, /* (73) position ::= expr ABOVE position */ -3, /* (74) position ::= expr BELOW position */ -4, /* (75) position ::= expr LEFT OF position */ -4, /* (76) position ::= expr RIGHT OF position */ -6, /* (77) position ::= expr ON HEADING EDGEPT OF position */ -5, /* (78) position ::= expr HEADING EDGEPT OF position */ -4, /* (79) position ::= expr EDGEPT OF position */ -6, /* (80) position ::= expr ON HEADING expr FROM position */ -5, /* (81) position ::= expr HEADING expr FROM position */ -3, /* (82) place ::= edge OF object */ -1, /* (83) place2 ::= object */ -3, /* (84) place2 ::= object DOT_E edge */ -4, /* (85) place2 ::= NTH VERTEX OF object */ -1, /* (86) object ::= nth */ -3, /* (87) object ::= nth OF|IN object */ -1, /* (88) objectname ::= THIS */ -1, /* (89) objectname ::= PLACENAME */ -3, /* (90) objectname ::= objectname DOT_U PLACENAME */ -2, /* (91) nth ::= NTH CLASSNAME */ -3, /* (92) nth ::= NTH LAST CLASSNAME */ -2, /* (93) nth ::= LAST CLASSNAME */ -1, /* (94) nth ::= LAST */ -3, /* (95) nth ::= NTH LB RB */ -4, /* (96) nth ::= NTH LAST LB RB */ -3, /* (97) nth ::= LAST LB RB */ -3, /* (98) expr ::= expr PLUS expr */ -3, /* (99) expr ::= expr MINUS expr */ -3, /* (100) expr ::= expr STAR expr */ -3, /* (101) expr ::= expr SLASH expr */ -2, /* (102) expr ::= MINUS expr */ -2, /* (103) expr ::= PLUS expr */ -3, /* (104) expr ::= LP expr RP */ -3, /* (105) expr ::= LP FILL|COLOR|THICKNESS RP */ -1, /* (106) expr ::= NUMBER */ -1, /* (107) expr ::= ID */ -4, /* (108) expr ::= FUNC1 LP expr RP */ -6, /* (109) expr ::= FUNC2 LP expr COMMA expr RP */ -6, /* (110) expr ::= DIST LP position COMMA position RP */ -3, /* (111) expr ::= place2 DOT_XY X */ -3, /* (112) expr ::= place2 DOT_XY Y */ -3, /* (113) expr ::= object DOT_L numproperty */ -3, /* (114) expr ::= object DOT_L dashproperty */ -3, /* (115) expr ::= object DOT_L colorproperty */ -1, /* (116) lvalue ::= ID */ -1, /* (117) lvalue ::= FILL */ -1, /* (118) lvalue ::= COLOR */ -1, /* (119) lvalue ::= THICKNESS */ -1, /* (120) rvalue ::= expr */ -1, /* (121) print ::= PRINT */ -1, /* (122) prlist ::= pritem */ -3, /* (123) prlist ::= prlist prsep pritem */ -1, /* (124) direction ::= UP */ -1, /* (125) direction ::= DOWN */ -1, /* (126) direction ::= LEFT */ -1, /* (127) direction ::= RIGHT */ -1, /* (128) optrelexpr ::= relexpr */ -1, /* (129) attribute_list ::= alist */ 0, /* (130) alist ::= */ -2, /* (131) alist ::= alist attribute */ -1, /* (132) attribute ::= boolproperty */ -2, /* (133) attribute ::= WITH withclause */ -1, /* (134) go ::= GO */ 0, /* (135) go ::= */ -3, /* (136) even ::= UNTIL EVEN WITH */ -2, /* (137) even ::= EVEN WITH */ -1, /* (138) dashproperty ::= DOTTED */ -1, /* (139) dashproperty ::= DASHED */ -1, /* (140) colorproperty ::= FILL */ -1, /* (141) colorproperty ::= COLOR */ -1, /* (142) position ::= place */ -2, /* (143) between ::= WAY BETWEEN */ -1, /* (144) between ::= BETWEEN */ -4, /* (145) between ::= OF THE WAY BETWEEN */ -1, /* (146) place ::= place2 */ -1, /* (147) edge ::= CENTER */ -1, /* (148) edge ::= EDGEPT */ -1, /* (149) edge ::= TOP */ -1, /* (150) edge ::= BOTTOM */ -1, /* (151) edge ::= START */ -1, /* (152) edge ::= END */ -1, /* (153) edge ::= RIGHT */ -1, /* (154) edge ::= LEFT */ -1, /* (155) object ::= objectname */ }; static void yy_accept(yyParser*); /* Forward Declaration */ /* ** Perform a reduce action and the shift that must immediately ** follow the reduce. ** ** The yyLookahead and yyLookaheadToken parameters provide reduce actions ** access to the lookahead token (if any). The yyLookahead will be YYNOCODE ** if the lookahead token has already been consumed. As this procedure is ** only called from one place, optimizing compilers will in-line it, which ** means that the extra parameters have no performance impact. */ static YYACTIONTYPE yy_reduce( yyParser *yypParser, /* The parser */ unsigned int yyruleno, /* Number of the rule by which to reduce */ int yyLookahead, /* Lookahead token, or YYNOCODE if none */ pik_parserTOKENTYPE yyLookaheadToken /* Value of the lookahead token */ pik_parserCTX_PDECL /* %extra_context */ ){ int yygoto; /* The next state */ YYACTIONTYPE yyact; /* The next action */ yyStackEntry *yymsp; /* The top of the parser's stack */ int yysize; /* Amount to pop the stack */ pik_parserARG_FETCH (void)yyLookahead; (void)yyLookaheadToken; yymsp = yypParser->yytos; switch( yyruleno ){ /* Beginning here are the reduction cases. A typical example ** follows: ** case 0: ** #line ** { ... } // User supplied code ** #line ** break; */ /********** Begin reduce actions **********************************************/ YYMINORTYPE yylhsminor; case 0: /* document ::= statement_list */ #line 561 "pikchr.y" {pik_render(p,yymsp[0].minor.yy235);} #line 2463 "pikchr.c" break; case 1: /* statement_list ::= statement */ #line 564 "pikchr.y" { yylhsminor.yy235 = pik_elist_append(p,0,yymsp[0].minor.yy162); } #line 2468 "pikchr.c" yymsp[0].minor.yy235 = yylhsminor.yy235; break; case 2: /* statement_list ::= statement_list EOL statement */ #line 566 "pikchr.y" { yylhsminor.yy235 = pik_elist_append(p,yymsp[-2].minor.yy235,yymsp[0].minor.yy162); } #line 2474 "pikchr.c" yymsp[-2].minor.yy235 = yylhsminor.yy235; break; case 3: /* statement ::= */ #line 569 "pikchr.y" { yymsp[1].minor.yy162 = 0; } #line 2480 "pikchr.c" break; case 4: /* statement ::= direction */ #line 570 "pikchr.y" { pik_set_direction(p,yymsp[0].minor.yy0.eCode); yylhsminor.yy162=0; } #line 2485 "pikchr.c" yymsp[0].minor.yy162 = yylhsminor.yy162; break; case 5: /* statement ::= lvalue ASSIGN rvalue */ #line 571 "pikchr.y" {pik_set_var(p,&yymsp[-2].minor.yy0,yymsp[0].minor.yy21,&yymsp[-1].minor.yy0); yylhsminor.yy162=0;} #line 2491 "pikchr.c" yymsp[-2].minor.yy162 = yylhsminor.yy162; break; case 6: /* statement ::= PLACENAME COLON unnamed_statement */ #line 573 "pikchr.y" { yylhsminor.yy162 = yymsp[0].minor.yy162; pik_elem_setname(p,yymsp[0].minor.yy162,&yymsp[-2].minor.yy0); } #line 2497 "pikchr.c" yymsp[-2].minor.yy162 = yylhsminor.yy162; break; case 7: /* statement ::= PLACENAME COLON position */ #line 575 "pikchr.y" { yylhsminor.yy162 = pik_elem_new(p,0,0,0); if(yylhsminor.yy162){ yylhsminor.yy162->ptAt = yymsp[0].minor.yy63; pik_elem_setname(p,yylhsminor.yy162,&yymsp[-2].minor.yy0); }} #line 2504 "pikchr.c" yymsp[-2].minor.yy162 = yylhsminor.yy162; break; case 8: /* statement ::= unnamed_statement */ #line 577 "pikchr.y" {yylhsminor.yy162 = yymsp[0].minor.yy162;} #line 2510 "pikchr.c" yymsp[0].minor.yy162 = yylhsminor.yy162; break; case 9: /* statement ::= print prlist */ #line 578 "pikchr.y" {pik_append(p,"
\n",5); yymsp[-1].minor.yy162=0;} #line 2516 "pikchr.c" break; case 10: /* statement ::= ASSERT LP expr EQ expr RP */ #line 583 "pikchr.y" {yymsp[-5].minor.yy162=pik_assert(p,yymsp[-3].minor.yy21,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy21);} #line 2521 "pikchr.c" break; case 11: /* statement ::= ASSERT LP position EQ position RP */ #line 585 "pikchr.y" {yymsp[-5].minor.yy162=pik_position_assert(p,&yymsp[-3].minor.yy63,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy63);} #line 2526 "pikchr.c" break; case 12: /* statement ::= DEFINE ID CODEBLOCK */ #line 586 "pikchr.y" {yymsp[-2].minor.yy162=0; pik_add_macro(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);} #line 2531 "pikchr.c" break; case 13: /* rvalue ::= PLACENAME */ #line 597 "pikchr.y" {yylhsminor.yy21 = pik_lookup_color(p,&yymsp[0].minor.yy0);} #line 2536 "pikchr.c" yymsp[0].minor.yy21 = yylhsminor.yy21; break; case 14: /* pritem ::= FILL */ case 15: /* pritem ::= COLOR */ yytestcase(yyruleno==15); case 16: /* pritem ::= THICKNESS */ yytestcase(yyruleno==16); #line 602 "pikchr.y" {pik_append_num(p,"",pik_value(p,yymsp[0].minor.yy0.z,yymsp[0].minor.yy0.n,0));} #line 2544 "pikchr.c" break; case 17: /* pritem ::= rvalue */ #line 605 "pikchr.y" {pik_append_num(p,"",yymsp[0].minor.yy21);} #line 2549 "pikchr.c" break; case 18: /* pritem ::= STRING */ #line 606 "pikchr.y" {pik_append_text(p,yymsp[0].minor.yy0.z+1,yymsp[0].minor.yy0.n-2,0);} #line 2554 "pikchr.c" break; case 19: /* prsep ::= COMMA */ #line 607 "pikchr.y" {pik_append(p, " ", 1);} #line 2559 "pikchr.c" break; case 20: /* unnamed_statement ::= basetype attribute_list */ #line 610 "pikchr.y" {yylhsminor.yy162 = yymsp[-1].minor.yy162; pik_after_adding_attributes(p,yylhsminor.yy162);} #line 2564 "pikchr.c" yymsp[-1].minor.yy162 = yylhsminor.yy162; break; case 21: /* basetype ::= CLASSNAME */ #line 612 "pikchr.y" {yylhsminor.yy162 = pik_elem_new(p,&yymsp[0].minor.yy0,0,0); } #line 2570 "pikchr.c" yymsp[0].minor.yy162 = yylhsminor.yy162; break; case 22: /* basetype ::= STRING textposition */ #line 614 "pikchr.y" {yymsp[-1].minor.yy0.eCode = yymsp[0].minor.yy188; yylhsminor.yy162 = pik_elem_new(p,0,&yymsp[-1].minor.yy0,0); } #line 2576 "pikchr.c" yymsp[-1].minor.yy162 = yylhsminor.yy162; break; case 23: /* basetype ::= LB savelist statement_list RB */ #line 616 "pikchr.y" { p->list = yymsp[-2].minor.yy235; yymsp[-3].minor.yy162 = pik_elem_new(p,0,0,yymsp[-1].minor.yy235); if(yymsp[-3].minor.yy162) yymsp[-3].minor.yy162->errTok = yymsp[0].minor.yy0; } #line 2582 "pikchr.c" break; case 24: /* savelist ::= */ #line 621 "pikchr.y" {yymsp[1].minor.yy235 = p->list; p->list = 0;} #line 2587 "pikchr.c" break; case 25: /* relexpr ::= expr */ #line 628 "pikchr.y" {yylhsminor.yy72.rAbs = yymsp[0].minor.yy21; yylhsminor.yy72.rRel = 0;} #line 2592 "pikchr.c" yymsp[0].minor.yy72 = yylhsminor.yy72; break; case 26: /* relexpr ::= expr PERCENT */ #line 629 "pikchr.y" {yylhsminor.yy72.rAbs = 0; yylhsminor.yy72.rRel = yymsp[-1].minor.yy21/100;} #line 2598 "pikchr.c" yymsp[-1].minor.yy72 = yylhsminor.yy72; break; case 27: /* optrelexpr ::= */ #line 631 "pikchr.y" {yymsp[1].minor.yy72.rAbs = 0; yymsp[1].minor.yy72.rRel = 1.0;} #line 2604 "pikchr.c" break; case 28: /* attribute_list ::= relexpr alist */ #line 633 "pikchr.y" {pik_add_direction(p,0,&yymsp[-1].minor.yy72);} #line 2609 "pikchr.c" break; case 29: /* attribute ::= numproperty relexpr */ #line 637 "pikchr.y" { pik_set_numprop(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy72); } #line 2614 "pikchr.c" break; case 30: /* attribute ::= dashproperty expr */ #line 638 "pikchr.y" { pik_set_dashed(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy21); } #line 2619 "pikchr.c" break; case 31: /* attribute ::= dashproperty */ #line 639 "pikchr.y" { pik_set_dashed(p,&yymsp[0].minor.yy0,0); } #line 2624 "pikchr.c" break; case 32: /* attribute ::= colorproperty rvalue */ #line 640 "pikchr.y" { pik_set_clrprop(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy21); } #line 2629 "pikchr.c" break; case 33: /* attribute ::= go direction optrelexpr */ #line 641 "pikchr.y" { pik_add_direction(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy72);} #line 2634 "pikchr.c" break; case 34: /* attribute ::= go direction even position */ #line 642 "pikchr.y" {pik_evenwith(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy63);} #line 2639 "pikchr.c" break; case 35: /* attribute ::= CLOSE */ #line 643 "pikchr.y" { pik_close_path(p,&yymsp[0].minor.yy0); } #line 2644 "pikchr.c" break; case 36: /* attribute ::= CHOP */ #line 644 "pikchr.y" { p->cur->bChop = 1; } #line 2649 "pikchr.c" break; case 37: /* attribute ::= FROM position */ #line 645 "pikchr.y" { pik_set_from(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy63); } #line 2654 "pikchr.c" break; case 38: /* attribute ::= TO position */ #line 646 "pikchr.y" { pik_add_to(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy63); } #line 2659 "pikchr.c" break; case 39: /* attribute ::= THEN */ #line 647 "pikchr.y" { pik_then(p, &yymsp[0].minor.yy0, p->cur); } #line 2664 "pikchr.c" break; case 40: /* attribute ::= THEN optrelexpr HEADING expr */ case 42: /* attribute ::= GO optrelexpr HEADING expr */ yytestcase(yyruleno==42); #line 649 "pikchr.y" {pik_move_hdg(p,&yymsp[-2].minor.yy72,&yymsp[-1].minor.yy0,yymsp[0].minor.yy21,0,&yymsp[-3].minor.yy0);} #line 2670 "pikchr.c" break; case 41: /* attribute ::= THEN optrelexpr EDGEPT */ case 43: /* attribute ::= GO optrelexpr EDGEPT */ yytestcase(yyruleno==43); #line 650 "pikchr.y" {pik_move_hdg(p,&yymsp[-1].minor.yy72,0,0,&yymsp[0].minor.yy0,&yymsp[-2].minor.yy0);} #line 2676 "pikchr.c" break; case 44: /* attribute ::= AT position */ #line 655 "pikchr.y" { pik_set_at(p,0,&yymsp[0].minor.yy63,&yymsp[-1].minor.yy0); } #line 2681 "pikchr.c" break; case 45: /* attribute ::= SAME */ #line 657 "pikchr.y" {pik_same(p,0,&yymsp[0].minor.yy0);} #line 2686 "pikchr.c" break; case 46: /* attribute ::= SAME AS object */ #line 658 "pikchr.y" {pik_same(p,yymsp[0].minor.yy162,&yymsp[-2].minor.yy0);} #line 2691 "pikchr.c" break; case 47: /* attribute ::= STRING textposition */ #line 659 "pikchr.y" {pik_add_txt(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy188);} #line 2696 "pikchr.c" break; case 48: /* attribute ::= FIT */ #line 660 "pikchr.y" {pik_size_to_fit(p,&yymsp[0].minor.yy0,3); } #line 2701 "pikchr.c" break; case 49: /* attribute ::= BEHIND object */ #line 661 "pikchr.y" {pik_behind(p,yymsp[0].minor.yy162);} #line 2706 "pikchr.c" break; case 50: /* withclause ::= DOT_E edge AT position */ case 51: /* withclause ::= edge AT position */ yytestcase(yyruleno==51); #line 669 "pikchr.y" { pik_set_at(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy63,&yymsp[-1].minor.yy0); } #line 2712 "pikchr.c" break; case 52: /* numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */ #line 673 "pikchr.y" {yylhsminor.yy0 = yymsp[0].minor.yy0;} #line 2717 "pikchr.c" yymsp[0].minor.yy0 = yylhsminor.yy0; break; case 53: /* boolproperty ::= CW */ #line 684 "pikchr.y" {p->cur->cw = 1;} #line 2723 "pikchr.c" break; case 54: /* boolproperty ::= CCW */ #line 685 "pikchr.y" {p->cur->cw = 0;} #line 2728 "pikchr.c" break; case 55: /* boolproperty ::= LARROW */ #line 686 "pikchr.y" {p->cur->larrow=1; p->cur->rarrow=0; } #line 2733 "pikchr.c" break; case 56: /* boolproperty ::= RARROW */ #line 687 "pikchr.y" {p->cur->larrow=0; p->cur->rarrow=1; } #line 2738 "pikchr.c" break; case 57: /* boolproperty ::= LRARROW */ #line 688 "pikchr.y" {p->cur->larrow=1; p->cur->rarrow=1; } #line 2743 "pikchr.c" break; case 58: /* boolproperty ::= INVIS */ #line 689 "pikchr.y" {p->cur->sw = -0.00001;} #line 2748 "pikchr.c" break; case 59: /* boolproperty ::= THICK */ #line 690 "pikchr.y" {p->cur->sw *= 1.5;} #line 2753 "pikchr.c" break; case 60: /* boolproperty ::= THIN */ #line 691 "pikchr.y" {p->cur->sw *= 0.67;} #line 2758 "pikchr.c" break; case 61: /* boolproperty ::= SOLID */ #line 692 "pikchr.y" {p->cur->sw = pik_value(p,"thickness",9,0); p->cur->dotted = p->cur->dashed = 0.0;} #line 2764 "pikchr.c" break; case 62: /* textposition ::= */ #line 695 "pikchr.y" {yymsp[1].minor.yy188 = 0;} #line 2769 "pikchr.c" break; case 63: /* textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL */ #line 698 "pikchr.y" {yylhsminor.yy188 = (short int)pik_text_position(yymsp[-1].minor.yy188,&yymsp[0].minor.yy0);} #line 2774 "pikchr.c" yymsp[-1].minor.yy188 = yylhsminor.yy188; break; case 64: /* position ::= expr COMMA expr */ #line 701 "pikchr.y" {yylhsminor.yy63.x=yymsp[-2].minor.yy21; yylhsminor.yy63.y=yymsp[0].minor.yy21;} #line 2780 "pikchr.c" yymsp[-2].minor.yy63 = yylhsminor.yy63; break; case 65: /* position ::= place PLUS expr COMMA expr */ #line 703 "pikchr.y" {yylhsminor.yy63.x=yymsp[-4].minor.yy63.x+yymsp[-2].minor.yy21; yylhsminor.yy63.y=yymsp[-4].minor.yy63.y+yymsp[0].minor.yy21;} #line 2786 "pikchr.c" yymsp[-4].minor.yy63 = yylhsminor.yy63; break; case 66: /* position ::= place MINUS expr COMMA expr */ #line 704 "pikchr.y" {yylhsminor.yy63.x=yymsp[-4].minor.yy63.x-yymsp[-2].minor.yy21; yylhsminor.yy63.y=yymsp[-4].minor.yy63.y-yymsp[0].minor.yy21;} #line 2792 "pikchr.c" yymsp[-4].minor.yy63 = yylhsminor.yy63; break; case 67: /* position ::= place PLUS LP expr COMMA expr RP */ #line 706 "pikchr.y" {yylhsminor.yy63.x=yymsp[-6].minor.yy63.x+yymsp[-3].minor.yy21; yylhsminor.yy63.y=yymsp[-6].minor.yy63.y+yymsp[-1].minor.yy21;} #line 2798 "pikchr.c" yymsp[-6].minor.yy63 = yylhsminor.yy63; break; case 68: /* position ::= place MINUS LP expr COMMA expr RP */ #line 708 "pikchr.y" {yylhsminor.yy63.x=yymsp[-6].minor.yy63.x-yymsp[-3].minor.yy21; yylhsminor.yy63.y=yymsp[-6].minor.yy63.y-yymsp[-1].minor.yy21;} #line 2804 "pikchr.c" yymsp[-6].minor.yy63 = yylhsminor.yy63; break; case 69: /* position ::= LP position COMMA position RP */ #line 709 "pikchr.y" {yymsp[-4].minor.yy63.x=yymsp[-3].minor.yy63.x; yymsp[-4].minor.yy63.y=yymsp[-1].minor.yy63.y;} #line 2810 "pikchr.c" break; case 70: /* position ::= LP position RP */ #line 710 "pikchr.y" {yymsp[-2].minor.yy63=yymsp[-1].minor.yy63;} #line 2815 "pikchr.c" break; case 71: /* position ::= expr between position AND position */ #line 712 "pikchr.y" {yylhsminor.yy63 = pik_position_between(yymsp[-4].minor.yy21,yymsp[-2].minor.yy63,yymsp[0].minor.yy63);} #line 2820 "pikchr.c" yymsp[-4].minor.yy63 = yylhsminor.yy63; break; case 72: /* position ::= expr LT position COMMA position GT */ #line 714 "pikchr.y" {yylhsminor.yy63 = pik_position_between(yymsp[-5].minor.yy21,yymsp[-3].minor.yy63,yymsp[-1].minor.yy63);} #line 2826 "pikchr.c" yymsp[-5].minor.yy63 = yylhsminor.yy63; break; case 73: /* position ::= expr ABOVE position */ #line 715 "pikchr.y" {yylhsminor.yy63=yymsp[0].minor.yy63; yylhsminor.yy63.y += yymsp[-2].minor.yy21;} #line 2832 "pikchr.c" yymsp[-2].minor.yy63 = yylhsminor.yy63; break; case 74: /* position ::= expr BELOW position */ #line 716 "pikchr.y" {yylhsminor.yy63=yymsp[0].minor.yy63; yylhsminor.yy63.y -= yymsp[-2].minor.yy21;} #line 2838 "pikchr.c" yymsp[-2].minor.yy63 = yylhsminor.yy63; break; case 75: /* position ::= expr LEFT OF position */ #line 717 "pikchr.y" {yylhsminor.yy63=yymsp[0].minor.yy63; yylhsminor.yy63.x -= yymsp[-3].minor.yy21;} #line 2844 "pikchr.c" yymsp[-3].minor.yy63 = yylhsminor.yy63; break; case 76: /* position ::= expr RIGHT OF position */ #line 718 "pikchr.y" {yylhsminor.yy63=yymsp[0].minor.yy63; yylhsminor.yy63.x += yymsp[-3].minor.yy21;} #line 2850 "pikchr.c" yymsp[-3].minor.yy63 = yylhsminor.yy63; break; case 77: /* position ::= expr ON HEADING EDGEPT OF position */ #line 720 "pikchr.y" {yylhsminor.yy63 = pik_position_at_hdg(yymsp[-5].minor.yy21,&yymsp[-2].minor.yy0,yymsp[0].minor.yy63);} #line 2856 "pikchr.c" yymsp[-5].minor.yy63 = yylhsminor.yy63; break; case 78: /* position ::= expr HEADING EDGEPT OF position */ #line 722 "pikchr.y" {yylhsminor.yy63 = pik_position_at_hdg(yymsp[-4].minor.yy21,&yymsp[-2].minor.yy0,yymsp[0].minor.yy63);} #line 2862 "pikchr.c" yymsp[-4].minor.yy63 = yylhsminor.yy63; break; case 79: /* position ::= expr EDGEPT OF position */ #line 724 "pikchr.y" {yylhsminor.yy63 = pik_position_at_hdg(yymsp[-3].minor.yy21,&yymsp[-2].minor.yy0,yymsp[0].minor.yy63);} #line 2868 "pikchr.c" yymsp[-3].minor.yy63 = yylhsminor.yy63; break; case 80: /* position ::= expr ON HEADING expr FROM position */ #line 726 "pikchr.y" {yylhsminor.yy63 = pik_position_at_angle(yymsp[-5].minor.yy21,yymsp[-2].minor.yy21,yymsp[0].minor.yy63);} #line 2874 "pikchr.c" yymsp[-5].minor.yy63 = yylhsminor.yy63; break; case 81: /* position ::= expr HEADING expr FROM position */ #line 728 "pikchr.y" {yylhsminor.yy63 = pik_position_at_angle(yymsp[-4].minor.yy21,yymsp[-2].minor.yy21,yymsp[0].minor.yy63);} #line 2880 "pikchr.c" yymsp[-4].minor.yy63 = yylhsminor.yy63; break; case 82: /* place ::= edge OF object */ #line 740 "pikchr.y" {yylhsminor.yy63 = pik_place_of_elem(p,yymsp[0].minor.yy162,&yymsp[-2].minor.yy0);} #line 2886 "pikchr.c" yymsp[-2].minor.yy63 = yylhsminor.yy63; break; case 83: /* place2 ::= object */ #line 741 "pikchr.y" {yylhsminor.yy63 = pik_place_of_elem(p,yymsp[0].minor.yy162,0);} #line 2892 "pikchr.c" yymsp[0].minor.yy63 = yylhsminor.yy63; break; case 84: /* place2 ::= object DOT_E edge */ #line 742 "pikchr.y" {yylhsminor.yy63 = pik_place_of_elem(p,yymsp[-2].minor.yy162,&yymsp[0].minor.yy0);} #line 2898 "pikchr.c" yymsp[-2].minor.yy63 = yylhsminor.yy63; break; case 85: /* place2 ::= NTH VERTEX OF object */ #line 743 "pikchr.y" {yylhsminor.yy63 = pik_nth_vertex(p,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,yymsp[0].minor.yy162);} #line 2904 "pikchr.c" yymsp[-3].minor.yy63 = yylhsminor.yy63; break; case 86: /* object ::= nth */ #line 755 "pikchr.y" {yylhsminor.yy162 = pik_find_nth(p,0,&yymsp[0].minor.yy0);} #line 2910 "pikchr.c" yymsp[0].minor.yy162 = yylhsminor.yy162; break; case 87: /* object ::= nth OF|IN object */ #line 756 "pikchr.y" {yylhsminor.yy162 = pik_find_nth(p,yymsp[0].minor.yy162,&yymsp[-2].minor.yy0);} #line 2916 "pikchr.c" yymsp[-2].minor.yy162 = yylhsminor.yy162; break; case 88: /* objectname ::= THIS */ #line 758 "pikchr.y" {yymsp[0].minor.yy162 = p->cur;} #line 2922 "pikchr.c" break; case 89: /* objectname ::= PLACENAME */ #line 759 "pikchr.y" {yylhsminor.yy162 = pik_find_byname(p,0,&yymsp[0].minor.yy0);} #line 2927 "pikchr.c" yymsp[0].minor.yy162 = yylhsminor.yy162; break; case 90: /* objectname ::= objectname DOT_U PLACENAME */ #line 761 "pikchr.y" {yylhsminor.yy162 = pik_find_byname(p,yymsp[-2].minor.yy162,&yymsp[0].minor.yy0);} #line 2933 "pikchr.c" yymsp[-2].minor.yy162 = yylhsminor.yy162; break; case 91: /* nth ::= NTH CLASSNAME */ #line 763 "pikchr.y" {yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = pik_nth_value(p,&yymsp[-1].minor.yy0); } #line 2939 "pikchr.c" yymsp[-1].minor.yy0 = yylhsminor.yy0; break; case 92: /* nth ::= NTH LAST CLASSNAME */ #line 764 "pikchr.y" {yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -pik_nth_value(p,&yymsp[-2].minor.yy0); } #line 2945 "pikchr.c" yymsp[-2].minor.yy0 = yylhsminor.yy0; break; case 93: /* nth ::= LAST CLASSNAME */ #line 765 "pikchr.y" {yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.eCode = -1;} #line 2951 "pikchr.c" break; case 94: /* nth ::= LAST */ #line 766 "pikchr.y" {yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -1;} #line 2956 "pikchr.c" yymsp[0].minor.yy0 = yylhsminor.yy0; break; case 95: /* nth ::= NTH LB RB */ #line 767 "pikchr.y" {yylhsminor.yy0=yymsp[-1].minor.yy0; yylhsminor.yy0.eCode = pik_nth_value(p,&yymsp[-2].minor.yy0);} #line 2962 "pikchr.c" yymsp[-2].minor.yy0 = yylhsminor.yy0; break; case 96: /* nth ::= NTH LAST LB RB */ #line 768 "pikchr.y" {yylhsminor.yy0=yymsp[-1].minor.yy0; yylhsminor.yy0.eCode = -pik_nth_value(p,&yymsp[-3].minor.yy0);} #line 2968 "pikchr.c" yymsp[-3].minor.yy0 = yylhsminor.yy0; break; case 97: /* nth ::= LAST LB RB */ #line 769 "pikchr.y" {yymsp[-2].minor.yy0=yymsp[-1].minor.yy0; yymsp[-2].minor.yy0.eCode = -1; } #line 2974 "pikchr.c" break; case 98: /* expr ::= expr PLUS expr */ #line 771 "pikchr.y" {yylhsminor.yy21=yymsp[-2].minor.yy21+yymsp[0].minor.yy21;} #line 2979 "pikchr.c" yymsp[-2].minor.yy21 = yylhsminor.yy21; break; case 99: /* expr ::= expr MINUS expr */ #line 772 "pikchr.y" {yylhsminor.yy21=yymsp[-2].minor.yy21-yymsp[0].minor.yy21;} #line 2985 "pikchr.c" yymsp[-2].minor.yy21 = yylhsminor.yy21; break; case 100: /* expr ::= expr STAR expr */ #line 773 "pikchr.y" {yylhsminor.yy21=yymsp[-2].minor.yy21*yymsp[0].minor.yy21;} #line 2991 "pikchr.c" yymsp[-2].minor.yy21 = yylhsminor.yy21; break; case 101: /* expr ::= expr SLASH expr */ #line 774 "pikchr.y" { if( yymsp[0].minor.yy21==0.0 ){ pik_error(p, &yymsp[-1].minor.yy0, "division by zero"); yylhsminor.yy21 = 0.0; } else{ yylhsminor.yy21 = yymsp[-2].minor.yy21/yymsp[0].minor.yy21; } } #line 3000 "pikchr.c" yymsp[-2].minor.yy21 = yylhsminor.yy21; break; case 102: /* expr ::= MINUS expr */ #line 778 "pikchr.y" {yymsp[-1].minor.yy21=-yymsp[0].minor.yy21;} #line 3006 "pikchr.c" break; case 103: /* expr ::= PLUS expr */ #line 779 "pikchr.y" {yymsp[-1].minor.yy21=yymsp[0].minor.yy21;} #line 3011 "pikchr.c" break; case 104: /* expr ::= LP expr RP */ #line 780 "pikchr.y" {yymsp[-2].minor.yy21=yymsp[-1].minor.yy21;} #line 3016 "pikchr.c" break; case 105: /* expr ::= LP FILL|COLOR|THICKNESS RP */ #line 781 "pikchr.y" {yymsp[-2].minor.yy21=pik_get_var(p,&yymsp[-1].minor.yy0);} #line 3021 "pikchr.c" break; case 106: /* expr ::= NUMBER */ #line 782 "pikchr.y" {yylhsminor.yy21=pik_atof(&yymsp[0].minor.yy0);} #line 3026 "pikchr.c" yymsp[0].minor.yy21 = yylhsminor.yy21; break; case 107: /* expr ::= ID */ #line 783 "pikchr.y" {yylhsminor.yy21=pik_get_var(p,&yymsp[0].minor.yy0);} #line 3032 "pikchr.c" yymsp[0].minor.yy21 = yylhsminor.yy21; break; case 108: /* expr ::= FUNC1 LP expr RP */ #line 784 "pikchr.y" {yylhsminor.yy21 = pik_func(p,&yymsp[-3].minor.yy0,yymsp[-1].minor.yy21,0.0);} #line 3038 "pikchr.c" yymsp[-3].minor.yy21 = yylhsminor.yy21; break; case 109: /* expr ::= FUNC2 LP expr COMMA expr RP */ #line 785 "pikchr.y" {yylhsminor.yy21 = pik_func(p,&yymsp[-5].minor.yy0,yymsp[-3].minor.yy21,yymsp[-1].minor.yy21);} #line 3044 "pikchr.c" yymsp[-5].minor.yy21 = yylhsminor.yy21; break; case 110: /* expr ::= DIST LP position COMMA position RP */ #line 786 "pikchr.y" {yymsp[-5].minor.yy21 = pik_dist(&yymsp[-3].minor.yy63,&yymsp[-1].minor.yy63);} #line 3050 "pikchr.c" break; case 111: /* expr ::= place2 DOT_XY X */ #line 787 "pikchr.y" {yylhsminor.yy21 = yymsp[-2].minor.yy63.x;} #line 3055 "pikchr.c" yymsp[-2].minor.yy21 = yylhsminor.yy21; break; case 112: /* expr ::= place2 DOT_XY Y */ #line 788 "pikchr.y" {yylhsminor.yy21 = yymsp[-2].minor.yy63.y;} #line 3061 "pikchr.c" yymsp[-2].minor.yy21 = yylhsminor.yy21; break; case 113: /* expr ::= object DOT_L numproperty */ case 114: /* expr ::= object DOT_L dashproperty */ yytestcase(yyruleno==114); case 115: /* expr ::= object DOT_L colorproperty */ yytestcase(yyruleno==115); #line 789 "pikchr.y" {yylhsminor.yy21=pik_property_of(yymsp[-2].minor.yy162,&yymsp[0].minor.yy0);} #line 3069 "pikchr.c" yymsp[-2].minor.yy21 = yylhsminor.yy21; break; default: /* (116) lvalue ::= ID */ yytestcase(yyruleno==116); /* (117) lvalue ::= FILL */ yytestcase(yyruleno==117); /* (118) lvalue ::= COLOR */ yytestcase(yyruleno==118); /* (119) lvalue ::= THICKNESS */ yytestcase(yyruleno==119); /* (120) rvalue ::= expr */ yytestcase(yyruleno==120); /* (121) print ::= PRINT */ yytestcase(yyruleno==121); /* (122) prlist ::= pritem (OPTIMIZED OUT) */ assert(yyruleno!=122); /* (123) prlist ::= prlist prsep pritem */ yytestcase(yyruleno==123); /* (124) direction ::= UP */ yytestcase(yyruleno==124); /* (125) direction ::= DOWN */ yytestcase(yyruleno==125); /* (126) direction ::= LEFT */ yytestcase(yyruleno==126); /* (127) direction ::= RIGHT */ yytestcase(yyruleno==127); /* (128) optrelexpr ::= relexpr (OPTIMIZED OUT) */ assert(yyruleno!=128); /* (129) attribute_list ::= alist */ yytestcase(yyruleno==129); /* (130) alist ::= */ yytestcase(yyruleno==130); /* (131) alist ::= alist attribute */ yytestcase(yyruleno==131); /* (132) attribute ::= boolproperty (OPTIMIZED OUT) */ assert(yyruleno!=132); /* (133) attribute ::= WITH withclause */ yytestcase(yyruleno==133); /* (134) go ::= GO */ yytestcase(yyruleno==134); /* (135) go ::= */ yytestcase(yyruleno==135); /* (136) even ::= UNTIL EVEN WITH */ yytestcase(yyruleno==136); /* (137) even ::= EVEN WITH */ yytestcase(yyruleno==137); /* (138) dashproperty ::= DOTTED */ yytestcase(yyruleno==138); /* (139) dashproperty ::= DASHED */ yytestcase(yyruleno==139); /* (140) colorproperty ::= FILL */ yytestcase(yyruleno==140); /* (141) colorproperty ::= COLOR */ yytestcase(yyruleno==141); /* (142) position ::= place */ yytestcase(yyruleno==142); /* (143) between ::= WAY BETWEEN */ yytestcase(yyruleno==143); /* (144) between ::= BETWEEN */ yytestcase(yyruleno==144); /* (145) between ::= OF THE WAY BETWEEN */ yytestcase(yyruleno==145); /* (146) place ::= place2 */ yytestcase(yyruleno==146); /* (147) edge ::= CENTER */ yytestcase(yyruleno==147); /* (148) edge ::= EDGEPT */ yytestcase(yyruleno==148); /* (149) edge ::= TOP */ yytestcase(yyruleno==149); /* (150) edge ::= BOTTOM */ yytestcase(yyruleno==150); /* (151) edge ::= START */ yytestcase(yyruleno==151); /* (152) edge ::= END */ yytestcase(yyruleno==152); /* (153) edge ::= RIGHT */ yytestcase(yyruleno==153); /* (154) edge ::= LEFT */ yytestcase(yyruleno==154); /* (155) object ::= objectname */ yytestcase(yyruleno==155); break; /********** End reduce actions ************************************************/ }; assert( yyrulenoYY_MAX_SHIFT && yyact<=YY_MAX_SHIFTREDUCE) ); /* It is not possible for a REDUCE to be followed by an error */ assert( yyact!=YY_ERROR_ACTION ); yymsp += yysize+1; yypParser->yytos = yymsp; yymsp->stateno = (YYACTIONTYPE)yyact; yymsp->major = (YYCODETYPE)yygoto; yyTraceShift(yypParser, yyact, "... then shift"); return yyact; } /* ** The following code executes when the parse fails */ #ifndef YYNOERRORRECOVERY static void yy_parse_failed( yyParser *yypParser /* The parser */ ){ pik_parserARG_FETCH pik_parserCTX_FETCH #ifndef NDEBUG if( yyTraceFILE ){ fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt); } #endif while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser); /* Here code is inserted which will be executed whenever the ** parser fails */ /************ Begin %parse_failure code ***************************************/ /************ End %parse_failure code *****************************************/ pik_parserARG_STORE /* Suppress warning about unused %extra_argument variable */ pik_parserCTX_STORE } #endif /* YYNOERRORRECOVERY */ /* ** The following code executes when a syntax error first occurs. */ static void yy_syntax_error( yyParser *yypParser, /* The parser */ int yymajor, /* The major type of the error token */ pik_parserTOKENTYPE yyminor /* The minor type of the error token */ ){ pik_parserARG_FETCH pik_parserCTX_FETCH #define TOKEN yyminor /************ Begin %syntax_error code ****************************************/ #line 549 "pikchr.y" if( TOKEN.z && TOKEN.z[0] ){ pik_error(p, &TOKEN, "syntax error"); }else{ pik_error(p, 0, "syntax error"); } UNUSED_PARAMETER(yymajor); #line 3180 "pikchr.c" /************ End %syntax_error code ******************************************/ pik_parserARG_STORE /* Suppress warning about unused %extra_argument variable */ pik_parserCTX_STORE } /* ** The following is executed when the parser accepts */ static void yy_accept( yyParser *yypParser /* The parser */ ){ pik_parserARG_FETCH pik_parserCTX_FETCH #ifndef NDEBUG if( yyTraceFILE ){ fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt); } #endif #ifndef YYNOERRORRECOVERY yypParser->yyerrcnt = -1; #endif assert( yypParser->yytos==yypParser->yystack ); /* Here code is inserted which will be executed whenever the ** parser accepts */ /*********** Begin %parse_accept code *****************************************/ /*********** End %parse_accept code *******************************************/ pik_parserARG_STORE /* Suppress warning about unused %extra_argument variable */ pik_parserCTX_STORE } /* The main parser program. ** The first argument is a pointer to a structure obtained from ** "pik_parserAlloc" which describes the current state of the parser. ** The second argument is the major token number. The third is ** the minor token. The fourth optional argument is whatever the ** user wants (and specified in the grammar) and is available for ** use by the action routines. ** ** Inputs: **
    **
  • A pointer to the parser (an opaque structure.) **
  • The major token number. **
  • The minor token number. **
  • An option argument of a grammar-specified type. **
** ** Outputs: ** None. */ void pik_parser( void *yyp, /* The parser */ int yymajor, /* The major token code number */ pik_parserTOKENTYPE yyminor /* The value for the token */ pik_parserARG_PDECL /* Optional %extra_argument parameter */ ){ YYMINORTYPE yyminorunion; YYACTIONTYPE yyact; /* The parser action. */ #if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY) int yyendofinput; /* True if we are at the end of input */ #endif #ifdef YYERRORSYMBOL int yyerrorhit = 0; /* True if yymajor has invoked an error */ #endif yyParser *yypParser = (yyParser*)yyp; /* The parser */ pik_parserCTX_FETCH pik_parserARG_STORE assert( yypParser->yytos!=0 ); #if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY) yyendofinput = (yymajor==0); #endif yyact = yypParser->yytos->stateno; #ifndef NDEBUG if( yyTraceFILE ){ if( yyact < YY_MIN_REDUCE ){ fprintf(yyTraceFILE,"%sInput '%s' in state %d\n", yyTracePrompt,yyTokenName[yymajor],yyact); }else{ fprintf(yyTraceFILE,"%sInput '%s' with pending reduce %d\n", yyTracePrompt,yyTokenName[yymajor],yyact-YY_MIN_REDUCE); } } #endif while(1){ /* Exit by "break" */ assert( yypParser->yytos>=yypParser->yystack ); assert( yyact==yypParser->yytos->stateno ); yyact = yy_find_shift_action((YYCODETYPE)yymajor,yyact); if( yyact >= YY_MIN_REDUCE ){ unsigned int yyruleno = yyact - YY_MIN_REDUCE; /* Reduce by this rule */ #ifndef NDEBUG assert( yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ); if( yyTraceFILE ){ int yysize = yyRuleInfoNRhs[yyruleno]; if( yysize ){ fprintf(yyTraceFILE, "%sReduce %d [%s]%s, pop back to state %d.\n", yyTracePrompt, yyruleno, yyRuleName[yyruleno], yyrulenoyytos[yysize].stateno); }else{ fprintf(yyTraceFILE, "%sReduce %d [%s]%s.\n", yyTracePrompt, yyruleno, yyRuleName[yyruleno], yyrulenoyytos - yypParser->yystack)>yypParser->yyhwm ){ yypParser->yyhwm++; assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack)); } #endif if( yypParser->yytos>=yypParser->yystackEnd ){ if( yyGrowStack(yypParser) ){ yyStackOverflow(yypParser); break; } } } yyact = yy_reduce(yypParser,yyruleno,yymajor,yyminor pik_parserCTX_PARAM); }else if( yyact <= YY_MAX_SHIFTREDUCE ){ yy_shift(yypParser,yyact,(YYCODETYPE)yymajor,yyminor); #ifndef YYNOERRORRECOVERY yypParser->yyerrcnt--; #endif break; }else if( yyact==YY_ACCEPT_ACTION ){ yypParser->yytos--; yy_accept(yypParser); return; }else{ assert( yyact == YY_ERROR_ACTION ); yyminorunion.yy0 = yyminor; #ifdef YYERRORSYMBOL int yymx; #endif #ifndef NDEBUG if( yyTraceFILE ){ fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt); } #endif #ifdef YYERRORSYMBOL /* A syntax error has occurred. ** The response to an error depends upon whether or not the ** grammar defines an error token "ERROR". ** ** This is what we do if the grammar does define ERROR: ** ** * Call the %syntax_error function. ** ** * Begin popping the stack until we enter a state where ** it is legal to shift the error symbol, then shift ** the error symbol. ** ** * Set the error count to three. ** ** * Begin accepting and shifting new tokens. No new error ** processing will occur until three tokens have been ** shifted successfully. ** */ if( yypParser->yyerrcnt<0 ){ yy_syntax_error(yypParser,yymajor,yyminor); } yymx = yypParser->yytos->major; if( yymx==YYERRORSYMBOL || yyerrorhit ){ #ifndef NDEBUG if( yyTraceFILE ){ fprintf(yyTraceFILE,"%sDiscard input token %s\n", yyTracePrompt,yyTokenName[yymajor]); } #endif yy_destructor(yypParser, (YYCODETYPE)yymajor, &yyminorunion); yymajor = YYNOCODE; }else{ while( yypParser->yytos > yypParser->yystack ){ yyact = yy_find_reduce_action(yypParser->yytos->stateno, YYERRORSYMBOL); if( yyact<=YY_MAX_SHIFTREDUCE ) break; yy_pop_parser_stack(yypParser); } if( yypParser->yytos <= yypParser->yystack || yymajor==0 ){ yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); yy_parse_failed(yypParser); #ifndef YYNOERRORRECOVERY yypParser->yyerrcnt = -1; #endif yymajor = YYNOCODE; }else if( yymx!=YYERRORSYMBOL ){ yy_shift(yypParser,yyact,YYERRORSYMBOL,yyminor); } } yypParser->yyerrcnt = 3; yyerrorhit = 1; if( yymajor==YYNOCODE ) break; yyact = yypParser->yytos->stateno; #elif defined(YYNOERRORRECOVERY) /* If the YYNOERRORRECOVERY macro is defined, then do not attempt to ** do any kind of error recovery. Instead, simply invoke the syntax ** error routine and continue going as if nothing had happened. ** ** Applications can set this macro (for example inside %include) if ** they intend to abandon the parse upon the first syntax error seen. */ yy_syntax_error(yypParser,yymajor, yyminor); yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); break; #else /* YYERRORSYMBOL is not defined */ /* This is what we do if the grammar does not define ERROR: ** ** * Report an error message, and throw away the input token. ** ** * If the input token is $, then fail the parse. ** ** As before, subsequent error messages are suppressed until ** three input tokens have been successfully shifted. */ if( yypParser->yyerrcnt<=0 ){ yy_syntax_error(yypParser,yymajor, yyminor); } yypParser->yyerrcnt = 3; yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); if( yyendofinput ){ yy_parse_failed(yypParser); #ifndef YYNOERRORRECOVERY yypParser->yyerrcnt = -1; #endif } break; #endif } } #ifndef NDEBUG if( yyTraceFILE ){ yyStackEntry *i; char cDiv = '['; fprintf(yyTraceFILE,"%sReturn. Stack=",yyTracePrompt); for(i=&yypParser->yystack[1]; i<=yypParser->yytos; i++){ fprintf(yyTraceFILE,"%c%s", cDiv, yyTokenName[i->major]); cDiv = ' '; } fprintf(yyTraceFILE,"]\n"); } #endif return; } /* ** Return the fallback token corresponding to canonical token iToken, or ** 0 if iToken has no fallback. */ int pik_parserFallback(int iToken){ #ifdef YYFALLBACK assert( iToken<(int)(sizeof(yyFallback)/sizeof(yyFallback[0])) ); return yyFallback[iToken]; #else (void)iToken; return 0; #endif } #line 794 "pikchr.y" /* Chart of the 148 official CSS color names with their ** corresponding RGB values thru Color Module Level 4: ** https://developer.mozilla.org/en-US/docs/Web/CSS/color_value ** ** Two new names "None" and "Off" are added with a value ** of -1. */ static const struct { const char *zName; /* Name of the color */ int val; /* RGB value */ } aColor[] = { { "AliceBlue", 0xf0f8ff }, { "AntiqueWhite", 0xfaebd7 }, { "Aqua", 0x00ffff }, { "Aquamarine", 0x7fffd4 }, { "Azure", 0xf0ffff }, { "Beige", 0xf5f5dc }, { "Bisque", 0xffe4c4 }, { "Black", 0x000000 }, { "BlanchedAlmond", 0xffebcd }, { "Blue", 0x0000ff }, { "BlueViolet", 0x8a2be2 }, { "Brown", 0xa52a2a }, { "BurlyWood", 0xdeb887 }, { "CadetBlue", 0x5f9ea0 }, { "Chartreuse", 0x7fff00 }, { "Chocolate", 0xd2691e }, { "Coral", 0xff7f50 }, { "CornflowerBlue", 0x6495ed }, { "Cornsilk", 0xfff8dc }, { "Crimson", 0xdc143c }, { "Cyan", 0x00ffff }, { "DarkBlue", 0x00008b }, { "DarkCyan", 0x008b8b }, { "DarkGoldenrod", 0xb8860b }, { "DarkGray", 0xa9a9a9 }, { "DarkGreen", 0x006400 }, { "DarkGrey", 0xa9a9a9 }, { "DarkKhaki", 0xbdb76b }, { "DarkMagenta", 0x8b008b }, { "DarkOliveGreen", 0x556b2f }, { "DarkOrange", 0xff8c00 }, { "DarkOrchid", 0x9932cc }, { "DarkRed", 0x8b0000 }, { "DarkSalmon", 0xe9967a }, { "DarkSeaGreen", 0x8fbc8f }, { "DarkSlateBlue", 0x483d8b }, { "DarkSlateGray", 0x2f4f4f }, { "DarkSlateGrey", 0x2f4f4f }, { "DarkTurquoise", 0x00ced1 }, { "DarkViolet", 0x9400d3 }, { "DeepPink", 0xff1493 }, { "DeepSkyBlue", 0x00bfff }, { "DimGray", 0x696969 }, { "DimGrey", 0x696969 }, { "DodgerBlue", 0x1e90ff }, { "Firebrick", 0xb22222 }, { "FloralWhite", 0xfffaf0 }, { "ForestGreen", 0x228b22 }, { "Fuchsia", 0xff00ff }, { "Gainsboro", 0xdcdcdc }, { "GhostWhite", 0xf8f8ff }, { "Gold", 0xffd700 }, { "Goldenrod", 0xdaa520 }, { "Gray", 0x808080 }, { "Green", 0x008000 }, { "GreenYellow", 0xadff2f }, { "Grey", 0x808080 }, { "Honeydew", 0xf0fff0 }, { "HotPink", 0xff69b4 }, { "IndianRed", 0xcd5c5c }, { "Indigo", 0x4b0082 }, { "Ivory", 0xfffff0 }, { "Khaki", 0xf0e68c }, { "Lavender", 0xe6e6fa }, { "LavenderBlush", 0xfff0f5 }, { "LawnGreen", 0x7cfc00 }, { "LemonChiffon", 0xfffacd }, { "LightBlue", 0xadd8e6 }, { "LightCoral", 0xf08080 }, { "LightCyan", 0xe0ffff }, { "LightGoldenrodYellow", 0xfafad2 }, { "LightGray", 0xd3d3d3 }, { "LightGreen", 0x90ee90 }, { "LightGrey", 0xd3d3d3 }, { "LightPink", 0xffb6c1 }, { "LightSalmon", 0xffa07a }, { "LightSeaGreen", 0x20b2aa }, { "LightSkyBlue", 0x87cefa }, { "LightSlateGray", 0x778899 }, { "LightSlateGrey", 0x778899 }, { "LightSteelBlue", 0xb0c4de }, { "LightYellow", 0xffffe0 }, { "Lime", 0x00ff00 }, { "LimeGreen", 0x32cd32 }, { "Linen", 0xfaf0e6 }, { "Magenta", 0xff00ff }, { "Maroon", 0x800000 }, { "MediumAquamarine", 0x66cdaa }, { "MediumBlue", 0x0000cd }, { "MediumOrchid", 0xba55d3 }, { "MediumPurple", 0x9370db }, { "MediumSeaGreen", 0x3cb371 }, { "MediumSlateBlue", 0x7b68ee }, { "MediumSpringGreen", 0x00fa9a }, { "MediumTurquoise", 0x48d1cc }, { "MediumVioletRed", 0xc71585 }, { "MidnightBlue", 0x191970 }, { "MintCream", 0xf5fffa }, { "MistyRose", 0xffe4e1 }, { "Moccasin", 0xffe4b5 }, { "NavajoWhite", 0xffdead }, { "Navy", 0x000080 }, { "None", -1 }, /* Non-standard addition */ { "Off", -1 }, /* Non-standard addition */ { "OldLace", 0xfdf5e6 }, { "Olive", 0x808000 }, { "OliveDrab", 0x6b8e23 }, { "Orange", 0xffa500 }, { "OrangeRed", 0xff4500 }, { "Orchid", 0xda70d6 }, { "PaleGoldenrod", 0xeee8aa }, { "PaleGreen", 0x98fb98 }, { "PaleTurquoise", 0xafeeee }, { "PaleVioletRed", 0xdb7093 }, { "PapayaWhip", 0xffefd5 }, { "PeachPuff", 0xffdab9 }, { "Peru", 0xcd853f }, { "Pink", 0xffc0cb }, { "Plum", 0xdda0dd }, { "PowderBlue", 0xb0e0e6 }, { "Purple", 0x800080 }, { "RebeccaPurple", 0x663399 }, { "Red", 0xff0000 }, { "RosyBrown", 0xbc8f8f }, { "RoyalBlue", 0x4169e1 }, { "SaddleBrown", 0x8b4513 }, { "Salmon", 0xfa8072 }, { "SandyBrown", 0xf4a460 }, { "SeaGreen", 0x2e8b57 }, { "Seashell", 0xfff5ee }, { "Sienna", 0xa0522d }, { "Silver", 0xc0c0c0 }, { "SkyBlue", 0x87ceeb }, { "SlateBlue", 0x6a5acd }, { "SlateGray", 0x708090 }, { "SlateGrey", 0x708090 }, { "Snow", 0xfffafa }, { "SpringGreen", 0x00ff7f }, { "SteelBlue", 0x4682b4 }, { "Tan", 0xd2b48c }, { "Teal", 0x008080 }, { "Thistle", 0xd8bfd8 }, { "Tomato", 0xff6347 }, { "Turquoise", 0x40e0d0 }, { "Violet", 0xee82ee }, { "Wheat", 0xf5deb3 }, { "White", 0xffffff }, { "WhiteSmoke", 0xf5f5f5 }, { "Yellow", 0xffff00 }, { "YellowGreen", 0x9acd32 }, }; /* Built-in variable names. ** ** This array is constant. When a script changes the value of one of ** these built-ins, a new PVar record is added at the head of ** the Pik.pVar list, which is searched first. Thus the new PVar entry ** will override this default value. ** ** Units are in inches, except for "color" and "fill" which are ** interpreted as 24-bit RGB values. ** ** Binary search used. Must be kept in sorted order. */ static const struct { const char *zName; PNum val; } aBuiltin[] = { { "arcrad", 0.25 }, { "arrowhead", 2.0 }, { "arrowht", 0.08 }, { "arrowwid", 0.06 }, { "boxht", 0.5 }, { "boxrad", 0.0 }, { "boxwid", 0.75 }, { "charht", 0.14 }, { "charwid", 0.08 }, { "circlerad", 0.25 }, { "color", 0.0 }, { "cylht", 0.5 }, { "cylrad", 0.075 }, { "cylwid", 0.75 }, { "dashwid", 0.05 }, { "diamondht", 0.75 }, { "diamondwid", 1.0 }, { "dotrad", 0.015 }, { "ellipseht", 0.5 }, { "ellipsewid", 0.75 }, { "fileht", 0.75 }, { "filerad", 0.15 }, { "filewid", 0.5 }, { "fill", -1.0 }, { "lineht", 0.5 }, { "linewid", 0.5 }, { "movewid", 0.5 }, { "ovalht", 0.5 }, { "ovalwid", 1.0 }, { "scale", 1.0 }, { "textht", 0.5 }, { "textwid", 0.75 }, { "thickness", 0.015 }, }; /* Methods for the "arc" class */ static void arcInit(Pik *p, PObj *pObj){ pObj->w = pik_value(p, "arcrad",6,0); pObj->h = pObj->w; } /* Hack: Arcs are here rendered as quadratic Bezier curves rather ** than true arcs. Multiple reasons: (1) the legacy-PIC parameters ** that control arcs are obscure and I could not figure out what they ** mean based on available documentation. (2) Arcs are rarely used, ** and so do not seem that important. */ static PPoint arcControlPoint(int cw, PPoint f, PPoint t, PNum rScale){ PPoint m; PNum dx, dy; m.x = 0.5*(f.x+t.x); m.y = 0.5*(f.y+t.y); dx = t.x - f.x; dy = t.y - f.y; if( cw ){ m.x -= 0.5*rScale*dy; m.y += 0.5*rScale*dx; }else{ m.x += 0.5*rScale*dy; m.y -= 0.5*rScale*dx; } return m; } static void arcCheck(Pik *p, PObj *pObj){ PPoint m; if( p->nTPath>2 ){ pik_error(p, &pObj->errTok, "arc geometry error"); return; } m = arcControlPoint(pObj->cw, p->aTPath[0], p->aTPath[1], 0.5); pik_bbox_add_xy(&pObj->bbox, m.x, m.y); } static void arcRender(Pik *p, PObj *pObj){ PPoint f, m, t; if( pObj->nPath<2 ) return; if( pObj->sw<0.0 ) return; f = pObj->aPath[0]; t = pObj->aPath[1]; m = arcControlPoint(pObj->cw,f,t,1.0); if( pObj->larrow ){ pik_draw_arrowhead(p,&m,&f,pObj); } if( pObj->rarrow ){ pik_draw_arrowhead(p,&m,&t,pObj); } pik_append_xy(p,"\n", -1); pik_append_txt(p, pObj, 0); } /* Methods for the "arrow" class */ static void arrowInit(Pik *p, PObj *pObj){ pObj->w = pik_value(p, "linewid",7,0); pObj->h = pik_value(p, "lineht",6,0); pObj->rad = pik_value(p, "linerad",7,0); pObj->rarrow = 1; } /* Methods for the "box" class */ static void boxInit(Pik *p, PObj *pObj){ pObj->w = pik_value(p, "boxwid",6,0); pObj->h = pik_value(p, "boxht",5,0); pObj->rad = pik_value(p, "boxrad",6,0); } /* Return offset from the center of the box to the compass point ** given by parameter cp */ static PPoint boxOffset(Pik *p, PObj *pObj, int cp){ PPoint pt = cZeroPoint; PNum w2 = 0.5*pObj->w; PNum h2 = 0.5*pObj->h; PNum rad = pObj->rad; PNum rx; if( rad<=0.0 ){ rx = 0.0; }else{ if( rad>w2 ) rad = w2; if( rad>h2 ) rad = h2; rx = 0.29289321881345252392*rad; } switch( cp ){ case CP_C: break; case CP_N: pt.x = 0.0; pt.y = h2; break; case CP_NE: pt.x = w2-rx; pt.y = h2-rx; break; case CP_E: pt.x = w2; pt.y = 0.0; break; case CP_SE: pt.x = w2-rx; pt.y = rx-h2; break; case CP_S: pt.x = 0.0; pt.y = -h2; break; case CP_SW: pt.x = rx-w2; pt.y = rx-h2; break; case CP_W: pt.x = -w2; pt.y = 0.0; break; case CP_NW: pt.x = rx-w2; pt.y = h2-rx; break; default: assert(0); } UNUSED_PARAMETER(p); return pt; } static PPoint boxChop(Pik *p, PObj *pObj, PPoint *pPt){ PNum dx, dy; int cp = CP_C; PPoint chop = pObj->ptAt; if( pObj->w<=0.0 ) return chop; if( pObj->h<=0.0 ) return chop; dx = (pPt->x - pObj->ptAt.x)*pObj->h/pObj->w; dy = (pPt->y - pObj->ptAt.y); if( dx>0.0 ){ if( dy>=2.414*dx ){ cp = CP_N; }else if( dy>=0.414*dx ){ cp = CP_NE; }else if( dy>=-0.414*dx ){ cp = CP_E; }else if( dy>-2.414*dx ){ cp = CP_SE; }else{ cp = CP_S; } }else{ if( dy>=-2.414*dx ){ cp = CP_N; }else if( dy>=-0.414*dx ){ cp = CP_NW; }else if( dy>=0.414*dx ){ cp = CP_W; }else if( dy>2.414*dx ){ cp = CP_SW; }else{ cp = CP_S; } } chop = pObj->type->xOffset(p,pObj,cp); chop.x += pObj->ptAt.x; chop.y += pObj->ptAt.y; return chop; } static void boxFit(Pik *p, PObj *pObj, PNum w, PNum h){ if( w>0 ) pObj->w = w; if( h>0 ) pObj->h = h; UNUSED_PARAMETER(p); } static void boxRender(Pik *p, PObj *pObj){ PNum w2 = 0.5*pObj->w; PNum h2 = 0.5*pObj->h; PNum rad = pObj->rad; PPoint pt = pObj->ptAt; if( pObj->sw>=0.0 ){ if( rad<=0.0 ){ pik_append_xy(p,"w2 ) rad = w2; if( rad>h2 ) rad = h2; x0 = pt.x - w2; x1 = x0 + rad; x3 = pt.x + w2; x2 = x3 - rad; y0 = pt.y - h2; y1 = y0 + rad; y3 = pt.y + h2; y2 = y3 - rad; pik_append_xy(p,"x1 ) pik_append_xy(p, "L", x2, y0); pik_append_arc(p, rad, rad, x3, y1); if( y2>y1 ) pik_append_xy(p, "L", x3, y2); pik_append_arc(p, rad, rad, x2, y3); if( x2>x1 ) pik_append_xy(p, "L", x1, y3); pik_append_arc(p, rad, rad, x0, y2); if( y2>y1 ) pik_append_xy(p, "L", x0, y1); pik_append_arc(p, rad, rad, x1, y0); pik_append(p,"Z\" ",-1); } pik_append_style(p,pObj,3); pik_append(p,"\" />\n", -1); } pik_append_txt(p, pObj, 0); } /* Methods for the "circle" class */ static void circleInit(Pik *p, PObj *pObj){ pObj->w = pik_value(p, "circlerad",9,0)*2; pObj->h = pObj->w; pObj->rad = 0.5*pObj->w; } static void circleNumProp(Pik *p, PObj *pObj, PToken *pId){ /* For a circle, the width must equal the height and both must ** be twice the radius. Enforce those constraints. */ switch( pId->eType ){ case T_DIAMETER: case T_RADIUS: pObj->w = pObj->h = 2.0*pObj->rad; break; case T_WIDTH: pObj->h = pObj->w; pObj->rad = 0.5*pObj->w; break; case T_HEIGHT: pObj->w = pObj->h; pObj->rad = 0.5*pObj->w; break; } UNUSED_PARAMETER(p); } static PPoint circleChop(Pik *p, PObj *pObj, PPoint *pPt){ PPoint chop; PNum dx = pPt->x - pObj->ptAt.x; PNum dy = pPt->y - pObj->ptAt.y; PNum dist = hypot(dx,dy); if( distrad || dist<=0 ) return pObj->ptAt; chop.x = pObj->ptAt.x + dx*pObj->rad/dist; chop.y = pObj->ptAt.y + dy*pObj->rad/dist; UNUSED_PARAMETER(p); return chop; } static void circleFit(Pik *p, PObj *pObj, PNum w, PNum h){ PNum mx = 0.0; if( w>0 ) mx = w; if( h>mx ) mx = h; if( w*h>0 && (w*w + h*h) > mx*mx ){ mx = hypot(w,h); } if( mx>0.0 ){ pObj->rad = 0.5*mx; pObj->w = pObj->h = mx; } UNUSED_PARAMETER(p); } static void circleRender(Pik *p, PObj *pObj){ PNum r = pObj->rad; PPoint pt = pObj->ptAt; if( pObj->sw>=0.0 ){ pik_append_x(p,"\n", -1); } pik_append_txt(p, pObj, 0); } /* Methods for the "cylinder" class */ static void cylinderInit(Pik *p, PObj *pObj){ pObj->w = pik_value(p, "cylwid",6,0); pObj->h = pik_value(p, "cylht",5,0); pObj->rad = pik_value(p, "cylrad",6,0); /* Minor radius of ellipses */ } static void cylinderFit(Pik *p, PObj *pObj, PNum w, PNum h){ if( w>0 ) pObj->w = w; if( h>0 ) pObj->h = h + 0.25*pObj->rad + pObj->sw; UNUSED_PARAMETER(p); } static void cylinderRender(Pik *p, PObj *pObj){ PNum w2 = 0.5*pObj->w; PNum h2 = 0.5*pObj->h; PNum rad = pObj->rad; PPoint pt = pObj->ptAt; if( pObj->sw>=0.0 ){ if( rad>h2 ){ rad = h2; }else if( rad<0 ){ rad = 0; } pik_append_xy(p,"\n", -1); } pik_append_txt(p, pObj, 0); } static PPoint cylinderOffset(Pik *p, PObj *pObj, int cp){ PPoint pt = cZeroPoint; PNum w2 = pObj->w*0.5; PNum h1 = pObj->h*0.5; PNum h2 = h1 - pObj->rad; switch( cp ){ case CP_C: break; case CP_N: pt.x = 0.0; pt.y = h1; break; case CP_NE: pt.x = w2; pt.y = h2; break; case CP_E: pt.x = w2; pt.y = 0.0; break; case CP_SE: pt.x = w2; pt.y = -h2; break; case CP_S: pt.x = 0.0; pt.y = -h1; break; case CP_SW: pt.x = -w2; pt.y = -h2; break; case CP_W: pt.x = -w2; pt.y = 0.0; break; case CP_NW: pt.x = -w2; pt.y = h2; break; default: assert(0); } UNUSED_PARAMETER(p); return pt; } /* Methods for the "dot" class */ static void dotInit(Pik *p, PObj *pObj){ pObj->rad = pik_value(p, "dotrad",6,0); pObj->h = pObj->w = pObj->rad*6; pObj->fill = pObj->color; } static void dotNumProp(Pik *p, PObj *pObj, PToken *pId){ switch( pId->eType ){ case T_COLOR: pObj->fill = pObj->color; break; case T_FILL: pObj->color = pObj->fill; break; } UNUSED_PARAMETER(p); } static void dotCheck(Pik *p, PObj *pObj){ pObj->w = pObj->h = 0; pik_bbox_addellipse(&pObj->bbox, pObj->ptAt.x, pObj->ptAt.y, pObj->rad, pObj->rad); UNUSED_PARAMETER(p); } static PPoint dotOffset(Pik *p, PObj *pObj, int cp){ UNUSED_PARAMETER(p); UNUSED_PARAMETER(pObj); UNUSED_PARAMETER(cp); return cZeroPoint; } static void dotRender(Pik *p, PObj *pObj){ PNum r = pObj->rad; PPoint pt = pObj->ptAt; if( pObj->sw>=0.0 ){ pik_append_x(p,"\n", -1); } pik_append_txt(p, pObj, 0); } /* Methods for the "diamond" class */ static void diamondInit(Pik *p, PObj *pObj){ pObj->w = pik_value(p, "diamondwid",10,0); pObj->h = pik_value(p, "diamondht",9,0); pObj->bAltAutoFit = 1; } /* Return offset from the center of the box to the compass point ** given by parameter cp */ static PPoint diamondOffset(Pik *p, PObj *pObj, int cp){ PPoint pt = cZeroPoint; PNum w2 = 0.5*pObj->w; PNum w4 = 0.25*pObj->w; PNum h2 = 0.5*pObj->h; PNum h4 = 0.25*pObj->h; switch( cp ){ case CP_C: break; case CP_N: pt.x = 0.0; pt.y = h2; break; case CP_NE: pt.x = w4; pt.y = h4; break; case CP_E: pt.x = w2; pt.y = 0.0; break; case CP_SE: pt.x = w4; pt.y = -h4; break; case CP_S: pt.x = 0.0; pt.y = -h2; break; case CP_SW: pt.x = -w4; pt.y = -h4; break; case CP_W: pt.x = -w2; pt.y = 0.0; break; case CP_NW: pt.x = -w4; pt.y = h4; break; default: assert(0); } UNUSED_PARAMETER(p); return pt; } static void diamondFit(Pik *p, PObj *pObj, PNum w, PNum h){ if( pObj->w<=0 ) pObj->w = w*1.5; if( pObj->h<=0 ) pObj->h = h*1.5; if( pObj->w>0 && pObj->h>0 ){ PNum x = pObj->w*h/pObj->h + w; PNum y = pObj->h*x/pObj->w; pObj->w = x; pObj->h = y; } UNUSED_PARAMETER(p); } static void diamondRender(Pik *p, PObj *pObj){ PNum w2 = 0.5*pObj->w; PNum h2 = 0.5*pObj->h; PPoint pt = pObj->ptAt; if( pObj->sw>=0.0 ){ pik_append_xy(p,"\n", -1); } pik_append_txt(p, pObj, 0); } /* Methods for the "ellipse" class */ static void ellipseInit(Pik *p, PObj *pObj){ pObj->w = pik_value(p, "ellipsewid",10,0); pObj->h = pik_value(p, "ellipseht",9,0); } static PPoint ellipseChop(Pik *p, PObj *pObj, PPoint *pPt){ PPoint chop; PNum s, dq, dist; PNum dx = pPt->x - pObj->ptAt.x; PNum dy = pPt->y - pObj->ptAt.y; if( pObj->w<=0.0 ) return pObj->ptAt; if( pObj->h<=0.0 ) return pObj->ptAt; s = pObj->h/pObj->w; dq = dx*s; dist = hypot(dq,dy); if( disth ) return pObj->ptAt; chop.x = pObj->ptAt.x + 0.5*dq*pObj->h/(dist*s); chop.y = pObj->ptAt.y + 0.5*dy*pObj->h/dist; UNUSED_PARAMETER(p); return chop; } static PPoint ellipseOffset(Pik *p, PObj *pObj, int cp){ PPoint pt = cZeroPoint; PNum w = pObj->w*0.5; PNum w2 = w*0.70710678118654747608; PNum h = pObj->h*0.5; PNum h2 = h*0.70710678118654747608; switch( cp ){ case CP_C: break; case CP_N: pt.x = 0.0; pt.y = h; break; case CP_NE: pt.x = w2; pt.y = h2; break; case CP_E: pt.x = w; pt.y = 0.0; break; case CP_SE: pt.x = w2; pt.y = -h2; break; case CP_S: pt.x = 0.0; pt.y = -h; break; case CP_SW: pt.x = -w2; pt.y = -h2; break; case CP_W: pt.x = -w; pt.y = 0.0; break; case CP_NW: pt.x = -w2; pt.y = h2; break; default: assert(0); } UNUSED_PARAMETER(p); return pt; } static void ellipseRender(Pik *p, PObj *pObj){ PNum w = pObj->w; PNum h = pObj->h; PPoint pt = pObj->ptAt; if( pObj->sw>=0.0 ){ pik_append_x(p,"\n", -1); } pik_append_txt(p, pObj, 0); } /* Methods for the "file" object */ static void fileInit(Pik *p, PObj *pObj){ pObj->w = pik_value(p, "filewid",7,0); pObj->h = pik_value(p, "fileht",6,0); pObj->rad = pik_value(p, "filerad",7,0); } /* Return offset from the center of the file to the compass point ** given by parameter cp */ static PPoint fileOffset(Pik *p, PObj *pObj, int cp){ PPoint pt = cZeroPoint; PNum w2 = 0.5*pObj->w; PNum h2 = 0.5*pObj->h; PNum rx = pObj->rad; PNum mn = w2

mn ) rx = mn; if( rx0 ) pObj->w = w; if( h>0 ) pObj->h = h + 2*pObj->rad; UNUSED_PARAMETER(p); } static void fileRender(Pik *p, PObj *pObj){ PNum w2 = 0.5*pObj->w; PNum h2 = 0.5*pObj->h; PNum rad = pObj->rad; PPoint pt = pObj->ptAt; PNum mn = w2

mn ) rad = mn; if( radsw>=0.0 ){ pik_append_xy(p,"\n",-1); pik_append_xy(p,"\n",-1); } pik_append_txt(p, pObj, 0); } /* Methods for the "line" class */ static void lineInit(Pik *p, PObj *pObj){ pObj->w = pik_value(p, "linewid",7,0); pObj->h = pik_value(p, "lineht",6,0); pObj->rad = pik_value(p, "linerad",7,0); } static PPoint lineOffset(Pik *p, PObj *pObj, int cp){ #if 0 /* In legacy PIC, the .center of an unclosed line is half way between ** its .start and .end. */ if( cp==CP_C && !pObj->bClose ){ PPoint out; out.x = 0.5*(pObj->ptEnter.x + pObj->ptExit.x) - pObj->ptAt.x; out.y = 0.5*(pObj->ptEnter.x + pObj->ptExit.y) - pObj->ptAt.y; return out; } #endif return boxOffset(p,pObj,cp); } static void lineRender(Pik *p, PObj *pObj){ int i; if( pObj->sw>0.0 ){ const char *z = "nPath; if( pObj->larrow ){ pik_draw_arrowhead(p,&pObj->aPath[1],&pObj->aPath[0],pObj); } if( pObj->rarrow ){ pik_draw_arrowhead(p,&pObj->aPath[n-2],&pObj->aPath[n-1],pObj); } for(i=0; inPath; i++){ pik_append_xy(p,z,pObj->aPath[i].x,pObj->aPath[i].y); z = "L"; } if( pObj->bClose ){ pik_append(p,"Z",1); }else{ pObj->fill = -1.0; } pik_append(p,"\" ",-1); pik_append_style(p,pObj,pObj->bClose?3:0); pik_append(p,"\" />\n", -1); } pik_append_txt(p, pObj, 0); } /* Methods for the "move" class */ static void moveInit(Pik *p, PObj *pObj){ pObj->w = pik_value(p, "movewid",7,0); pObj->h = pObj->w; pObj->fill = -1.0; pObj->color = -1.0; pObj->sw = -1.0; } static void moveRender(Pik *p, PObj *pObj){ /* No-op */ UNUSED_PARAMETER(p); UNUSED_PARAMETER(pObj); } /* Methods for the "oval" class */ static void ovalInit(Pik *p, PObj *pObj){ pObj->h = pik_value(p, "ovalht",6,0); pObj->w = pik_value(p, "ovalwid",7,0); pObj->rad = 0.5*(pObj->hw?pObj->h:pObj->w); } static void ovalNumProp(Pik *p, PObj *pObj, PToken *pId){ UNUSED_PARAMETER(p); UNUSED_PARAMETER(pId); /* Always adjust the radius to be half of the smaller of ** the width and height. */ pObj->rad = 0.5*(pObj->hw?pObj->h:pObj->w); } static void ovalFit(Pik *p, PObj *pObj, PNum w, PNum h){ UNUSED_PARAMETER(p); if( w>0 ) pObj->w = w; if( h>0 ) pObj->h = h; if( pObj->wh ) pObj->w = pObj->h; pObj->rad = 0.5*(pObj->hw?pObj->h:pObj->w); } /* Methods for the "spline" class */ static void splineInit(Pik *p, PObj *pObj){ pObj->w = pik_value(p, "linewid",7,0); pObj->h = pik_value(p, "lineht",6,0); pObj->rad = 1000; } /* Return a point along the path from "f" to "t" that is r units ** prior to reaching "t", except if the path is less than 2*r total, ** return the midpoint. */ static PPoint radiusMidpoint(PPoint f, PPoint t, PNum r, int *pbMid){ PNum dx = t.x - f.x; PNum dy = t.y - f.y; PNum dist = hypot(dx,dy); PPoint m; if( dist<=0.0 ) return t; dx /= dist; dy /= dist; if( r > 0.5*dist ){ r = 0.5*dist; *pbMid = 1; }else{ *pbMid = 0; } m.x = t.x - r*dx; m.y = t.y - r*dy; return m; } static void radiusPath(Pik *p, PObj *pObj, PNum r){ int i; int n = pObj->nPath; const PPoint *a = pObj->aPath; PPoint m; PPoint an = a[n-1]; int isMid = 0; int iLast = pObj->bClose ? n : n-1; pik_append_xy(p,"bClose ){ pik_append(p,"Z",1); }else{ pObj->fill = -1.0; } pik_append(p,"\" ",-1); pik_append_style(p,pObj,pObj->bClose?3:0); pik_append(p,"\" />\n", -1); } static void splineRender(Pik *p, PObj *pObj){ if( pObj->sw>0.0 ){ int n = pObj->nPath; PNum r = pObj->rad; if( n<3 || r<=0.0 ){ lineRender(p,pObj); return; } if( pObj->larrow ){ pik_draw_arrowhead(p,&pObj->aPath[1],&pObj->aPath[0],pObj); } if( pObj->rarrow ){ pik_draw_arrowhead(p,&pObj->aPath[n-2],&pObj->aPath[n-1],pObj); } radiusPath(p,pObj,pObj->rad); } pik_append_txt(p, pObj, 0); } /* Methods for the "text" class */ static void textInit(Pik *p, PObj *pObj){ pik_value(p, "textwid",7,0); pik_value(p, "textht",6,0); pObj->sw = 0.0; } static PPoint textOffset(Pik *p, PObj *pObj, int cp){ /* Automatically slim-down the width and height of text ** statements so that the bounding box tightly encloses the text, ** then get boxOffset() to do the offset computation. */ pik_size_to_fit(p, &pObj->errTok,3); return boxOffset(p, pObj, cp); } static void textRender(Pik *p, PObj *pObj){ pik_append_txt(p, pObj, 0); } /* Methods for the "sublist" class */ static void sublistInit(Pik *p, PObj *pObj){ PList *pList = pObj->pSublist; int i; UNUSED_PARAMETER(p); pik_bbox_init(&pObj->bbox); for(i=0; in; i++){ pik_bbox_addbox(&pObj->bbox, &pList->a[i]->bbox); } pObj->w = pObj->bbox.ne.x - pObj->bbox.sw.x; pObj->h = pObj->bbox.ne.y - pObj->bbox.sw.y; pObj->ptAt.x = 0.5*(pObj->bbox.ne.x + pObj->bbox.sw.x); pObj->ptAt.y = 0.5*(pObj->bbox.ne.y + pObj->bbox.sw.y); pObj->mCalc |= A_WIDTH|A_HEIGHT|A_RADIUS; } /* ** The following array holds all the different kinds of objects. ** The special [] object is separate. */ static const PClass aClass[] = { { /* name */ "arc", /* isline */ 1, /* eJust */ 0, /* xInit */ arcInit, /* xNumProp */ 0, /* xCheck */ arcCheck, /* xChop */ 0, /* xOffset */ boxOffset, /* xFit */ 0, /* xRender */ arcRender }, { /* name */ "arrow", /* isline */ 1, /* eJust */ 0, /* xInit */ arrowInit, /* xNumProp */ 0, /* xCheck */ 0, /* xChop */ 0, /* xOffset */ lineOffset, /* xFit */ 0, /* xRender */ splineRender }, { /* name */ "box", /* isline */ 0, /* eJust */ 1, /* xInit */ boxInit, /* xNumProp */ 0, /* xCheck */ 0, /* xChop */ boxChop, /* xOffset */ boxOffset, /* xFit */ boxFit, /* xRender */ boxRender }, { /* name */ "circle", /* isline */ 0, /* eJust */ 0, /* xInit */ circleInit, /* xNumProp */ circleNumProp, /* xCheck */ 0, /* xChop */ circleChop, /* xOffset */ ellipseOffset, /* xFit */ circleFit, /* xRender */ circleRender }, { /* name */ "cylinder", /* isline */ 0, /* eJust */ 1, /* xInit */ cylinderInit, /* xNumProp */ 0, /* xCheck */ 0, /* xChop */ boxChop, /* xOffset */ cylinderOffset, /* xFit */ cylinderFit, /* xRender */ cylinderRender }, { /* name */ "diamond", /* isline */ 0, /* eJust */ 0, /* xInit */ diamondInit, /* xNumProp */ 0, /* xCheck */ 0, /* xChop */ boxChop, /* xOffset */ diamondOffset, /* xFit */ diamondFit, /* xRender */ diamondRender }, { /* name */ "dot", /* isline */ 0, /* eJust */ 0, /* xInit */ dotInit, /* xNumProp */ dotNumProp, /* xCheck */ dotCheck, /* xChop */ circleChop, /* xOffset */ dotOffset, /* xFit */ 0, /* xRender */ dotRender }, { /* name */ "ellipse", /* isline */ 0, /* eJust */ 0, /* xInit */ ellipseInit, /* xNumProp */ 0, /* xCheck */ 0, /* xChop */ ellipseChop, /* xOffset */ ellipseOffset, /* xFit */ boxFit, /* xRender */ ellipseRender }, { /* name */ "file", /* isline */ 0, /* eJust */ 1, /* xInit */ fileInit, /* xNumProp */ 0, /* xCheck */ 0, /* xChop */ boxChop, /* xOffset */ fileOffset, /* xFit */ fileFit, /* xRender */ fileRender }, { /* name */ "line", /* isline */ 1, /* eJust */ 0, /* xInit */ lineInit, /* xNumProp */ 0, /* xCheck */ 0, /* xChop */ 0, /* xOffset */ lineOffset, /* xFit */ 0, /* xRender */ splineRender }, { /* name */ "move", /* isline */ 1, /* eJust */ 0, /* xInit */ moveInit, /* xNumProp */ 0, /* xCheck */ 0, /* xChop */ 0, /* xOffset */ boxOffset, /* xFit */ 0, /* xRender */ moveRender }, { /* name */ "oval", /* isline */ 0, /* eJust */ 1, /* xInit */ ovalInit, /* xNumProp */ ovalNumProp, /* xCheck */ 0, /* xChop */ boxChop, /* xOffset */ boxOffset, /* xFit */ ovalFit, /* xRender */ boxRender }, { /* name */ "spline", /* isline */ 1, /* eJust */ 0, /* xInit */ splineInit, /* xNumProp */ 0, /* xCheck */ 0, /* xChop */ 0, /* xOffset */ lineOffset, /* xFit */ 0, /* xRender */ splineRender }, { /* name */ "text", /* isline */ 0, /* eJust */ 0, /* xInit */ textInit, /* xNumProp */ 0, /* xCheck */ 0, /* xChop */ boxChop, /* xOffset */ textOffset, /* xFit */ boxFit, /* xRender */ textRender }, }; static const PClass sublistClass = { /* name */ "[]", /* isline */ 0, /* eJust */ 0, /* xInit */ sublistInit, /* xNumProp */ 0, /* xCheck */ 0, /* xChop */ 0, /* xOffset */ boxOffset, /* xFit */ 0, /* xRender */ 0 }; static const PClass noopClass = { /* name */ "noop", /* isline */ 0, /* eJust */ 0, /* xInit */ 0, /* xNumProp */ 0, /* xCheck */ 0, /* xChop */ 0, /* xOffset */ boxOffset, /* xFit */ 0, /* xRender */ 0 }; /* ** Reduce the length of the line segment by amt (if possible) by ** modifying the location of *t. */ static void pik_chop(PPoint *f, PPoint *t, PNum amt){ PNum dx = t->x - f->x; PNum dy = t->y - f->y; PNum dist = hypot(dx,dy); PNum r; if( dist<=amt ){ *t = *f; return; } r = 1.0 - amt/dist; t->x = f->x + r*dx; t->y = f->y + r*dy; } /* ** Draw an arrowhead on the end of the line segment from pFrom to pTo. ** Also, shorten the line segment (by changing the value of pTo) so that ** the shaft of the arrow does not extend into the arrowhead. */ static void pik_draw_arrowhead(Pik *p, PPoint *f, PPoint *t, PObj *pObj){ PNum dx = t->x - f->x; PNum dy = t->y - f->y; PNum dist = hypot(dx,dy); PNum h = p->hArrow * pObj->sw; PNum w = p->wArrow * pObj->sw; PNum e1, ddx, ddy; PNum bx, by; if( pObj->color<0.0 ) return; if( pObj->sw<=0.0 ) return; if( dist<=0.0 ) return; /* Unable */ dx /= dist; dy /= dist; e1 = dist - h; if( e1<0.0 ){ e1 = 0.0; h = dist; } ddx = -w*dy; ddy = w*dx; bx = f->x + e1*dx; by = f->y + e1*dy; pik_append_xy(p,"x, t->y); pik_append_xy(p," ",bx-ddx, by-ddy); pik_append_xy(p," ",bx+ddx, by+ddy); pik_append_clr(p,"\" style=\"fill:",pObj->color,"\"/>\n",0); pik_chop(f,t,h/2); } /* ** Compute the relative offset to an edge location from the reference for a ** an statement. */ static PPoint pik_elem_offset(Pik *p, PObj *pObj, int cp){ return pObj->type->xOffset(p, pObj, cp); } /* ** Append raw text to zOut */ static void pik_append(Pik *p, const char *zText, int n){ if( n<0 ) n = (int)strlen(zText); if( p->nOut+n>=p->nOutAlloc ){ int nNew = (p->nOut+n)*2 + 1; char *z = realloc(p->zOut, nNew); if( z==0 ){ pik_error(p, 0, 0); return; } p->zOut = z; p->nOutAlloc = nNew; } memcpy(p->zOut+p->nOut, zText, n); p->nOut += n; p->zOut[p->nOut] = 0; } /* ** Given a string and its length, returns true if the string begins ** with a construct which syntactically matches an HTML entity escape ** sequence (without checking for whether it's a known entity). Always ** returns false if zText[0] is false or n<4. Entities match the ** equivalent of the regexes `&#[0-9]{2,};` and ** `&[a-zA-Z][a-zA-Z0-9]+;`. */ static int pik_isentity(char const * zText, int n){ int i = 0; if( n<4 || '&'!=zText[0] ) return 0; n--; zText++; if( '#'==zText[0] ){ zText++; n--; for(i=0; i1 && ';'==zText[i] ) return 1; else if( zText[i]<'0' || zText[i]>'9' ) return 0; /* Note that &#nn; values nn<32d are not legal entities. */ } }else{ for(i=0; i1 && ';'==zText[i] ) return 1; else if( i>0 && zText[i]>='0' && zText[i]<='9' ){ continue; }else if( zText[i]<'A' || zText[i]>'z' || (zText[i]>'Z' && zText[i]<'a') ) return 0; } } return 0; } /* ** Append text to zOut with HTML characters escaped. ** ** * The space character is changed into non-breaking space (U+00a0) ** if mFlags has the 0x01 bit set. This is needed when outputting ** text to preserve leading and trailing whitespace. Turns out we ** cannot use   as that is an HTML-ism and is not valid in XML. ** ** * The "&" character is changed into "&" if mFlags has the ** 0x02 bit set. This is needed when generating error message text. ** ** * Except for the above, only "<" and ">" are escaped. */ static void pik_append_text(Pik *p, const char *zText, int n, int mFlags){ int i; char c = 0; int bQSpace = mFlags & 1; int bQAmp = mFlags & 2; if( n<0 ) n = (int)strlen(zText); while( n>0 ){ for(i=0; i' ) break; if( c==' ' && bQSpace ) break; if( c=='&' && bQAmp ) break; } if( i ) pik_append(p, zText, i); if( i==n ) break; switch( c ){ case '<': { pik_append(p, "<", 4); break; } case '>': { pik_append(p, ">", 4); break; } case ' ': { pik_append(p, "\302\240;", 2); break; } case '&': if( pik_isentity(zText+i, n-i) ){ pik_append(p, "&", 1); } else { pik_append(p, "&", 5); } } i++; n -= i; zText += i; i = 0; } } /* ** Append error message text. This is either a raw append, or an append ** with HTML escapes, depending on whether the PIKCHR_PLAINTEXT_ERRORS flag ** is set. */ static void pik_append_errtxt(Pik *p, const char *zText, int n){ if( p->mFlags & PIKCHR_PLAINTEXT_ERRORS ){ pik_append(p, zText, n); }else{ pik_append_text(p, zText, n, 0); } } /* Append a PNum value */ static void pik_append_num(Pik *p, const char *z,PNum v){ char buf[100]; snprintf(buf, sizeof(buf)-1, "%.10g", (double)v); buf[sizeof(buf)-1] = 0; pik_append(p, z, -1); pik_append(p, buf, -1); } /* Append a PPoint value (Used for debugging only) */ static void pik_append_point(Pik *p, const char *z, PPoint *pPt){ char buf[100]; snprintf(buf, sizeof(buf)-1, "%.10g,%.10g", (double)pPt->x, (double)pPt->y); buf[sizeof(buf)-1] = 0; pik_append(p, z, -1); pik_append(p, buf, -1); } /* ** Invert the RGB color so that it is appropriate for dark mode. ** Variable x hold the initial color. The color is intended for use ** as a background color if isBg is true, and as a foreground color ** if isBg is false. */ static int pik_color_to_dark_mode(int x, int isBg){ int r, g, b; int mn, mx; x = 0xffffff - x; r = (x>>16) & 0xff; g = (x>>8) & 0xff; b = x & 0xff; mx = r; if( g>mx ) mx = g; if( b>mx ) mx = b; mn = r; if( g127 ){ r = (127*r)/mx; g = (127*g)/mx; b = (127*b)/mx; } }else{ if( mn<128 && mx>mn ){ r = 127 + ((r-mn)*128)/(mx-mn); g = 127 + ((g-mn)*128)/(mx-mn); b = 127 + ((b-mn)*128)/(mx-mn); } } return r*0x10000 + g*0x100 + b; } /* Append a PNum value surrounded by text. Do coordinate transformations ** on the value. */ static void pik_append_x(Pik *p, const char *z1, PNum v, const char *z2){ char buf[200]; v -= p->bbox.sw.x; snprintf(buf, sizeof(buf)-1, "%s%g%s", z1, p->rScale*v, z2); buf[sizeof(buf)-1] = 0; pik_append(p, buf, -1); } static void pik_append_y(Pik *p, const char *z1, PNum v, const char *z2){ char buf[200]; v = p->bbox.ne.y - v; snprintf(buf, sizeof(buf)-1, "%s%g%s", z1, p->rScale*v, z2); buf[sizeof(buf)-1] = 0; pik_append(p, buf, -1); } static void pik_append_xy(Pik *p, const char *z1, PNum x, PNum y){ char buf[200]; x = x - p->bbox.sw.x; y = p->bbox.ne.y - y; snprintf(buf, sizeof(buf)-1, "%s%g,%g", z1, p->rScale*x, p->rScale*y); buf[sizeof(buf)-1] = 0; pik_append(p, buf, -1); } static void pik_append_dis(Pik *p, const char *z1, PNum v, const char *z2){ char buf[200]; snprintf(buf, sizeof(buf)-1, "%s%g%s", z1, p->rScale*v, z2); buf[sizeof(buf)-1] = 0; pik_append(p, buf, -1); } /* Append a color specification to the output. ** ** In PIKCHR_DARK_MODE, the color is inverted. The "bg" flags indicates that ** the color is intended for use as a background color if true, or as a ** foreground color if false. The distinction only matters for color ** inversions in PIKCHR_DARK_MODE. */ static void pik_append_clr(Pik *p,const char *z1,PNum v,const char *z2,int bg){ char buf[200]; int x = pik_round(v); int r, g, b; if( x==0 && p->fgcolor>0 && !bg ){ x = p->fgcolor; }else if( bg && x>=0xffffff && p->bgcolor>0 ){ x = p->bgcolor; }else if( p->mFlags & PIKCHR_DARK_MODE ){ x = pik_color_to_dark_mode(x,bg); } r = (x>>16) & 0xff; g = (x>>8) & 0xff; b = x & 0xff; snprintf(buf, sizeof(buf)-1, "%srgb(%d,%d,%d)%s", z1, r, g, b, z2); buf[sizeof(buf)-1] = 0; pik_append(p, buf, -1); } /* Append an SVG path A record: ** ** A r1 r2 0 0 0 x y */ static void pik_append_arc(Pik *p, PNum r1, PNum r2, PNum x, PNum y){ char buf[200]; x = x - p->bbox.sw.x; y = p->bbox.ne.y - y; snprintf(buf, sizeof(buf)-1, "A%g %g 0 0 0 %g %g", p->rScale*r1, p->rScale*r2, p->rScale*x, p->rScale*y); buf[sizeof(buf)-1] = 0; pik_append(p, buf, -1); } /* Append a style="..." text. But, leave the quote unterminated, in case ** the caller wants to add some more. ** ** eFill is non-zero to fill in the background, or 0 if no fill should ** occur. Non-zero values of eFill determine the "bg" flag to pik_append_clr() ** for cases when pObj->fill==pObj->color ** ** 1 fill is background, and color is foreground. ** 2 fill and color are both foreground. (Used by "dot" objects) ** 3 fill and color are both background. (Used by most other objs) */ static void pik_append_style(Pik *p, PObj *pObj, int eFill){ int clrIsBg = 0; pik_append(p, " style=\"", -1); if( pObj->fill>=0 && eFill ){ int fillIsBg = 1; if( pObj->fill==pObj->color ){ if( eFill==2 ) fillIsBg = 0; if( eFill==3 ) clrIsBg = 1; } pik_append_clr(p, "fill:", pObj->fill, ";", fillIsBg); }else{ pik_append(p,"fill:none;",-1); } if( pObj->sw>=0.0 && pObj->color>=0.0 ){ PNum sw = pObj->sw; pik_append_dis(p, "stroke-width:", sw, ";"); if( pObj->nPath>2 && pObj->rad<=pObj->sw ){ pik_append(p, "stroke-linejoin:round;", -1); } pik_append_clr(p, "stroke:",pObj->color,";",clrIsBg); if( pObj->dotted>0.0 ){ PNum v = pObj->dotted; if( sw<2.1/p->rScale ) sw = 2.1/p->rScale; pik_append_dis(p,"stroke-dasharray:",sw,""); pik_append_dis(p,",",v,";"); }else if( pObj->dashed>0.0 ){ PNum v = pObj->dashed; pik_append_dis(p,"stroke-dasharray:",v,""); pik_append_dis(p,",",v,";"); } } } /* ** Compute the vertical locations for all text items in the ** object pObj. In other words, set every pObj->aTxt[*].eCode ** value to contain exactly one of: TP_ABOVE2, TP_ABOVE, TP_CENTER, ** TP_BELOW, or TP_BELOW2 is set. */ static void pik_txt_vertical_layout(PObj *pObj){ int n, i; PToken *aTxt; n = pObj->nTxt; if( n==0 ) return; aTxt = pObj->aTxt; if( n==1 ){ if( (aTxt[0].eCode & TP_VMASK)==0 ){ aTxt[0].eCode |= TP_CENTER; } }else{ int allSlots = 0; int aFree[5]; int iSlot; int j, mJust; /* If there is more than one TP_ABOVE, change the first to TP_ABOVE2. */ for(j=mJust=0, i=n-1; i>=0; i--){ if( aTxt[i].eCode & TP_ABOVE ){ if( j==0 ){ j++; mJust = aTxt[i].eCode & TP_JMASK; }else if( j==1 && mJust!=0 && (aTxt[i].eCode & mJust)==0 ){ j++; }else{ aTxt[i].eCode = (aTxt[i].eCode & ~TP_VMASK) | TP_ABOVE2; break; } } } /* If there is more than one TP_BELOW, change the last to TP_BELOW2 */ for(j=mJust=0, i=0; i=4 && (allSlots & TP_ABOVE2)==0 ) aFree[iSlot++] = TP_ABOVE2; if( (allSlots & TP_ABOVE)==0 ) aFree[iSlot++] = TP_ABOVE; if( (n&1)!=0 ) aFree[iSlot++] = TP_CENTER; if( (allSlots & TP_BELOW)==0 ) aFree[iSlot++] = TP_BELOW; if( n>=4 && (allSlots & TP_BELOW2)==0 ) aFree[iSlot++] = TP_BELOW2; } /* Set the VMASK for all unassigned texts */ for(i=iSlot=0; ieCode & TP_BIG ) scale *= 1.25; if( t->eCode & TP_SMALL ) scale *= 0.8; if( t->eCode & TP_XTRA ) scale *= scale; return scale; } /* Append multiple SVG elements for the text fields of the PObj. ** Parameters: ** ** p The Pik object into which we are rendering ** ** pObj Object containing the text to be rendered ** ** pBox If not NULL, do no rendering at all. Instead ** expand the box object so that it will include all ** of the text. */ static void pik_append_txt(Pik *p, PObj *pObj, PBox *pBox){ PNum jw; /* Justification margin relative to center */ PNum ha2 = 0.0; /* Height of the top row of text */ PNum ha1 = 0.0; /* Height of the second "above" row */ PNum hc = 0.0; /* Height of the center row */ PNum hb1 = 0.0; /* Height of the first "below" row of text */ PNum hb2 = 0.0; /* Height of the second "below" row */ PNum yBase = 0.0; PNum sw = pObj->sw>=0.0 ? pObj->sw : 0; int n, i, nz; PNum x, y, orig_y, s; const char *z; PToken *aTxt; unsigned allMask = 0; if( p->nErr ) return; if( pObj->nTxt==0 ) return; aTxt = pObj->aTxt; n = pObj->nTxt; pik_txt_vertical_layout(pObj); x = pObj->ptAt.x; for(i=0; iaTxt[i].eCode; if( pObj->type->isLine ){ hc = sw*1.5; }else if( pObj->rad>0.0 && pObj->type->xInit==cylinderInit ){ yBase = -0.75*pObj->rad; } if( allMask & TP_CENTER ){ for(i=0; iaTxt[i].eCode & TP_CENTER ){ s = pik_font_scale(pObj->aTxt+i); if( hccharHeight ) hc = s*p->charHeight; } } } if( allMask & TP_ABOVE ){ for(i=0; iaTxt[i].eCode & TP_ABOVE ){ s = pik_font_scale(pObj->aTxt+i)*p->charHeight; if( ha1aTxt[i].eCode & TP_ABOVE2 ){ s = pik_font_scale(pObj->aTxt+i)*p->charHeight; if( ha2aTxt[i].eCode & TP_BELOW ){ s = pik_font_scale(pObj->aTxt+i)*p->charHeight; if( hb1aTxt[i].eCode & TP_BELOW2 ){ s = pik_font_scale(pObj->aTxt+i)*p->charHeight; if( hb2type->eJust==1 ){ jw = 0.5*(pObj->w - 0.5*(p->charWidth + sw)); }else{ jw = 0.0; } for(i=0; iptAt.y; y = yBase; if( t->eCode & TP_ABOVE2 ) y += 0.5*hc + ha1 + 0.5*ha2; if( t->eCode & TP_ABOVE ) y += 0.5*hc + 0.5*ha1; if( t->eCode & TP_BELOW ) y -= 0.5*hc + 0.5*hb1; if( t->eCode & TP_BELOW2 ) y -= 0.5*hc + hb1 + 0.5*hb2; if( t->eCode & TP_LJUST ) nx -= jw; if( t->eCode & TP_RJUST ) nx += jw; if( pBox!=0 ){ /* If pBox is not NULL, do not draw any . Instead, just expand ** pBox to include the text */ PNum cw = pik_text_length(t, t->eCode & TP_MONO)*p->charWidth*xtraFontScale*0.01; PNum ch = p->charHeight*0.5*xtraFontScale; PNum x0, y0, x1, y1; /* Boundary of text relative to pObj->ptAt */ if( (t->eCode & (TP_BOLD|TP_MONO))==TP_BOLD ){ cw *= 1.1; } if( t->eCode & TP_RJUST ){ x0 = nx; y0 = y-ch; x1 = nx-cw; y1 = y+ch; }else if( t->eCode & TP_LJUST ){ x0 = nx; y0 = y-ch; x1 = nx+cw; y1 = y+ch; }else{ x0 = nx+cw/2; y0 = y+ch; x1 = nx-cw/2; y1 = y-ch; } if( (t->eCode & TP_ALIGN)!=0 && pObj->nPath>=2 ){ int nn = pObj->nPath; PNum dx = pObj->aPath[nn-1].x - pObj->aPath[0].x; PNum dy = pObj->aPath[nn-1].y - pObj->aPath[0].y; if( dx!=0 || dy!=0 ){ PNum dist = hypot(dx,dy); PNum tt; dx /= dist; dy /= dist; tt = dx*x0 - dy*y0; y0 = dy*x0 - dx*y0; x0 = tt; tt = dx*x1 - dy*y1; y1 = dy*x1 - dx*y1; x1 = tt; } } pik_bbox_add_xy(pBox, x+x0, orig_y+y0); pik_bbox_add_xy(pBox, x+x1, orig_y+y1); continue; } nx += x; y += orig_y; pik_append_x(p, "eCode & TP_RJUST ){ pik_append(p, " text-anchor=\"end\"", -1); }else if( t->eCode & TP_LJUST ){ pik_append(p, " text-anchor=\"start\"", -1); }else{ pik_append(p, " text-anchor=\"middle\"", -1); } if( t->eCode & TP_ITALIC ){ pik_append(p, " font-style=\"italic\"", -1); } if( t->eCode & TP_BOLD ){ pik_append(p, " font-weight=\"bold\"", -1); } if( t->eCode & TP_MONO ){ pik_append(p, " font-family=\"monospace\"", -1); } if( pObj->color>=0.0 ){ pik_append_clr(p, " fill=\"", pObj->color, "\"",0); } xtraFontScale *= p->fontScale; if( xtraFontScale<=0.99 || xtraFontScale>=1.01 ){ pik_append_num(p, " font-size=\"", xtraFontScale*100.0); pik_append(p, "%\"", 2); } if( (t->eCode & TP_ALIGN)!=0 && pObj->nPath>=2 ){ int nn = pObj->nPath; PNum dx = pObj->aPath[nn-1].x - pObj->aPath[0].x; PNum dy = pObj->aPath[nn-1].y - pObj->aPath[0].y; if( dx!=0 || dy!=0 ){ PNum ang = atan2(dy,dx)*-180/M_PI; pik_append_num(p, " transform=\"rotate(", ang); pik_append_xy(p, " ", x, orig_y); pik_append(p,")\"",2); } } pik_append(p," dominant-baseline=\"central\">",-1); if( t->n>=2 && t->z[0]=='"' ){ z = t->z+1; nz = t->n-2; }else{ z = t->z; nz = t->n; } while( nz>0 ){ int j; for(j=0; j\n", -1); } } /* ** Append text (that will go inside of a
...
) that ** shows the context of an error token. */ static void pik_error_context(Pik *p, PToken *pErr, int nContext){ int iErrPt; /* Index of first byte of error from start of input */ int iErrCol; /* Column of the error token on its line */ int iStart; /* Start position of the error context */ int iEnd; /* End position of the error context */ int iLineno; /* Line number of the error */ int iFirstLineno; /* Line number of start of error context */ int i; /* Loop counter */ int iBump = 0; /* Bump the location of the error cursor */ char zLineno[24]; /* Buffer in which to generate line numbers */ iErrPt = (int)(pErr->z - p->sIn.z); if( iErrPt>=(int)p->sIn.n ){ iErrPt = p->sIn.n-1; iBump = 1; }else{ while( iErrPt>0 && (p->sIn.z[iErrPt]=='\n' || p->sIn.z[iErrPt]=='\r') ){ iErrPt--; iBump = 1; } } iLineno = 1; for(i=0; isIn.z[i]=='\n' ){ iLineno++; } } iStart = 0; iFirstLineno = 1; while( iFirstLineno+nContextsIn.z[iStart]!='\n' ){ iStart++; } iStart++; iFirstLineno++; } for(iEnd=iErrPt; p->sIn.z[iEnd]!=0 && p->sIn.z[iEnd]!='\n'; iEnd++){} i = iStart; while( iFirstLineno<=iLineno ){ snprintf(zLineno,sizeof(zLineno)-1,"/* %4d */ ", iFirstLineno++); zLineno[sizeof(zLineno)-1] = 0; pik_append(p, zLineno, -1); for(i=iStart; p->sIn.z[i]!=0 && p->sIn.z[i]!='\n'; i++){} pik_append_errtxt(p, p->sIn.z+iStart, i-iStart); iStart = i+1; pik_append(p, "\n", 1); } for(iErrCol=0, i=iErrPt; i>0 && p->sIn.z[i]!='\n'; iErrCol++, i--){} for(i=0; in; i++) pik_append(p, "^", 1); pik_append(p, "\n", 1); } /* ** Generate an error message for the output. pErr is the token at which ** the error should point. zMsg is the text of the error message. If ** either pErr or zMsg is NULL, generate an out-of-memory error message. ** ** This routine is a no-op if there has already been an error reported. */ static void pik_error(Pik *p, PToken *pErr, const char *zMsg){ int i; if( p==0 ) return; if( p->nErr ) return; p->nErr++; if( zMsg==0 ){ if( p->mFlags & PIKCHR_PLAINTEXT_ERRORS ){ pik_append(p, "\nOut of memory\n", -1); }else{ pik_append(p, "\n

Out of memory

\n", -1); } return; } if( pErr==0 ){ pik_append(p, "\n", 1); pik_append_errtxt(p, zMsg, -1); return; } if( (p->mFlags & PIKCHR_PLAINTEXT_ERRORS)==0 ){ pik_append(p, "
\n", -1);
  }
  pik_error_context(p, pErr, 5);
  pik_append(p, "ERROR: ", -1);
  pik_append_errtxt(p, zMsg, -1);
  pik_append(p, "\n", 1);
  for(i=p->nCtx-1; i>=0; i--){
    pik_append(p, "Called from:\n", -1);
    pik_error_context(p, &p->aCtx[i], 0);
  }
  if( (p->mFlags & PIKCHR_PLAINTEXT_ERRORS)==0 ){
    pik_append(p, "
\n", -1); } } /* ** Process an "assert( e1 == e2 )" statement. Always return NULL. */ static PObj *pik_assert(Pik *p, PNum e1, PToken *pEq, PNum e2){ char zE1[100], zE2[100], zMsg[300]; /* Convert the numbers to strings using %g for comparison. This ** limits the precision of the comparison to account for rounding error. */ snprintf(zE1, sizeof(zE1), "%g", e1); zE1[sizeof(zE1)-1] = 0; snprintf(zE2, sizeof(zE2), "%g", e2); zE1[sizeof(zE2)-1] = 0; if( strcmp(zE1,zE2)!=0 ){ snprintf(zMsg, sizeof(zMsg), "%.50s != %.50s", zE1, zE2); pik_error(p, pEq, zMsg); } return 0; } /* ** Process an "assert( place1 == place2 )" statement. Always return NULL. */ static PObj *pik_position_assert(Pik *p, PPoint *e1, PToken *pEq, PPoint *e2){ char zE1[100], zE2[100], zMsg[210]; /* Convert the numbers to strings using %g for comparison. This ** limits the precision of the comparison to account for rounding error. */ snprintf(zE1, sizeof(zE1), "(%g,%g)", e1->x, e1->y); zE1[sizeof(zE1)-1] = 0; snprintf(zE2, sizeof(zE2), "(%g,%g)", e2->x, e2->y); zE1[sizeof(zE2)-1] = 0; if( strcmp(zE1,zE2)!=0 ){ snprintf(zMsg, sizeof(zMsg), "%s != %s", zE1, zE2); pik_error(p, pEq, zMsg); } return 0; } /* Free a complete list of objects */ static void pik_elist_free(Pik *p, PList *pList){ int i; if( pList==0 ) return; for(i=0; in; i++){ pik_elem_free(p, pList->a[i]); } free(pList->a); free(pList); return; } /* Free a single object, and its substructure */ static void pik_elem_free(Pik *p, PObj *pObj){ if( pObj==0 ) return; free(pObj->zName); pik_elist_free(p, pObj->pSublist); free(pObj->aPath); free(pObj); } /* Convert a numeric literal into a number. Return that number. ** There is no error handling because the tokenizer has already ** assured us that the numeric literal is valid. ** ** Allowed number forms: ** ** (1) Floating point literal ** (2) Same as (1) but followed by a unit: "cm", "mm", "in", ** "px", "pt", or "pc". ** (3) Hex integers: 0x000000 ** ** This routine returns the result in inches. If a different unit ** is specified, the conversion happens automatically. */ PNum pik_atof(PToken *num){ char *endptr; PNum ans; if( num->n>=3 && num->z[0]=='0' && (num->z[1]=='x'||num->z[1]=='X') ){ return (PNum)strtol(num->z+2, 0, 16); } ans = strtod(num->z, &endptr); if( (int)(endptr - num->z)==(int)num->n-2 ){ char c1 = endptr[0]; char c2 = endptr[1]; if( c1=='c' && c2=='m' ){ ans /= 2.54; }else if( c1=='m' && c2=='m' ){ ans /= 25.4; }else if( c1=='p' && c2=='x' ){ ans /= 96; }else if( c1=='p' && c2=='t' ){ ans /= 72; }else if( c1=='p' && c2=='c' ){ ans /= 6; } } return ans; } /* ** Compute the distance between two points */ static PNum pik_dist(PPoint *pA, PPoint *pB){ PNum dx, dy; dx = pB->x - pA->x; dy = pB->y - pA->y; return hypot(dx,dy); } /* Return true if a bounding box is empty. */ static int pik_bbox_isempty(PBox *p){ return p->sw.x>p->ne.x; } /* Return true if point pPt is contained within the bounding box pBox */ static int pik_bbox_contains_point(PBox *pBox, PPoint *pPt){ if( pik_bbox_isempty(pBox) ) return 0; if( pPt->x < pBox->sw.x ) return 0; if( pPt->x > pBox->ne.x ) return 0; if( pPt->y < pBox->sw.y ) return 0; if( pPt->y > pBox->ne.y ) return 0; return 1; } /* Initialize a bounding box to an empty container */ static void pik_bbox_init(PBox *p){ p->sw.x = 1.0; p->sw.y = 1.0; p->ne.x = 0.0; p->ne.y = 0.0; } /* Enlarge the PBox of the first argument so that it fully ** covers the second PBox */ static void pik_bbox_addbox(PBox *pA, PBox *pB){ if( pik_bbox_isempty(pA) ){ *pA = *pB; } if( pik_bbox_isempty(pB) ) return; if( pA->sw.x>pB->sw.x ) pA->sw.x = pB->sw.x; if( pA->sw.y>pB->sw.y ) pA->sw.y = pB->sw.y; if( pA->ne.xne.x ) pA->ne.x = pB->ne.x; if( pA->ne.yne.y ) pA->ne.y = pB->ne.y; } /* Enlarge the PBox of the first argument, if necessary, so that ** it contains the point described by the 2nd and 3rd arguments. */ static void pik_bbox_add_xy(PBox *pA, PNum x, PNum y){ if( pik_bbox_isempty(pA) ){ pA->ne.x = x; pA->ne.y = y; pA->sw.x = x; pA->sw.y = y; return; } if( pA->sw.x>x ) pA->sw.x = x; if( pA->sw.y>y ) pA->sw.y = y; if( pA->ne.xne.x = x; if( pA->ne.yne.y = y; } /* Enlarge the PBox so that it is able to contain an ellipse ** centered at x,y and with radiuses rx and ry. */ static void pik_bbox_addellipse(PBox *pA, PNum x, PNum y, PNum rx, PNum ry){ if( pik_bbox_isempty(pA) ){ pA->ne.x = x+rx; pA->ne.y = y+ry; pA->sw.x = x-rx; pA->sw.y = y-ry; return; } if( pA->sw.x>x-rx ) pA->sw.x = x-rx; if( pA->sw.y>y-ry ) pA->sw.y = y-ry; if( pA->ne.xne.x = x+rx; if( pA->ne.yne.y = y+ry; } /* Append a new object onto the end of an object list. The ** object list is created if it does not already exist. Return ** the new object list. */ static PList *pik_elist_append(Pik *p, PList *pList, PObj *pObj){ if( pObj==0 ) return pList; if( pList==0 ){ pList = malloc(sizeof(*pList)); if( pList==0 ){ pik_error(p, 0, 0); pik_elem_free(p, pObj); return 0; } memset(pList, 0, sizeof(*pList)); } if( pList->n>=pList->nAlloc ){ int nNew = (pList->n+5)*2; PObj **pNew = realloc(pList->a, sizeof(PObj*)*nNew); if( pNew==0 ){ pik_error(p, 0, 0); pik_elem_free(p, pObj); return pList; } pList->nAlloc = nNew; pList->a = pNew; } pList->a[pList->n++] = pObj; p->list = pList; return pList; } /* Convert an object class name into a PClass pointer */ static const PClass *pik_find_class(PToken *pId){ int first = 0; int last = count(aClass) - 1; do{ int mid = (first+last)/2; int c = strncmp(aClass[mid].zName, pId->z, pId->n); if( c==0 ){ c = aClass[mid].zName[pId->n]!=0; if( c==0 ) return &aClass[mid]; } if( c<0 ){ first = mid + 1; }else{ last = mid - 1; } }while( first<=last ); return 0; } /* Allocate and return a new PObj object. ** ** If pId!=0 then pId is an identifier that defines the object class. ** If pStr!=0 then it is a STRING literal that defines a text object. ** If pSublist!=0 then this is a [...] object. If all three parameters ** are NULL then this is a no-op object used to define a PLACENAME. */ static PObj *pik_elem_new(Pik *p, PToken *pId, PToken *pStr,PList *pSublist){ PObj *pNew; int miss = 0; if( p->nErr ) return 0; pNew = malloc( sizeof(*pNew) ); if( pNew==0 ){ pik_error(p,0,0); pik_elist_free(p, pSublist); return 0; } memset(pNew, 0, sizeof(*pNew)); p->cur = pNew; p->nTPath = 1; p->thenFlag = 0; if( p->list==0 || p->list->n==0 ){ pNew->ptAt.x = pNew->ptAt.y = 0.0; pNew->eWith = CP_C; }else{ PObj *pPrior = p->list->a[p->list->n-1]; pNew->ptAt = pPrior->ptExit; switch( p->eDir ){ default: pNew->eWith = CP_W; break; case DIR_LEFT: pNew->eWith = CP_E; break; case DIR_UP: pNew->eWith = CP_S; break; case DIR_DOWN: pNew->eWith = CP_N; break; } } p->aTPath[0] = pNew->ptAt; pNew->with = pNew->ptAt; pNew->outDir = pNew->inDir = p->eDir; pNew->iLayer = pik_value_int(p, "layer", 5, &miss); if( miss ) pNew->iLayer = 1000; if( pNew->iLayer<0 ) pNew->iLayer = 0; if( pSublist ){ pNew->type = &sublistClass; pNew->pSublist = pSublist; sublistClass.xInit(p,pNew); return pNew; } if( pStr ){ PToken n; n.z = "text"; n.n = 4; pNew->type = pik_find_class(&n); assert( pNew->type!=0 ); pNew->errTok = *pStr; pNew->type->xInit(p, pNew); pik_add_txt(p, pStr, pStr->eCode); return pNew; } if( pId ){ const PClass *pClass; pNew->errTok = *pId; pClass = pik_find_class(pId); if( pClass ){ pNew->type = pClass; pNew->sw = pik_value(p, "thickness",9,0); pNew->fill = pik_value(p, "fill",4,0); pNew->color = pik_value(p, "color",5,0); pClass->xInit(p, pNew); return pNew; } pik_error(p, pId, "unknown object type"); pik_elem_free(p, pNew); return 0; } pNew->type = &noopClass; pNew->ptExit = pNew->ptEnter = pNew->ptAt; return pNew; } /* ** If the ID token in the argument is the name of a macro, return ** the PMacro object for that macro */ static PMacro *pik_find_macro(Pik *p, PToken *pId){ PMacro *pMac; for(pMac = p->pMacros; pMac; pMac=pMac->pNext){ if( pMac->macroName.n==pId->n && strncmp(pMac->macroName.z,pId->z,pId->n)==0 ){ return pMac; } } return 0; } /* Add a new macro */ static void pik_add_macro( Pik *p, /* Current Pikchr diagram */ PToken *pId, /* The ID token that defines the macro name */ PToken *pCode /* Macro body inside of {...} */ ){ PMacro *pNew = pik_find_macro(p, pId); if( pNew==0 ){ pNew = malloc( sizeof(*pNew) ); if( pNew==0 ){ pik_error(p, 0, 0); return; } pNew->pNext = p->pMacros; p->pMacros = pNew; pNew->macroName = *pId; } pNew->macroBody.z = pCode->z+1; pNew->macroBody.n = pCode->n-2; pNew->inUse = 0; } /* ** Set the output direction and exit point for an object */ static void pik_elem_set_exit(PObj *pObj, int eDir){ assert( ValidDir(eDir) ); pObj->outDir = eDir; if( !pObj->type->isLine || pObj->bClose ){ pObj->ptExit = pObj->ptAt; switch( pObj->outDir ){ default: pObj->ptExit.x += pObj->w*0.5; break; case DIR_LEFT: pObj->ptExit.x -= pObj->w*0.5; break; case DIR_UP: pObj->ptExit.y += pObj->h*0.5; break; case DIR_DOWN: pObj->ptExit.y -= pObj->h*0.5; break; } } } /* Change the layout direction. */ static void pik_set_direction(Pik *p, int eDir){ assert( ValidDir(eDir) ); p->eDir = (unsigned char)eDir; /* It seems to make sense to reach back into the last object and ** change its exit point (its ".end") to correspond to the new ** direction. Things just seem to work better this way. However, ** legacy PIC does *not* do this. ** ** The difference can be seen in a script like this: ** ** arrow; circle; down; arrow ** ** You can make pikchr render the above exactly like PIC ** by deleting the following three lines. But I (drh) think ** it works better with those lines in place. */ if( p->list && p->list->n ){ pik_elem_set_exit(p->list->a[p->list->n-1], eDir); } } /* Move all coordinates contained within an object (and within its ** substructure) by dx, dy */ static void pik_elem_move(PObj *pObj, PNum dx, PNum dy){ int i; pObj->ptAt.x += dx; pObj->ptAt.y += dy; pObj->ptEnter.x += dx; pObj->ptEnter.y += dy; pObj->ptExit.x += dx; pObj->ptExit.y += dy; pObj->bbox.ne.x += dx; pObj->bbox.ne.y += dy; pObj->bbox.sw.x += dx; pObj->bbox.sw.y += dy; for(i=0; inPath; i++){ pObj->aPath[i].x += dx; pObj->aPath[i].y += dy; } if( pObj->pSublist ){ pik_elist_move(pObj->pSublist, dx, dy); } } static void pik_elist_move(PList *pList, PNum dx, PNum dy){ int i; for(i=0; in; i++){ pik_elem_move(pList->a[i], dx, dy); } } /* ** Check to see if it is ok to set the value of paraemeter mThis. ** Return 0 if it is ok. If it not ok, generate an appropriate ** error message and return non-zero. ** ** Flags are set in pObj so that the same object or conflicting ** objects may not be set again. ** ** To be ok, bit mThis must be clear and no more than one of ** the bits identified by mBlockers may be set. */ static int pik_param_ok( Pik *p, /* For storing the error message (if any) */ PObj *pObj, /* The object under construction */ PToken *pId, /* Make the error point to this token */ int mThis /* Value we are trying to set */ ){ if( pObj->mProp & mThis ){ pik_error(p, pId, "value is already set"); return 1; } if( pObj->mCalc & mThis ){ pik_error(p, pId, "value already fixed by prior constraints"); return 1; } pObj->mProp |= mThis; return 0; } /* ** Set a numeric property like "width 7" or "radius 200%". ** ** The rAbs term is an absolute value to add in. rRel is ** a relative value by which to change the current value. */ void pik_set_numprop(Pik *p, PToken *pId, PRel *pVal){ PObj *pObj = p->cur; switch( pId->eType ){ case T_HEIGHT: if( pik_param_ok(p, pObj, pId, A_HEIGHT) ) return; pObj->h = pObj->h*pVal->rRel + pVal->rAbs; break; case T_WIDTH: if( pik_param_ok(p, pObj, pId, A_WIDTH) ) return; pObj->w = pObj->w*pVal->rRel + pVal->rAbs; break; case T_RADIUS: if( pik_param_ok(p, pObj, pId, A_RADIUS) ) return; pObj->rad = pObj->rad*pVal->rRel + pVal->rAbs; break; case T_DIAMETER: if( pik_param_ok(p, pObj, pId, A_RADIUS) ) return; pObj->rad = pObj->rad*pVal->rRel + 0.5*pVal->rAbs; /* diam it 2x rad */ break; case T_THICKNESS: if( pik_param_ok(p, pObj, pId, A_THICKNESS) ) return; pObj->sw = pObj->sw*pVal->rRel + pVal->rAbs; break; } if( pObj->type->xNumProp ){ pObj->type->xNumProp(p, pObj, pId); } return; } /* ** Set a color property. The argument is an RGB value. */ void pik_set_clrprop(Pik *p, PToken *pId, PNum rClr){ PObj *pObj = p->cur; switch( pId->eType ){ case T_FILL: if( pik_param_ok(p, pObj, pId, A_FILL) ) return; pObj->fill = rClr; break; case T_COLOR: if( pik_param_ok(p, pObj, pId, A_COLOR) ) return; pObj->color = rClr; break; } if( pObj->type->xNumProp ){ pObj->type->xNumProp(p, pObj, pId); } return; } /* ** Set a "dashed" property like "dash 0.05" ** ** Use the value supplied by pVal if available. If pVal==0, use ** a default. */ void pik_set_dashed(Pik *p, PToken *pId, PNum *pVal){ PObj *pObj = p->cur; PNum v; switch( pId->eType ){ case T_DOTTED: { v = pVal==0 ? pik_value(p,"dashwid",7,0) : *pVal; pObj->dotted = v; pObj->dashed = 0.0; break; } case T_DASHED: { v = pVal==0 ? pik_value(p,"dashwid",7,0) : *pVal; pObj->dashed = v; pObj->dotted = 0.0; break; } } } /* ** If the current path information came from a "same" or "same as" ** reset it. */ static void pik_reset_samepath(Pik *p){ if( p->samePath ){ p->samePath = 0; p->nTPath = 1; } } /* Add a new term to the path for a line-oriented object by transferring ** the information in the ptTo field over onto the path and into ptFrom ** resetting the ptTo. */ static void pik_then(Pik *p, PToken *pToken, PObj *pObj){ int n; if( !pObj->type->isLine ){ pik_error(p, pToken, "use with line-oriented objects only"); return; } n = p->nTPath - 1; if( n<1 && (pObj->mProp & A_FROM)==0 ){ pik_error(p, pToken, "no prior path points"); return; } p->thenFlag = 1; } /* Advance to the next entry in p->aTPath. Return its index. */ static int pik_next_rpath(Pik *p, PToken *pErr){ int n = p->nTPath - 1; if( n+1>=(int)count(p->aTPath) ){ pik_error(0, pErr, "too many path elements"); return n; } n++; p->nTPath++; p->aTPath[n] = p->aTPath[n-1]; p->mTPath = 0; return n; } /* Add a direction term to an object. "up 0.5", or "left 3", or "down" ** or "down 50%". */ static void pik_add_direction(Pik *p, PToken *pDir, PRel *pVal){ PObj *pObj = p->cur; int n; int dir; if( !pObj->type->isLine ){ if( pDir ){ pik_error(p, pDir, "use with line-oriented objects only"); }else{ PToken x = pik_next_semantic_token(&pObj->errTok); pik_error(p, &x, "syntax error"); } return; } pik_reset_samepath(p); n = p->nTPath - 1; if( p->thenFlag || p->mTPath==3 || n==0 ){ n = pik_next_rpath(p, pDir); p->thenFlag = 0; } dir = pDir ? pDir->eCode : p->eDir; switch( dir ){ case DIR_UP: if( p->mTPath & 2 ) n = pik_next_rpath(p, pDir); p->aTPath[n].y += pVal->rAbs + pObj->h*pVal->rRel; p->mTPath |= 2; break; case DIR_DOWN: if( p->mTPath & 2 ) n = pik_next_rpath(p, pDir); p->aTPath[n].y -= pVal->rAbs + pObj->h*pVal->rRel; p->mTPath |= 2; break; case DIR_RIGHT: if( p->mTPath & 1 ) n = pik_next_rpath(p, pDir); p->aTPath[n].x += pVal->rAbs + pObj->w*pVal->rRel; p->mTPath |= 1; break; case DIR_LEFT: if( p->mTPath & 1 ) n = pik_next_rpath(p, pDir); p->aTPath[n].x -= pVal->rAbs + pObj->w*pVal->rRel; p->mTPath |= 1; break; } pObj->outDir = dir; } /* Process a movement attribute of one of these forms: ** ** pDist pHdgKW rHdg pEdgept ** GO distance HEADING angle ** GO distance compasspoint */ static void pik_move_hdg( Pik *p, /* The Pikchr context */ PRel *pDist, /* Distance to move */ PToken *pHeading, /* "heading" keyword if present */ PNum rHdg, /* Angle argument to "heading" keyword */ PToken *pEdgept, /* EDGEPT keyword "ne", "sw", etc... */ PToken *pErr /* Token to use for error messages */ ){ PObj *pObj = p->cur; int n; PNum rDist = pDist->rAbs + pik_value(p,"linewid",7,0)*pDist->rRel; if( !pObj->type->isLine ){ pik_error(p, pErr, "use with line-oriented objects only"); return; } pik_reset_samepath(p); do{ n = pik_next_rpath(p, pErr); }while( n<1 ); if( pHeading ){ rHdg = fmod(rHdg,360.0); }else if( pEdgept->eEdge==CP_C ){ pik_error(p, pEdgept, "syntax error"); return; }else{ rHdg = pik_hdg_angle[pEdgept->eEdge]; } if( rHdg<=45.0 ){ pObj->outDir = DIR_UP; }else if( rHdg<=135.0 ){ pObj->outDir = DIR_RIGHT; }else if( rHdg<=225.0 ){ pObj->outDir = DIR_DOWN; }else if( rHdg<=315.0 ){ pObj->outDir = DIR_LEFT; }else{ pObj->outDir = DIR_UP; } rHdg *= 0.017453292519943295769; /* degrees to radians */ p->aTPath[n].x += rDist*sin(rHdg); p->aTPath[n].y += rDist*cos(rHdg); p->mTPath = 2; } /* Process a movement attribute of the form "right until even with ..." ** ** pDir is the first keyword, "right" or "left" or "up" or "down". ** The movement is in that direction until its closest approach to ** the point specified by pPoint. */ static void pik_evenwith(Pik *p, PToken *pDir, PPoint *pPlace){ PObj *pObj = p->cur; int n; if( !pObj->type->isLine ){ pik_error(p, pDir, "use with line-oriented objects only"); return; } pik_reset_samepath(p); n = p->nTPath - 1; if( p->thenFlag || p->mTPath==3 || n==0 ){ n = pik_next_rpath(p, pDir); p->thenFlag = 0; } switch( pDir->eCode ){ case DIR_DOWN: case DIR_UP: if( p->mTPath & 2 ) n = pik_next_rpath(p, pDir); p->aTPath[n].y = pPlace->y; p->mTPath |= 2; break; case DIR_RIGHT: case DIR_LEFT: if( p->mTPath & 1 ) n = pik_next_rpath(p, pDir); p->aTPath[n].x = pPlace->x; p->mTPath |= 1; break; } pObj->outDir = pDir->eCode; } /* If the last referenced object is centered at point pPt then return ** a pointer to that object. If there is no prior object reference, ** or if the points are not the same, return NULL. ** ** This is a side-channel hack used to find the objects at which a ** line begins and ends. For example, in ** ** arrow from OBJ1 to OBJ2 chop ** ** The arrow object is normally just handed the coordinates of the ** centers for OBJ1 and OBJ2. But we also want to know the specific ** object named in case there are multiple objects centered at the ** same point. ** ** See forum post 1d46e3a0bc */ static PObj *pik_last_ref_object(Pik *p, PPoint *pPt){ PObj *pRes = 0; if( p->lastRef==0 ) return 0; if( p->lastRef->ptAt.x==pPt->x && p->lastRef->ptAt.y==pPt->y ){ pRes = p->lastRef; } p->lastRef = 0; return pRes; } /* Set the "from" of an object */ static void pik_set_from(Pik *p, PObj *pObj, PToken *pTk, PPoint *pPt){ if( !pObj->type->isLine ){ pik_error(p, pTk, "use \"at\" to position this object"); return; } if( pObj->mProp & A_FROM ){ pik_error(p, pTk, "line start location already fixed"); return; } if( pObj->bClose ){ pik_error(p, pTk, "polygon is closed"); return; } if( p->nTPath>1 ){ PNum dx = pPt->x - p->aTPath[0].x; PNum dy = pPt->y - p->aTPath[0].y; int i; for(i=1; inTPath; i++){ p->aTPath[i].x += dx; p->aTPath[i].y += dy; } } p->aTPath[0] = *pPt; p->mTPath = 3; pObj->mProp |= A_FROM; pObj->pFrom = pik_last_ref_object(p, pPt); } /* Set the "to" of an object */ static void pik_add_to(Pik *p, PObj *pObj, PToken *pTk, PPoint *pPt){ int n = p->nTPath-1; if( !pObj->type->isLine ){ pik_error(p, pTk, "use \"at\" to position this object"); return; } if( pObj->bClose ){ pik_error(p, pTk, "polygon is closed"); return; } pik_reset_samepath(p); if( n==0 || p->mTPath==3 || p->thenFlag ){ n = pik_next_rpath(p, pTk); } p->aTPath[n] = *pPt; p->mTPath = 3; pObj->pTo = pik_last_ref_object(p, pPt); } static void pik_close_path(Pik *p, PToken *pErr){ PObj *pObj = p->cur; if( p->nTPath<3 ){ pik_error(p, pErr, "need at least 3 vertexes in order to close the polygon"); return; } if( pObj->bClose ){ pik_error(p, pErr, "polygon already closed"); return; } pObj->bClose = 1; } /* Lower the layer of the current object so that it is behind the ** given object. */ static void pik_behind(Pik *p, PObj *pOther){ PObj *pObj = p->cur; if( p->nErr==0 && pObj->iLayer>=pOther->iLayer ){ pObj->iLayer = pOther->iLayer - 1; } } /* Set the "at" of an object */ static void pik_set_at(Pik *p, PToken *pEdge, PPoint *pAt, PToken *pErrTok){ PObj *pObj; static unsigned char eDirToCp[] = { CP_E, CP_S, CP_W, CP_N }; if( p->nErr ) return; pObj = p->cur; if( pObj->type->isLine ){ pik_error(p, pErrTok, "use \"from\" and \"to\" to position this object"); return; } if( pObj->mProp & A_AT ){ pik_error(p, pErrTok, "location fixed by prior \"at\""); return; } pObj->mProp |= A_AT; pObj->eWith = pEdge ? pEdge->eEdge : CP_C; if( pObj->eWith>=CP_END ){ int dir = pObj->eWith==CP_END ? pObj->outDir : (pObj->inDir+2)%4; pObj->eWith = eDirToCp[dir]; } pObj->with = *pAt; } /* ** Try to add a text attribute to an object */ static void pik_add_txt(Pik *p, PToken *pTxt, int iPos){ PObj *pObj = p->cur; PToken *pT; if( pObj->nTxt >= count(pObj->aTxt) ){ pik_error(p, pTxt, "too many text terms"); return; } pT = &pObj->aTxt[pObj->nTxt++]; *pT = *pTxt; pT->eCode = (short)iPos; } /* Merge "text-position" flags */ static int pik_text_position(int iPrev, PToken *pFlag){ int iRes = iPrev; switch( pFlag->eType ){ case T_LJUST: iRes = (iRes&~TP_JMASK) | TP_LJUST; break; case T_RJUST: iRes = (iRes&~TP_JMASK) | TP_RJUST; break; case T_ABOVE: iRes = (iRes&~TP_VMASK) | TP_ABOVE; break; case T_CENTER: iRes = (iRes&~TP_VMASK) | TP_CENTER; break; case T_BELOW: iRes = (iRes&~TP_VMASK) | TP_BELOW; break; case T_ITALIC: iRes |= TP_ITALIC; break; case T_BOLD: iRes |= TP_BOLD; break; case T_MONO: iRes |= TP_MONO; break; case T_ALIGNED: iRes |= TP_ALIGN; break; case T_BIG: if( iRes & TP_BIG ) iRes |= TP_XTRA; else iRes = (iRes &~TP_SZMASK)|TP_BIG; break; case T_SMALL: if( iRes & TP_SMALL ) iRes |= TP_XTRA; else iRes = (iRes &~TP_SZMASK)|TP_SMALL; break; } return iRes; } /* ** Table of scale-factor estimates for variable-width characters. ** Actual character widths vary by font. These numbers are only ** guesses. And this table only provides data for ASCII. ** ** 100 means normal width. */ static const unsigned char awChar[] = { /* Skip initial 32 control characters */ /* ' ' */ 45, /* '!' */ 55, /* '"' */ 62, /* '#' */ 115, /* '$' */ 90, /* '%' */ 132, /* '&' */ 125, /* '\''*/ 40, /* '(' */ 55, /* ')' */ 55, /* '*' */ 71, /* '+' */ 115, /* ',' */ 45, /* '-' */ 48, /* '.' */ 45, /* '/' */ 50, /* '0' */ 91, /* '1' */ 91, /* '2' */ 91, /* '3' */ 91, /* '4' */ 91, /* '5' */ 91, /* '6' */ 91, /* '7' */ 91, /* '8' */ 91, /* '9' */ 91, /* ':' */ 50, /* ';' */ 50, /* '<' */ 120, /* '=' */ 120, /* '>' */ 120, /* '?' */ 78, /* '@' */ 142, /* 'A' */ 102, /* 'B' */ 105, /* 'C' */ 110, /* 'D' */ 115, /* 'E' */ 105, /* 'F' */ 98, /* 'G' */ 105, /* 'H' */ 125, /* 'I' */ 58, /* 'J' */ 58, /* 'K' */ 107, /* 'L' */ 95, /* 'M' */ 145, /* 'N' */ 125, /* 'O' */ 115, /* 'P' */ 95, /* 'Q' */ 115, /* 'R' */ 107, /* 'S' */ 95, /* 'T' */ 97, /* 'U' */ 118, /* 'V' */ 102, /* 'W' */ 150, /* 'X' */ 100, /* 'Y' */ 93, /* 'Z' */ 100, /* '[' */ 58, /* '\\'*/ 50, /* ']' */ 58, /* '^' */ 119, /* '_' */ 72, /* '`' */ 72, /* 'a' */ 86, /* 'b' */ 92, /* 'c' */ 80, /* 'd' */ 92, /* 'e' */ 85, /* 'f' */ 52, /* 'g' */ 92, /* 'h' */ 92, /* 'i' */ 47, /* 'j' */ 47, /* 'k' */ 88, /* 'l' */ 48, /* 'm' */ 135, /* 'n' */ 92, /* 'o' */ 86, /* 'p' */ 92, /* 'q' */ 92, /* 'r' */ 69, /* 's' */ 75, /* 't' */ 58, /* 'u' */ 92, /* 'v' */ 80, /* 'w' */ 121, /* 'x' */ 81, /* 'y' */ 80, /* 'z' */ 76, /* '{' */ 91, /* '|'*/ 49, /* '}' */ 91, /* '~' */ 118, }; /* Return an estimate of the width of the displayed characters ** in a character string. The returned value is 100 times the ** average character width. ** ** Omit "\" used to escape characters. And count entities like ** "<" as a single character. Multi-byte UTF8 characters count ** as a single character. ** ** Unless using a monospaced font, attempt to scale the answer by ** the actual characters seen. Wide characters count more than ** narrow characters. But the widths are only guesses. ** */ static int pik_text_length(const PToken *pToken, const int isMonospace){ const int stdAvg=100, monoAvg=82; int n = pToken->n; const char *z = pToken->z; int cnt, j; for(j=1, cnt=0; j= 0x20 && c <= 0x7e ){ cnt += awChar[c-0x20]; }else{ cnt += stdAvg; } } return cnt; } /* Adjust the width, height, and/or radius of the object so that ** it fits around the text that has been added so far. ** ** (1) Only text specified prior to this attribute is considered. ** (2) The text size is estimated based on the charht and charwid ** variable settings. ** (3) The fitted attributes can be changed again after this ** attribute, for example using "width 110%" if this auto-fit ** underestimates the text size. ** (4) Previously set attributes will not be altered. In other words, ** "width 1in fit" might cause the height to change, but the ** width is now set. ** (5) This only works for attributes that have an xFit method. ** ** The eWhich parameter is: ** ** 1: Fit horizontally only ** 2: Fit vertically only ** 3: Fit both ways */ static void pik_size_to_fit(Pik *p, PToken *pFit, int eWhich){ PObj *pObj; PNum w, h; PBox bbox; if( p->nErr ) return; pObj = p->cur; if( pObj->nTxt==0 ){ pik_error(0, pFit, "no text to fit to"); return; } if( pObj->type->xFit==0 ) return; pik_bbox_init(&bbox); pik_compute_layout_settings(p); pik_append_txt(p, pObj, &bbox); if( (eWhich & 1)!=0 || pObj->bAltAutoFit ){ w = (bbox.ne.x - bbox.sw.x) + p->charWidth; }else{ w = 0; } if( (eWhich & 2)!=0 || pObj->bAltAutoFit ){ PNum h1, h2; h1 = (bbox.ne.y - pObj->ptAt.y); h2 = (pObj->ptAt.y - bbox.sw.y); h = 2.0*( h1

charHeight; }else{ h = 0; } pObj->type->xFit(p, pObj, w, h); pObj->mProp |= A_FIT; } /* Set a local variable name to "val". ** ** The name might be a built-in variable or a color name. In either case, ** a new application-defined variable is set. Since app-defined variables ** are searched first, this will override any built-in variables. */ static void pik_set_var(Pik *p, PToken *pId, PNum val, PToken *pOp){ PVar *pVar = p->pVar; while( pVar ){ if( pik_token_eq(pId,pVar->zName)==0 ) break; pVar = pVar->pNext; } if( pVar==0 ){ char *z; pVar = malloc( pId->n+1 + sizeof(*pVar) ); if( pVar==0 ){ pik_error(p, 0, 0); return; } pVar->zName = z = (char*)&pVar[1]; memcpy(z, pId->z, pId->n); z[pId->n] = 0; pVar->pNext = p->pVar; pVar->val = pik_value(p, pId->z, pId->n, 0); p->pVar = pVar; } switch( pOp->eCode ){ case T_PLUS: pVar->val += val; break; case T_STAR: pVar->val *= val; break; case T_MINUS: pVar->val -= val; break; case T_SLASH: if( val==0.0 ){ pik_error(p, pOp, "division by zero"); }else{ pVar->val /= val; } break; default: pVar->val = val; break; } p->bLayoutVars = 0; /* Clear the layout setting cache */ } /* ** Round a PNum into the nearest integer */ static int pik_round(PNum v){ if( isnan(v) ) return 0; if( v < -2147483647 ) return (-2147483647-1); if( v >= 2147483647 ) return 2147483647; return (int)v; } /* ** Search for the variable named z[0..n-1] in: ** ** * Application defined variables ** * Built-in variables ** ** Return the value of the variable if found. If not found ** return 0.0. Also if pMiss is not NULL, then set it to 1 ** if not found. ** ** This routine is a subroutine to pik_get_var(). But it is also ** used by object implementations to look up (possibly overwritten) ** values for built-in variables like "boxwid". */ static PNum pik_value(Pik *p, const char *z, int n, int *pMiss){ PVar *pVar; int first, last, mid, c; for(pVar=p->pVar; pVar; pVar=pVar->pNext){ if( strncmp(pVar->zName,z,n)==0 && pVar->zName[n]==0 ){ return pVar->val; } } first = 0; last = count(aBuiltin)-1; while( first<=last ){ mid = (first+last)/2; c = strncmp(z,aBuiltin[mid].zName,n); if( c==0 && aBuiltin[mid].zName[n] ) c = 1; if( c==0 ) return aBuiltin[mid].val; if( c>0 ){ first = mid+1; }else{ last = mid-1; } } if( pMiss ) *pMiss = 1; return 0.0; } static int pik_value_int(Pik *p, const char *z, int n, int *pMiss){ return pik_round(pik_value(p,z,n,pMiss)); } /* ** Look up a color-name. Unlike other names in this program, the ** color-names are not case sensitive. So "DarkBlue" and "darkblue" ** and "DARKBLUE" all find the same value (139). ** ** If not found, return -99.0. Also post an error if p!=NULL. ** ** Special color names "None" and "Off" return -1.0 without causing ** an error. */ static PNum pik_lookup_color(Pik *p, PToken *pId){ int first, last, mid, c = 0; first = 0; last = count(aColor)-1; while( first<=last ){ const char *zClr; int c1, c2; unsigned int i; mid = (first+last)/2; zClr = aColor[mid].zName; for(i=0; in; i++){ c1 = zClr[i]&0x7f; if( IsUpper(c1) ) c1 = ToLower(c1); c2 = pId->z[i]&0x7f; if( IsUpper(c2) ) c2 = ToLower(c2); c = c2 - c1; if( c ) break; } if( c==0 && aColor[mid].zName[pId->n] ) c = -1; if( c==0 ) return (double)aColor[mid].val; if( c>0 ){ first = mid+1; }else{ last = mid-1; } } if( p ) pik_error(p, pId, "not a known color name"); return -99.0; } /* Get the value of a variable. ** ** Search in order: ** ** * Application defined variables ** * Built-in variables ** * Color names ** ** If no such variable is found, throw an error. */ static PNum pik_get_var(Pik *p, PToken *pId){ int miss = 0; PNum v = pik_value(p, pId->z, pId->n, &miss); if( miss==0 ) return v; v = pik_lookup_color(0, pId); if( v>-90.0 ) return v; pik_error(p,pId,"no such variable"); return 0.0; } /* Convert a T_NTH token (ex: "2nd", "5th"} into a numeric value and ** return that value. Throw an error if the value is too big. */ static short int pik_nth_value(Pik *p, PToken *pNth){ int i = atoi(pNth->z); if( i>1000 ){ pik_error(p, pNth, "value too big - max '1000th'"); i = 1; } if( i==0 && pik_token_eq(pNth,"first")==0 ) i = 1; return (short int)i; } /* Search for the NTH object. ** ** If pBasis is not NULL then it should be a [] object. Use the ** sublist of that [] object for the search. If pBasis is not a [] ** object, then throw an error. ** ** The pNth token describes the N-th search. The pNth->eCode value ** is one more than the number of items to skip. It is negative ** to search backwards. If pNth->eType==T_ID, then it is the name ** of a class to search for. If pNth->eType==T_LB, then ** search for a [] object. If pNth->eType==T_LAST, then search for ** any type. ** ** Raise an error if the item is not found. */ static PObj *pik_find_nth(Pik *p, PObj *pBasis, PToken *pNth){ PList *pList; int i, n; const PClass *pClass; if( pBasis==0 ){ pList = p->list; }else{ pList = pBasis->pSublist; } if( pList==0 ){ pik_error(p, pNth, "no such object"); return 0; } if( pNth->eType==T_LAST ){ pClass = 0; }else if( pNth->eType==T_LB ){ pClass = &sublistClass; }else{ pClass = pik_find_class(pNth); if( pClass==0 ){ pik_error(0, pNth, "no such object type"); return 0; } } n = pNth->eCode; if( n<0 ){ for(i=pList->n-1; i>=0; i--){ PObj *pObj = pList->a[i]; if( pClass && pObj->type!=pClass ) continue; n++; if( n==0 ){ return pObj; } } }else{ for(i=0; in; i++){ PObj *pObj = pList->a[i]; if( pClass && pObj->type!=pClass ) continue; n--; if( n==0 ){ return pObj; } } } pik_error(p, pNth, "no such object"); return 0; } /* Search for an object by name. ** ** Search in pBasis->pSublist if pBasis is not NULL. If pBasis is NULL ** then search in p->list. */ static PObj *pik_find_byname(Pik *p, PObj *pBasis, PToken *pName){ PList *pList; int i, j; if( pBasis==0 ){ pList = p->list; }else{ pList = pBasis->pSublist; } if( pList==0 ){ pik_error(p, pName, "no such object"); return 0; } /* First look explicitly tagged objects */ for(i=pList->n-1; i>=0; i--){ PObj *pObj = pList->a[i]; if( pObj->zName && pik_token_eq(pName,pObj->zName)==0 ){ p->lastRef = pObj; return pObj; } } /* If not found, do a second pass looking for any object containing ** text which exactly matches pName */ for(i=pList->n-1; i>=0; i--){ PObj *pObj = pList->a[i]; for(j=0; jnTxt; j++){ if( pObj->aTxt[j].n==pName->n+2 && memcmp(pObj->aTxt[j].z+1,pName->z,pName->n)==0 ){ p->lastRef = pObj; return pObj; } } } pik_error(p, pName, "no such object"); return 0; } /* Change most of the settings for the current object to be the ** same as the pOther object, or the most recent object of the same ** type if pOther is NULL. */ static void pik_same(Pik *p, PObj *pOther, PToken *pErrTok){ PObj *pObj = p->cur; if( p->nErr ) return; if( pOther==0 ){ int i; for(i=(p->list ? p->list->n : 0)-1; i>=0; i--){ pOther = p->list->a[i]; if( pOther->type==pObj->type ) break; } if( i<0 ){ pik_error(p, pErrTok, "no prior objects of the same type"); return; } } if( pOther->nPath && pObj->type->isLine ){ PNum dx, dy; int i; dx = p->aTPath[0].x - pOther->aPath[0].x; dy = p->aTPath[0].y - pOther->aPath[0].y; for(i=1; inPath; i++){ p->aTPath[i].x = pOther->aPath[i].x + dx; p->aTPath[i].y = pOther->aPath[i].y + dy; } p->nTPath = pOther->nPath; p->mTPath = 3; p->samePath = 1; } if( !pObj->type->isLine ){ pObj->w = pOther->w; pObj->h = pOther->h; } pObj->rad = pOther->rad; pObj->sw = pOther->sw; pObj->dashed = pOther->dashed; pObj->dotted = pOther->dotted; pObj->fill = pOther->fill; pObj->color = pOther->color; pObj->cw = pOther->cw; pObj->larrow = pOther->larrow; pObj->rarrow = pOther->rarrow; pObj->bClose = pOther->bClose; pObj->bChop = pOther->bChop; pObj->iLayer = pOther->iLayer; } /* Return a "Place" associated with object pObj. If pEdge is NULL ** return the center of the object. Otherwise, return the corner ** described by pEdge. */ static PPoint pik_place_of_elem(Pik *p, PObj *pObj, PToken *pEdge){ PPoint pt = cZeroPoint; const PClass *pClass; if( pObj==0 ) return pt; if( pEdge==0 ){ return pObj->ptAt; } pClass = pObj->type; if( pEdge->eType==T_EDGEPT || (pEdge->eEdge>0 && pEdge->eEdgexOffset(p, pObj, pEdge->eEdge); pt.x += pObj->ptAt.x; pt.y += pObj->ptAt.y; return pt; } if( pEdge->eType==T_START ){ return pObj->ptEnter; }else{ return pObj->ptExit; } } /* Do a linear interpolation of two positions. */ static PPoint pik_position_between(PNum x, PPoint p1, PPoint p2){ PPoint out; out.x = p2.x*x + p1.x*(1.0 - x); out.y = p2.y*x + p1.y*(1.0 - x); return out; } /* Compute the position that is dist away from pt at an heading angle of r ** ** The angle is a compass heading in degrees. North is 0 (or 360). ** East is 90. South is 180. West is 270. And so forth. */ static PPoint pik_position_at_angle(PNum dist, PNum r, PPoint pt){ r *= 0.017453292519943295769; /* degrees to radians */ pt.x += dist*sin(r); pt.y += dist*cos(r); return pt; } /* Compute the position that is dist away at a compass point */ static PPoint pik_position_at_hdg(PNum dist, PToken *pD, PPoint pt){ return pik_position_at_angle(dist, pik_hdg_angle[pD->eEdge], pt); } /* Return the coordinates for the n-th vertex of a line. */ static PPoint pik_nth_vertex(Pik *p, PToken *pNth, PToken *pErr, PObj *pObj){ static const PPoint zero = {0, 0}; int n; if( p->nErr || pObj==0 ) return p->aTPath[0]; if( !pObj->type->isLine ){ pik_error(p, pErr, "object is not a line"); return zero; } n = atoi(pNth->z); if( n<1 || n>pObj->nPath ){ pik_error(p, pNth, "no such vertex"); return zero; } return pObj->aPath[n-1]; } /* Return the value of a property of an object. */ static PNum pik_property_of(PObj *pObj, PToken *pProp){ PNum v = 0.0; if( pObj ){ switch( pProp->eType ){ case T_HEIGHT: v = pObj->h; break; case T_WIDTH: v = pObj->w; break; case T_RADIUS: v = pObj->rad; break; case T_DIAMETER: v = pObj->rad*2.0; break; case T_THICKNESS: v = pObj->sw; break; case T_DASHED: v = pObj->dashed; break; case T_DOTTED: v = pObj->dotted; break; case T_FILL: v = pObj->fill; break; case T_COLOR: v = pObj->color; break; case T_X: v = pObj->ptAt.x; break; case T_Y: v = pObj->ptAt.y; break; case T_TOP: v = pObj->bbox.ne.y; break; case T_BOTTOM: v = pObj->bbox.sw.y; break; case T_LEFT: v = pObj->bbox.sw.x; break; case T_RIGHT: v = pObj->bbox.ne.x; break; } } return v; } /* Compute one of the built-in functions */ static PNum pik_func(Pik *p, PToken *pFunc, PNum x, PNum y){ PNum v = 0.0; switch( pFunc->eCode ){ case FN_ABS: v = x<0.0 ? -x : x; break; case FN_COS: v = cos(x); break; case FN_INT: v = rint(x); break; case FN_SIN: v = sin(x); break; case FN_SQRT: if( x<0.0 ){ pik_error(p, pFunc, "sqrt of negative value"); v = 0.0; }else{ v = sqrt(x); } break; case FN_MAX: v = x>y ? x : y; break; case FN_MIN: v = xzName); pObj->zName = malloc(pName->n+1); if( pObj->zName==0 ){ pik_error(p,0,0); }else{ memcpy(pObj->zName,pName->z,pName->n); pObj->zName[pName->n] = 0; } return; } /* ** Search for object located at *pCenter that has an xChop method and ** that does not enclose point pOther. ** ** Return a pointer to the object, or NULL if not found. */ static PObj *pik_find_chopper(PList *pList, PPoint *pCenter, PPoint *pOther){ int i; if( pList==0 ) return 0; for(i=pList->n-1; i>=0; i--){ PObj *pObj = pList->a[i]; if( pObj->type->xChop!=0 && pObj->ptAt.x==pCenter->x && pObj->ptAt.y==pCenter->y && !pik_bbox_contains_point(&pObj->bbox, pOther) ){ return pObj; }else if( pObj->pSublist ){ pObj = pik_find_chopper(pObj->pSublist,pCenter,pOther); if( pObj ) return pObj; } } return 0; } /* ** There is a line traveling from pFrom to pTo. ** ** If pObj is not null and is a choppable object, then chop at ** the boundary of pObj - where the line crosses the boundary ** of pObj. ** ** If pObj is NULL or has no xChop method, then search for some ** other object centered at pTo that is choppable and use it ** instead. */ static void pik_autochop(Pik *p, PPoint *pFrom, PPoint *pTo, PObj *pObj){ if( pObj==0 || pObj->type->xChop==0 ){ pObj = pik_find_chopper(p->list, pTo, pFrom); } if( pObj ){ *pTo = pObj->type->xChop(p, pObj, pFrom); } } /* This routine runs after all attributes have been received ** on an object. */ static void pik_after_adding_attributes(Pik *p, PObj *pObj){ int i; PPoint ofst; PNum dx, dy; if( p->nErr ) return; /* Position block objects */ if( pObj->type->isLine==0 ){ /* A height or width less than or equal to zero means "autofit". ** Change the height or width to be big enough to contain the text, */ if( pObj->h<=0.0 ){ if( pObj->nTxt==0 ){ pObj->h = 0.0; }else if( pObj->w<=0.0 ){ pik_size_to_fit(p, &pObj->errTok, 3); }else{ pik_size_to_fit(p, &pObj->errTok, 2); } } if( pObj->w<=0.0 ){ if( pObj->nTxt==0 ){ pObj->w = 0.0; }else{ pik_size_to_fit(p, &pObj->errTok, 1); } } ofst = pik_elem_offset(p, pObj, pObj->eWith); dx = (pObj->with.x - ofst.x) - pObj->ptAt.x; dy = (pObj->with.y - ofst.y) - pObj->ptAt.y; if( dx!=0 || dy!=0 ){ pik_elem_move(pObj, dx, dy); } } /* For a line object with no movement specified, a single movement ** of the default length in the current direction */ if( pObj->type->isLine && p->nTPath<2 ){ pik_next_rpath(p, 0); assert( p->nTPath==2 ); switch( pObj->inDir ){ default: p->aTPath[1].x += pObj->w; break; case DIR_DOWN: p->aTPath[1].y -= pObj->h; break; case DIR_LEFT: p->aTPath[1].x -= pObj->w; break; case DIR_UP: p->aTPath[1].y += pObj->h; break; } if( pObj->type->xInit==arcInit ){ pObj->outDir = (pObj->inDir + (pObj->cw ? 1 : 3))%4; p->eDir = (unsigned char)pObj->outDir; switch( pObj->outDir ){ default: p->aTPath[1].x += pObj->w; break; case DIR_DOWN: p->aTPath[1].y -= pObj->h; break; case DIR_LEFT: p->aTPath[1].x -= pObj->w; break; case DIR_UP: p->aTPath[1].y += pObj->h; break; } } } /* Initialize the bounding box prior to running xCheck */ pik_bbox_init(&pObj->bbox); /* Run object-specific code */ if( pObj->type->xCheck!=0 ){ pObj->type->xCheck(p,pObj); if( p->nErr ) return; } /* Compute final bounding box, entry and exit points, center ** point (ptAt) and path for the object */ if( pObj->type->isLine ){ pObj->aPath = malloc( sizeof(PPoint)*p->nTPath ); if( pObj->aPath==0 ){ pik_error(p, 0, 0); return; }else{ pObj->nPath = p->nTPath; for(i=0; inTPath; i++){ pObj->aPath[i] = p->aTPath[i]; } } /* "chop" processing: ** If the line goes to the center of an object with an ** xChop method, then use the xChop method to trim the line. */ if( pObj->bChop && pObj->nPath>=2 ){ int n = pObj->nPath; pik_autochop(p, &pObj->aPath[n-2], &pObj->aPath[n-1], pObj->pTo); pik_autochop(p, &pObj->aPath[1], &pObj->aPath[0], pObj->pFrom); } pObj->ptEnter = pObj->aPath[0]; pObj->ptExit = pObj->aPath[pObj->nPath-1]; /* Compute the center of the line based on the bounding box over ** the vertexes. This is a difference from PIC. In Pikchr, the ** center of a line is the center of its bounding box. In PIC, the ** center of a line is halfway between its .start and .end. For ** straight lines, this is the same point, but for multi-segment ** lines the result is usually diferent */ for(i=0; inPath; i++){ pik_bbox_add_xy(&pObj->bbox, pObj->aPath[i].x, pObj->aPath[i].y); } pObj->ptAt.x = (pObj->bbox.ne.x + pObj->bbox.sw.x)/2.0; pObj->ptAt.y = (pObj->bbox.ne.y + pObj->bbox.sw.y)/2.0; /* Reset the width and height of the object to be the width and height ** of the bounding box over vertexes */ pObj->w = pObj->bbox.ne.x - pObj->bbox.sw.x; pObj->h = pObj->bbox.ne.y - pObj->bbox.sw.y; /* If this is a polygon (if it has the "close" attribute), then ** adjust the exit point */ if( pObj->bClose ){ /* For "closed" lines, the .end is one of the .e, .s, .w, or .n ** points of the bounding box, as with block objects. */ pik_elem_set_exit(pObj, pObj->inDir); } }else{ PNum w2 = pObj->w/2.0; PNum h2 = pObj->h/2.0; pObj->ptEnter = pObj->ptAt; pObj->ptExit = pObj->ptAt; switch( pObj->inDir ){ default: pObj->ptEnter.x -= w2; break; case DIR_LEFT: pObj->ptEnter.x += w2; break; case DIR_UP: pObj->ptEnter.y -= h2; break; case DIR_DOWN: pObj->ptEnter.y += h2; break; } switch( pObj->outDir ){ default: pObj->ptExit.x += w2; break; case DIR_LEFT: pObj->ptExit.x -= w2; break; case DIR_UP: pObj->ptExit.y += h2; break; case DIR_DOWN: pObj->ptExit.y -= h2; break; } pik_bbox_add_xy(&pObj->bbox, pObj->ptAt.x - w2, pObj->ptAt.y - h2); pik_bbox_add_xy(&pObj->bbox, pObj->ptAt.x + w2, pObj->ptAt.y + h2); } p->eDir = (unsigned char)pObj->outDir; } /* Show basic information about each object as a comment in the ** generated HTML. Used for testing and debugging. Activated ** by the (undocumented) "debug = 1;" ** command. */ static void pik_elem_render(Pik *p, PObj *pObj){ char *zDir; if( pObj==0 ) return; pik_append(p,"\n", -1); } /* Render a list of objects */ void pik_elist_render(Pik *p, PList *pList){ int i; int iNextLayer = 0; int iThisLayer; int bMoreToDo; int miss = 0; int mDebug = pik_value_int(p, "debug", 5, 0); PNum colorLabel; do{ bMoreToDo = 0; iThisLayer = iNextLayer; iNextLayer = 0x7fffffff; for(i=0; in; i++){ PObj *pObj = pList->a[i]; void (*xRender)(Pik*,PObj*); if( pObj->iLayer>iThisLayer ){ if( pObj->iLayeriLayer; bMoreToDo = 1; continue; /* Defer until another round */ }else if( pObj->iLayertype->xRender; if( xRender ){ xRender(p, pObj); } if( pObj->pSublist ){ pik_elist_render(p, pObj->pSublist); } } }while( bMoreToDo ); /* If the color_debug_label value is defined, then go through ** and paint a dot at every label location */ colorLabel = pik_value(p, "debug_label_color", 17, &miss); if( miss==0 && colorLabel>=0.0 ){ PObj dot; memset(&dot, 0, sizeof(dot)); dot.type = &noopClass; dot.rad = 0.015; dot.sw = 0.015; dot.fill = colorLabel; dot.color = colorLabel; dot.nTxt = 1; dot.aTxt[0].eCode = TP_ABOVE; for(i=0; in; i++){ PObj *pObj = pList->a[i]; if( pObj->zName==0 ) continue; dot.ptAt = pObj->ptAt; dot.aTxt[0].z = pObj->zName; dot.aTxt[0].n = (int)strlen(pObj->zName); dotRender(p, &dot); } } } /* Add all objects of the list pList to the bounding box */ static void pik_bbox_add_elist(Pik *p, PList *pList, PNum wArrow){ int i; for(i=0; in; i++){ PObj *pObj = pList->a[i]; if( pObj->sw>=0.0 ) pik_bbox_addbox(&p->bbox, &pObj->bbox); pik_append_txt(p, pObj, &p->bbox); if( pObj->pSublist ) pik_bbox_add_elist(p, pObj->pSublist, wArrow); /* Expand the bounding box to account for arrowheads on lines */ if( pObj->type->isLine && pObj->nPath>0 ){ if( pObj->larrow ){ pik_bbox_addellipse(&p->bbox, pObj->aPath[0].x, pObj->aPath[0].y, wArrow, wArrow); } if( pObj->rarrow ){ int j = pObj->nPath-1; pik_bbox_addellipse(&p->bbox, pObj->aPath[j].x, pObj->aPath[j].y, wArrow, wArrow); } } } } /* Recompute key layout parameters from variables. */ static void pik_compute_layout_settings(Pik *p){ PNum thickness; /* Line thickness */ PNum wArrow; /* Width of arrowheads */ /* Set up rendering parameters */ if( p->bLayoutVars ) return; thickness = pik_value(p,"thickness",9,0); if( thickness<=0.01 ) thickness = 0.01; wArrow = 0.5*pik_value(p,"arrowwid",8,0); p->wArrow = wArrow/thickness; p->hArrow = pik_value(p,"arrowht",7,0)/thickness; p->fontScale = pik_value(p,"fontscale",9,0); if( p->fontScale<=0.0 ) p->fontScale = 1.0; p->rScale = 144.0; p->charWidth = pik_value(p,"charwid",7,0)*p->fontScale; p->charHeight = pik_value(p,"charht",6,0)*p->fontScale; p->bLayoutVars = 1; } /* Render a list of objects. Write the SVG into p->zOut. ** Delete the input object_list before returnning. */ static void pik_render(Pik *p, PList *pList){ if( pList==0 ) return; if( p->nErr==0 ){ PNum thickness; /* Stroke width */ PNum margin; /* Extra bounding box margin */ PNum w, h; /* Drawing width and height */ PNum wArrow; PNum pikScale; /* Value of the "scale" variable */ int miss = 0; /* Set up rendering parameters */ pik_compute_layout_settings(p); thickness = pik_value(p,"thickness",9,0); if( thickness<=0.01 ) thickness = 0.01; margin = pik_value(p,"margin",6,0); margin += thickness; wArrow = p->wArrow*thickness; miss = 0; p->fgcolor = pik_value_int(p,"fgcolor",7,&miss); if( miss ){ PToken t; t.z = "fgcolor"; t.n = 7; p->fgcolor = pik_round(pik_lookup_color(0, &t)); } miss = 0; p->bgcolor = pik_value_int(p,"bgcolor",7,&miss); if( miss ){ PToken t; t.z = "bgcolor"; t.n = 7; p->bgcolor = pik_round(pik_lookup_color(0, &t)); } /* Compute a bounding box over all objects so that we can know ** how big to declare the SVG canvas */ pik_bbox_init(&p->bbox); pik_bbox_add_elist(p, pList, wArrow); /* Expand the bounding box slightly to account for line thickness ** and the optional "margin = EXPR" setting. */ p->bbox.ne.x += margin + pik_value(p,"rightmargin",11,0); p->bbox.ne.y += margin + pik_value(p,"topmargin",9,0); p->bbox.sw.x -= margin + pik_value(p,"leftmargin",10,0); p->bbox.sw.y -= margin + pik_value(p,"bottommargin",12,0); /* Output the SVG */ pik_append(p, "zClass ){ pik_append(p, " class=\"", -1); pik_append(p, p->zClass, -1); pik_append(p, "\"", 1); } w = p->bbox.ne.x - p->bbox.sw.x; h = p->bbox.ne.y - p->bbox.sw.y; p->wSVG = pik_round(p->rScale*w); p->hSVG = pik_round(p->rScale*h); pikScale = pik_value(p,"scale",5,0); if( pikScale>=0.001 && pikScale<=1000.0 && (pikScale<0.99 || pikScale>1.01) ){ p->wSVG = pik_round(p->wSVG*pikScale); p->hSVG = pik_round(p->hSVG*pikScale); pik_append_num(p, " width=\"", p->wSVG); pik_append_num(p, "\" height=\"", p->hSVG); pik_append(p, "\"", 1); } pik_append_dis(p, " viewBox=\"0 0 ",w,""); pik_append_dis(p, " ",h,"\">\n"); pik_elist_render(p, pList); pik_append(p,"\n", -1); }else{ p->wSVG = -1; p->hSVG = -1; } pik_elist_free(p, pList); } /* ** An array of this structure defines a list of keywords. */ typedef struct PikWord { char *zWord; /* Text of the keyword */ unsigned char nChar; /* Length of keyword text in bytes */ unsigned char eType; /* Token code */ unsigned char eCode; /* Extra code for the token */ unsigned char eEdge; /* CP_* code for corner/edge keywords */ } PikWord; /* ** Keywords */ static const PikWord pik_keywords[] = { { "above", 5, T_ABOVE, 0, 0 }, { "abs", 3, T_FUNC1, FN_ABS, 0 }, { "aligned", 7, T_ALIGNED, 0, 0 }, { "and", 3, T_AND, 0, 0 }, { "as", 2, T_AS, 0, 0 }, { "assert", 6, T_ASSERT, 0, 0 }, { "at", 2, T_AT, 0, 0 }, { "behind", 6, T_BEHIND, 0, 0 }, { "below", 5, T_BELOW, 0, 0 }, { "between", 7, T_BETWEEN, 0, 0 }, { "big", 3, T_BIG, 0, 0 }, { "bold", 4, T_BOLD, 0, 0 }, { "bot", 3, T_EDGEPT, 0, CP_S }, { "bottom", 6, T_BOTTOM, 0, CP_S }, { "c", 1, T_EDGEPT, 0, CP_C }, { "ccw", 3, T_CCW, 0, 0 }, { "center", 6, T_CENTER, 0, CP_C }, { "chop", 4, T_CHOP, 0, 0 }, { "close", 5, T_CLOSE, 0, 0 }, { "color", 5, T_COLOR, 0, 0 }, { "cos", 3, T_FUNC1, FN_COS, 0 }, { "cw", 2, T_CW, 0, 0 }, { "dashed", 6, T_DASHED, 0, 0 }, { "define", 6, T_DEFINE, 0, 0 }, { "diameter", 8, T_DIAMETER, 0, 0 }, { "dist", 4, T_DIST, 0, 0 }, { "dotted", 6, T_DOTTED, 0, 0 }, { "down", 4, T_DOWN, DIR_DOWN, 0 }, { "e", 1, T_EDGEPT, 0, CP_E }, { "east", 4, T_EDGEPT, 0, CP_E }, { "end", 3, T_END, 0, CP_END }, { "even", 4, T_EVEN, 0, 0 }, { "fill", 4, T_FILL, 0, 0 }, { "first", 5, T_NTH, 0, 0 }, { "fit", 3, T_FIT, 0, 0 }, { "from", 4, T_FROM, 0, 0 }, { "go", 2, T_GO, 0, 0 }, { "heading", 7, T_HEADING, 0, 0 }, { "height", 6, T_HEIGHT, 0, 0 }, { "ht", 2, T_HEIGHT, 0, 0 }, { "in", 2, T_IN, 0, 0 }, { "int", 3, T_FUNC1, FN_INT, 0 }, { "invis", 5, T_INVIS, 0, 0 }, { "invisible", 9, T_INVIS, 0, 0 }, { "italic", 6, T_ITALIC, 0, 0 }, { "last", 4, T_LAST, 0, 0 }, { "left", 4, T_LEFT, DIR_LEFT, CP_W }, { "ljust", 5, T_LJUST, 0, 0 }, { "max", 3, T_FUNC2, FN_MAX, 0 }, { "min", 3, T_FUNC2, FN_MIN, 0 }, { "mono", 4, T_MONO, 0, 0 }, { "monospace", 9, T_MONO, 0, 0 }, { "n", 1, T_EDGEPT, 0, CP_N }, { "ne", 2, T_EDGEPT, 0, CP_NE }, { "north", 5, T_EDGEPT, 0, CP_N }, { "nw", 2, T_EDGEPT, 0, CP_NW }, { "of", 2, T_OF, 0, 0 }, { "previous", 8, T_LAST, 0, 0, }, { "print", 5, T_PRINT, 0, 0 }, { "rad", 3, T_RADIUS, 0, 0 }, { "radius", 6, T_RADIUS, 0, 0 }, { "right", 5, T_RIGHT, DIR_RIGHT, CP_E }, { "rjust", 5, T_RJUST, 0, 0 }, { "s", 1, T_EDGEPT, 0, CP_S }, { "same", 4, T_SAME, 0, 0 }, { "se", 2, T_EDGEPT, 0, CP_SE }, { "sin", 3, T_FUNC1, FN_SIN, 0 }, { "small", 5, T_SMALL, 0, 0 }, { "solid", 5, T_SOLID, 0, 0 }, { "south", 5, T_EDGEPT, 0, CP_S }, { "sqrt", 4, T_FUNC1, FN_SQRT, 0 }, { "start", 5, T_START, 0, CP_START }, { "sw", 2, T_EDGEPT, 0, CP_SW }, { "t", 1, T_TOP, 0, CP_N }, { "the", 3, T_THE, 0, 0 }, { "then", 4, T_THEN, 0, 0 }, { "thick", 5, T_THICK, 0, 0 }, { "thickness", 9, T_THICKNESS, 0, 0 }, { "thin", 4, T_THIN, 0, 0 }, { "this", 4, T_THIS, 0, 0 }, { "to", 2, T_TO, 0, 0 }, { "top", 3, T_TOP, 0, CP_N }, { "until", 5, T_UNTIL, 0, 0 }, { "up", 2, T_UP, DIR_UP, 0 }, { "vertex", 6, T_VERTEX, 0, 0 }, { "w", 1, T_EDGEPT, 0, CP_W }, { "way", 3, T_WAY, 0, 0 }, { "west", 4, T_EDGEPT, 0, CP_W }, { "wid", 3, T_WIDTH, 0, 0 }, { "width", 5, T_WIDTH, 0, 0 }, { "with", 4, T_WITH, 0, 0 }, { "x", 1, T_X, 0, 0 }, { "y", 1, T_Y, 0, 0 }, }; /* ** Search a PikWordlist for the given keyword. Return a pointer to the ** keyword entry found. Or return 0 if not found. */ static const PikWord *pik_find_word( const char *zIn, /* Word to search for */ int n, /* Length of zIn */ const PikWord *aList, /* List to search */ int nList /* Number of entries in aList */ ){ int first = 0; int last = nList-1; while( first<=last ){ int mid = (first + last)/2; int sz = aList[mid].nChar; int c = strncmp(zIn, aList[mid].zWord, szz character. Fill in other fields of the ** pToken object as appropriate. */ static int pik_token_length(PToken *pToken, int bAllowCodeBlock){ const unsigned char *z = (const unsigned char*)pToken->z; int i; unsigned char c, c2; switch( z[0] ){ case '\\': { pToken->eType = T_WHITESPACE; for(i=1; z[i]=='\r' || z[i]==' ' || z[i]=='\t'; i++){} if( z[i]=='\n' ) return i+1; pToken->eType = T_ERROR; return 1; } case ';': case '\n': { pToken->eType = T_EOL; return 1; } case '"': { for(i=1; (c = z[i])!=0; i++){ if( c=='\\' ){ if( z[i+1]==0 ) break; i++; continue; } if( c=='"' ){ pToken->eType = T_STRING; return i+1; } } pToken->eType = T_ERROR; return i; } case ' ': case '\t': case '\f': case '\r': { for(i=1; (c = z[i])==' ' || c=='\t' || c=='\r' || c=='\f'; i++){} pToken->eType = T_WHITESPACE; return i; } case '#': { for(i=1; (c = z[i])!=0 && c!='\n'; i++){} pToken->eType = T_WHITESPACE; /* If the comment is "#breakpoint" then invoke the pik_breakpoint() ** routine. The pik_breakpoint() routie is a no-op that serves as ** a convenient place to set a gdb breakpoint when debugging. */ if( strncmp((const char*)z,"#breakpoint",11)==0 ) pik_breakpoint(z); return i; } case '/': { if( z[1]=='*' ){ for(i=2; z[i]!=0 && (z[i]!='*' || z[i+1]!='/'); i++){} if( z[i]=='*' ){ pToken->eType = T_WHITESPACE; return i+2; }else{ pToken->eType = T_ERROR; return i; } }else if( z[1]=='/' ){ for(i=2; z[i]!=0 && z[i]!='\n'; i++){} pToken->eType = T_WHITESPACE; return i; }else if( z[1]=='=' ){ pToken->eType = T_ASSIGN; pToken->eCode = T_SLASH; return 2; }else{ pToken->eType = T_SLASH; return 1; } } case '+': { if( z[1]=='=' ){ pToken->eType = T_ASSIGN; pToken->eCode = T_PLUS; return 2; } pToken->eType = T_PLUS; return 1; } case '*': { if( z[1]=='=' ){ pToken->eType = T_ASSIGN; pToken->eCode = T_STAR; return 2; } pToken->eType = T_STAR; return 1; } case '%': { pToken->eType = T_PERCENT; return 1; } case '(': { pToken->eType = T_LP; return 1; } case ')': { pToken->eType = T_RP; return 1; } case '[': { pToken->eType = T_LB; return 1; } case ']': { pToken->eType = T_RB; return 1; } case ',': { pToken->eType = T_COMMA; return 1; } case ':': { pToken->eType = T_COLON; return 1; } case '>': { pToken->eType = T_GT; return 1; } case '=': { if( z[1]=='=' ){ pToken->eType = T_EQ; return 2; } pToken->eType = T_ASSIGN; pToken->eCode = T_ASSIGN; return 1; } case '-': { if( z[1]=='>' ){ pToken->eType = T_RARROW; return 2; }else if( z[1]=='=' ){ pToken->eType = T_ASSIGN; pToken->eCode = T_MINUS; return 2; }else{ pToken->eType = T_MINUS; return 1; } } case '<': { if( z[1]=='-' ){ if( z[2]=='>' ){ pToken->eType = T_LRARROW; return 3; }else{ pToken->eType = T_LARROW; return 2; } }else{ pToken->eType = T_LT; return 1; } } case 0xe2: { if( z[1]==0x86 ){ if( z[2]==0x90 ){ pToken->eType = T_LARROW; /* <- */ return 3; } if( z[2]==0x92 ){ pToken->eType = T_RARROW; /* -> */ return 3; } if( z[2]==0x94 ){ pToken->eType = T_LRARROW; /* <-> */ return 3; } } pToken->eType = T_ERROR; return 1; } case '{': { int len, depth; i = 1; if( bAllowCodeBlock ){ depth = 1; while( z[i] && depth>0 ){ PToken x; x.z = (char*)(z+i); len = pik_token_length(&x, 0); if( len==1 ){ if( z[i]=='{' ) depth++; if( z[i]=='}' ) depth--; } i += len; } }else{ depth = 0; } if( depth ){ pToken->eType = T_ERROR; return 1; } pToken->eType = T_CODEBLOCK; return i; } case '&': { static const struct { int nByte; /* Number of bytes in zEntity */ int eCode; /* Corresponding token code */ const char *zEntity; /* Name of the HTML entity */ } aEntity[] = { /* 123456789 1234567 */ { 6, T_RARROW, "→" }, /* Same as -> */ { 12, T_RARROW, "→" }, /* Same as -> */ { 6, T_LARROW, "←" }, /* Same as <- */ { 11, T_LARROW, "←" }, /* Same as <- */ { 16, T_LRARROW, "↔" }, /* Same as <-> */ }; unsigned int i; for(i=0; ieType = aEntity[i].eCode; return aEntity[i].nByte; } } pToken->eType = T_ERROR; return 1; } default: { c = z[0]; if( c=='.' ){ unsigned char c1 = z[1]; if( IsLower(c1) ){ const PikWord *pFound; for(i=2; (c = z[i])>='a' && c<='z'; i++){} pFound = pik_find_word((const char*)z+1, i-1, pik_keywords, count(pik_keywords)); if( pFound && (pFound->eEdge>0 || pFound->eType==T_EDGEPT || pFound->eType==T_START || pFound->eType==T_END ) ){ /* Dot followed by something that is a 2-D place value */ pToken->eType = T_DOT_E; }else if( pFound && (pFound->eType==T_X || pFound->eType==T_Y) ){ /* Dot followed by "x" or "y" */ pToken->eType = T_DOT_XY; }else{ /* Any other "dot" */ pToken->eType = T_DOT_L; } return 1; }else if( IsDigit(c1) ){ i = 0; /* no-op. Fall through to number handling */ }else if( IsUpper(c1) ){ for(i=2; (c = z[i])!=0 && (IsAlnum(c) || c=='_'); i++){} pToken->eType = T_DOT_U; return 1; }else{ pToken->eType = T_ERROR; return 1; } } if( (c>='0' && c<='9') || c=='.' ){ int nDigit; int isInt = 1; if( c!='.' ){ nDigit = 1; for(i=1; (c = z[i])>='0' && c<='9'; i++){ nDigit++; } if( i==1 && (c=='x' || c=='X') ){ for(i=2; (c = z[i])!=0 && IsXDigit(c); i++){} pToken->eType = T_NUMBER; return i; } }else{ isInt = 0; nDigit = 0; i = 0; } if( c=='.' ){ isInt = 0; for(i++; (c = z[i])>='0' && c<='9'; i++){ nDigit++; } } if( nDigit==0 ){ pToken->eType = T_ERROR; return i; } if( c=='e' || c=='E' ){ int iBefore = i; i++; c2 = z[i]; if( c2=='+' || c2=='-' ){ i++; c2 = z[i]; } if( c2<'0' || c>'9' ){ /* This is not an exp */ i = iBefore; }else{ i++; isInt = 0; while( (c = z[i])>='0' && c<='9' ){ i++; } } } c2 = c ? z[i+1] : 0; if( isInt ){ if( (c=='t' && c2=='h') || (c=='r' && c2=='d') || (c=='n' && c2=='d') || (c=='s' && c2=='t') ){ pToken->eType = T_NTH; return i+2; } } if( (c=='i' && c2=='n') || (c=='c' && c2=='m') || (c=='m' && c2=='m') || (c=='p' && c2=='t') || (c=='p' && c2=='x') || (c=='p' && c2=='c') ){ i += 2; } pToken->eType = T_NUMBER; return i; }else if( IsLower(c) ){ const PikWord *pFound; for(i=1; (c = z[i])!=0 && (IsAlnum(c) || c=='_'); i++){} pFound = pik_find_word((const char*)z, i, pik_keywords, count(pik_keywords)); if( pFound ){ pToken->eType = pFound->eType; pToken->eCode = pFound->eCode; pToken->eEdge = pFound->eEdge; return i; } pToken->n = i; if( pik_find_class(pToken)!=0 ){ pToken->eType = T_CLASSNAME; }else{ pToken->eType = T_ID; } return i; }else if( c>='A' && c<='Z' ){ for(i=1; (c = z[i])!=0 && (IsAlnum(c) || c=='_'); i++){} pToken->eType = T_PLACENAME; return i; }else if( c=='$' && z[1]>='1' && z[1]<='9' && !IsDigit(z[2]) ){ pToken->eType = T_PARAMETER; pToken->eCode = z[1] - '1'; return 2; }else if( c=='_' || c=='$' || c=='@' ){ for(i=1; (c = z[i])!=0 && (IsAlnum(c) || c=='_'); i++){} pToken->eType = T_ID; return i; }else{ pToken->eType = T_ERROR; return 1; } } } } /* ** Return a pointer to the next non-whitespace token after pThis. ** This is used to help form error messages. */ static PToken pik_next_semantic_token(PToken *pThis){ PToken x; int sz; int i = pThis->n; memset(&x, 0, sizeof(x)); x.z = pThis->z; while(1){ x.z = pThis->z + i; sz = pik_token_length(&x, 1); if( x.eType!=T_WHITESPACE ){ x.n = sz; return x; } i += sz; } } /* Parser arguments to a macro invocation ** ** (arg1, arg2, ...) ** ** Arguments are comma-separated, except that commas within string ** literals or with (...), {...}, or [...] do not count. The argument ** list begins and ends with parentheses. There can be at most 9 ** arguments. ** ** Return the number of bytes in the argument list. */ static unsigned int pik_parse_macro_args( Pik *p, const char *z, /* Start of the argument list */ int n, /* Available bytes */ PToken *args, /* Fill in with the arguments */ PToken *pOuter /* Arguments of the next outer context, or NULL */ ){ int nArg = 0; int i, j, sz; int iStart; int depth = 0; PToken x; if( z[0]!='(' ) return 0; args[0].z = z+1; iStart = 1; for(i=1; in>0 && IsSpace(t->z[0]) ){ t->n--; t->z++; } while( t->n>0 && IsSpace(t->z[t->n-1]) ){ t->n--; } if( t->n==2 && t->z[0]=='$' && t->z[1]>='1' && t->z[1]<='9' ){ if( pOuter ) *t = pOuter[t->z[1]-'1']; else t->n = 0; } } return i+1; } x.z = z; x.n = 1; pik_error(p, &x, "unterminated macro argument list"); return 0; } /* ** Split up the content of a PToken into multiple tokens and ** send each to the parser. */ void pik_tokenize(Pik *p, PToken *pIn, yyParser *pParser, PToken *aParam){ unsigned int i; int sz = 0; PToken token; PMacro *pMac; for(i=0; in && pIn->z[i] && p->nErr==0; i+=sz){ token.eCode = 0; token.eEdge = 0; token.z = pIn->z + i; sz = pik_token_length(&token, 1); if( token.eType==T_WHITESPACE ){ /* no-op */ }else if( sz>50000 ){ token.n = 1; pik_error(p, &token, "token is too long - max length 50000 bytes"); break; }else if( token.eType==T_ERROR ){ token.n = (unsigned short)(sz & 0xffff); pik_error(p, &token, "unrecognized token"); break; }else if( sz+i>pIn->n ){ token.n = pIn->n - i; pik_error(p, &token, "syntax error"); break; }else if( token.eType==T_PARAMETER ){ /* Substitute a parameter into the input stream */ if( aParam==0 || aParam[token.eCode].n==0 ){ continue; } token.n = (unsigned short)(sz & 0xffff); if( p->nCtx>=count(p->aCtx) ){ pik_error(p, &token, "macros nested too deep"); }else{ p->aCtx[p->nCtx++] = token; pik_tokenize(p, &aParam[token.eCode], pParser, 0); p->nCtx--; } }else if( token.eType==T_ID && (token.n = (unsigned short)(sz & 0xffff), (pMac = pik_find_macro(p,&token))!=0) ){ PToken args[9]; unsigned int j = i+sz; if( pMac->inUse ){ pik_error(p, &pMac->macroName, "recursive macro definition"); break; } token.n = (short int)(sz & 0xffff); if( p->nCtx>=count(p->aCtx) ){ pik_error(p, &token, "macros nested too deep"); break; } pMac->inUse = 1; memset(args, 0, sizeof(args)); p->aCtx[p->nCtx++] = token; sz += pik_parse_macro_args(p, pIn->z+j, pIn->n-j, args, aParam); pik_tokenize(p, &pMac->macroBody, pParser, args); p->nCtx--; pMac->inUse = 0; }else{ #if 0 printf("******** Token %s (%d): \"%.*s\" **************\n", yyTokenName[token.eType], token.eType, (int)(IsSpace(token.z[0]) ? 0 : sz), token.z); #endif token.n = (unsigned short)(sz & 0xffff); if( p->nToken++ > PIKCHR_TOKEN_LIMIT ){ pik_error(p, &token, "script is too complex"); break; } pik_parser(pParser, token.eType, token); } } } /* ** Parse the PIKCHR script contained in zText[]. Return a rendering. Or ** if an error is encountered, return the error text. The error message ** is HTML formatted. So regardless of what happens, the return text ** is safe to be insertd into an HTML output stream. ** ** If pnWidth and pnHeight are not NULL, then this routine writes the ** width and height of the object into the integers that they ** point to. A value of -1 is written if an error is seen. ** ** If zClass is not NULL, then it is a class name to be included in ** the markup. ** ** The returned string is contained in memory obtained from malloc() ** and should be released by the caller. */ char *pikchr( const char *zText, /* Input PIKCHR source text. zero-terminated */ const char *zClass, /* Add class="%s" to markup */ unsigned int mFlags, /* Flags used to influence rendering behavior */ int *pnWidth, /* Write width of here, if not NULL */ int *pnHeight /* Write height here, if not NULL */ ){ Pik s; yyParser sParse; memset(&s, 0, sizeof(s)); s.sIn.z = zText; s.sIn.n = (unsigned int)strlen(zText); s.eDir = DIR_RIGHT; s.zClass = zClass; s.mFlags = mFlags; pik_parserInit(&sParse, &s); #if 0 pik_parserTrace(stdout, "parser: "); #endif pik_tokenize(&s, &s.sIn, &sParse, 0); if( s.nErr==0 ){ PToken token; memset(&token,0,sizeof(token)); token.z = zText + (s.sIn.n>0 ? s.sIn.n-1 : 0); token.n = 1; pik_parser(&sParse, 0, token); } pik_parserFinalize(&sParse); if( s.zOut==0 && s.nErr==0 ){ pik_append(&s, "\n", -1); } while( s.pVar ){ PVar *pNext = s.pVar->pNext; free(s.pVar); s.pVar = pNext; } while( s.pMacros ){ PMacro *pNext = s.pMacros->pNext; free(s.pMacros); s.pMacros = pNext; } if( pnWidth ) *pnWidth = s.nErr ? -1 : s.wSVG; if( pnHeight ) *pnHeight = s.nErr ? -1 : s.hSVG; if( s.zOut ){ s.zOut[s.nOut] = 0; s.zOut = realloc(s.zOut, s.nOut+1); } return s.zOut; } #if defined(PIKCHR_FUZZ) #include int LLVMFuzzerTestOneInput(const uint8_t *aData, size_t nByte){ int w,h; char *zIn, *zOut; unsigned int mFlags = nByte & 3; zIn = malloc( nByte + 1 ); if( zIn==0 ) return 0; memcpy(zIn, aData, nByte); zIn[nByte] = 0; zOut = pikchr(zIn, "pikchr", mFlags, &w, &h); free(zIn); free(zOut); return 0; } #endif /* PIKCHR_FUZZ */ #if defined(PIKCHR_SHELL) /* Print a usage comment for the shell and exit. */ static void usage(const char *argv0){ fprintf(stderr, "usage: %s [OPTIONS] FILE ...\n", argv0); fprintf(stderr, "Convert Pikchr input files into SVG. Filename \"-\" means stdin.\n" "All output goes to stdout.\n" "Options:\n" " --dark-mode Generate \"dark mode\" output\n" " --dont-stop Process all files even if earlier files have errors\n" " --svg-only Emit raw SVG without the HTML wrapper\n" ); exit(1); } /* Send text to standard output, but escape HTML markup */ static void print_escape_html(const char *z){ int j; char c; while( z[0]!=0 ){ for(j=0; (c = z[j])!=0 && c!='<' && c!='>' && c!='&'; j++){} if( j ) printf("%.*s", j, z); z += j+1; j = -1; if( c=='<' ){ printf("<"); }else if( c=='>' ){ printf(">"); }else if( c=='&' ){ printf("&"); }else if( c==0 ){ break; } } } /* Read the content of file zFilename into memory obtained from malloc() ** Return the memory. If something goes wrong (ex: the file does not exist ** or cannot be opened) put an error message on stderr and return NULL. ** ** If the filename is "-" read stdin. */ static char *readFile(const char *zFilename){ FILE *in; size_t n; size_t nUsed = 0; size_t nAlloc = 0; char *z = 0, *zNew = 0; in = strcmp(zFilename,"-")==0 ? stdin : fopen(zFilename, "rb"); if( in==0 ){ fprintf(stderr, "cannot open \"%s\" for reading\n", zFilename); return 0; } while(1){ if( nUsed+2>=nAlloc ){ nAlloc = nAlloc*2 + 4000; zNew = realloc(z, nAlloc); } if( zNew==0 ){ free(z); fprintf(stderr, "out of memory trying to allocate %lld bytes\n", (long long int)nAlloc); exit(1); } z = zNew; n = fread(z+nUsed, 1, nAlloc-nUsed-1, in); if( n<=0 ){ break; } nUsed += n; } if( in!=stdin ) fclose(in); z[nUsed] = 0; return z; } /* Testing interface ** ** Generate HTML on standard output that displays both the original ** input text and the rendered SVG for all files named on the command ** line. */ int main(int argc, char **argv){ int i; int bSvgOnly = 0; /* Output SVG only. No HTML wrapper */ int bDontStop = 0; /* Continue in spite of errors */ int exitCode = 0; /* What to return */ int mFlags = 0; /* mFlags argument to pikchr() */ const char *zStyle = ""; /* Extra styling */ const char *zHtmlHdr = "\n" "\n" "\nPIKCHR Test\n" "\n" "\n" "\n" "\n" "\n" ; if( argc<2 ) usage(argv[0]); for(i=1; iFile %s

\n", argv[i]); if( w<0 ){ printf("

ERROR

\n%s\n", zOut); }else{ printf("
\n",i,i); printf("
\n", w,zStyle); printf("%s
\n", zOut); printf("\n
\n"); } } free(zOut); free(zIn); } if( !bSvgOnly ){ printf("\n"); } return exitCode ? EXIT_FAILURE : EXIT_SUCCESS; } #endif /* PIKCHR_SHELL */ #ifdef PIKCHR_TCL #include /* ** An interface to TCL ** ** TCL command: pikchr $INPUTTEXT ** ** Returns a list of 3 elements which are the output text, the width, and ** the height. ** ** Register the "pikchr" command by invoking Pikchr_Init(Tcl_Interp*). Or ** compile this source file as a shared library and load it using the ** "load" command of Tcl. ** ** Compile this source-code file into a shared library using a command ** similar to this: ** ** gcc -c pikchr.so -DPIKCHR_TCL -shared pikchr.c */ static int pik_tcl_command( ClientData clientData, /* Not Used */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ Tcl_Obj *CONST objv[] /* Command arguments */ ){ int w, h; /* Width and height of the pikchr */ const char *zIn; /* Source text input */ char *zOut; /* SVG output text */ Tcl_Obj *pRes; /* The result TCL object */ (void)clientData; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "PIKCHR_SOURCE_TEXT"); return TCL_ERROR; } zIn = Tcl_GetString(objv[1]); w = h = 0; zOut = pikchr(zIn, "pikchr", 0, &w, &h); if( zOut==0 ){ return TCL_ERROR; /* Out of memory */ } pRes = Tcl_NewObj(); Tcl_ListObjAppendElement(0, pRes, Tcl_NewStringObj(zOut, -1)); free(zOut); Tcl_ListObjAppendElement(0, pRes, Tcl_NewIntObj(w)); Tcl_ListObjAppendElement(0, pRes, Tcl_NewIntObj(h)); Tcl_SetObjResult(interp, pRes); return TCL_OK; } #ifndef PACKAGE_NAME # define PACKAGE_NAME "pikchr" #endif #ifndef PACKAGE_VERSION # define PACKAGE_VERSION "1.0" #endif /* Invoke this routine to register the "pikchr" command with the interpreter ** given in the argument */ int Pikchr_Init(Tcl_Interp *interp){ Tcl_CreateObjCommand(interp, "pikchr", pik_tcl_command, 0, 0); Tcl_PkgProvide(interp, PACKAGE_NAME, PACKAGE_VERSION); return TCL_OK; } #endif /* PIKCHR_TCL */ #line 8284 "pikchr.c"