├── .gitignore ├── Doxyfile ├── LICENSE ├── README.md ├── Syntax ├── BBEdit │ └── Firth.plist └── Emacs │ └── firth-mode.el ├── examples ├── iniloader.firth └── test.ini ├── firth.lua ├── firth ├── compiler+prims.lua ├── compiler.lua ├── core.firth ├── stack.lua ├── stringio.lua └── table.save.lua ├── lua2dox ├── notes.txt └── test ├── test-stack.lua ├── test-stringio.lua └── test.lua /.gitignore: -------------------------------------------------------------------------------- 1 | doc/* 2 | .DS_Store -------------------------------------------------------------------------------- /Doxyfile: -------------------------------------------------------------------------------- 1 | # Doxyfile 1.6.1 2 | 3 | # This file describes the settings to be used by the documentation system 4 | # doxygen (www.doxygen.org) for a project 5 | # 6 | # All text after a hash (#) is considered a comment and will be ignored 7 | # The format is: 8 | # TAG = value [value, ...] 9 | # For lists items can also be appended using: 10 | # TAG += value [value, ...] 11 | # Values that contain spaces should be placed between quotes (" ") 12 | 13 | #--------------------------------------------------------------------------- 14 | # Project related configuration options 15 | #--------------------------------------------------------------------------- 16 | 17 | # This tag specifies the encoding used for all characters in the config file 18 | # that follow. The default is UTF-8 which is also the encoding used for all 19 | # text before the first occurrence of this tag. Doxygen uses libiconv (or the 20 | # iconv built into libc) for the transcoding. See 21 | # http://www.gnu.org/software/libiconv for the list of possible encodings. 22 | 23 | DOXYFILE_ENCODING = UTF-8 24 | 25 | # The PROJECT_NAME tag is a single word (or a sequence of words surrounded 26 | # by quotes) that should identify the project. 27 | 28 | PROJECT_NAME = Firth 29 | 30 | # The PROJECT_NUMBER tag can be used to enter a project or revision number. 31 | # This could be handy for archiving the generated documentation or 32 | # if some version control system is used. 33 | 34 | PROJECT_NUMBER = 35 | 36 | # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 37 | # base path where the generated documentation will be put. 38 | # If a relative path is entered, it will be relative to the location 39 | # where doxygen was started. If left blank the current directory will be used. 40 | 41 | OUTPUT_DIRECTORY = doc 42 | 43 | # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 44 | # 4096 sub-directories (in 2 levels) under the output directory of each output 45 | # format and will distribute the generated files over these directories. 46 | # Enabling this option can be useful when feeding doxygen a huge amount of 47 | # source files, where putting all generated files in the same directory would 48 | # otherwise cause performance problems for the file system. 49 | 50 | CREATE_SUBDIRS = NO 51 | 52 | # The OUTPUT_LANGUAGE tag is used to specify the language in which all 53 | # documentation generated by doxygen is written. Doxygen will use this 54 | # information to generate all constant output in the proper language. 55 | # The default language is English, other supported languages are: 56 | # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, 57 | # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, 58 | # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English 59 | # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, 60 | # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, 61 | # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. 62 | 63 | OUTPUT_LANGUAGE = English 64 | 65 | # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 66 | # include brief member descriptions after the members that are listed in 67 | # the file and class documentation (similar to JavaDoc). 68 | # Set to NO to disable this. 69 | 70 | BRIEF_MEMBER_DESC = YES 71 | 72 | # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 73 | # the brief description of a member or function before the detailed description. 74 | # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 75 | # brief descriptions will be completely suppressed. 76 | 77 | REPEAT_BRIEF = YES 78 | 79 | # This tag implements a quasi-intelligent brief description abbreviator 80 | # that is used to form the text in various listings. Each string 81 | # in this list, if found as the leading text of the brief description, will be 82 | # stripped from the text and the result after processing the whole list, is 83 | # used as the annotated text. Otherwise, the brief description is used as-is. 84 | # If left blank, the following values are used ("$name" is automatically 85 | # replaced with the name of the entity): "The $name class" "The $name widget" 86 | # "The $name file" "is" "provides" "specifies" "contains" 87 | # "represents" "a" "an" "the" 88 | 89 | ABBREVIATE_BRIEF = 90 | 91 | # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 92 | # Doxygen will generate a detailed section even if there is only a brief 93 | # description. 94 | 95 | ALWAYS_DETAILED_SEC = NO 96 | 97 | # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all 98 | # inherited members of a class in the documentation of that class as if those 99 | # members were ordinary class members. Constructors, destructors and assignment 100 | # operators of the base classes will not be shown. 101 | 102 | INLINE_INHERITED_MEMB = NO 103 | 104 | # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 105 | # path before files name in the file list and in the header files. If set 106 | # to NO the shortest path that makes the file name unique will be used. 107 | 108 | FULL_PATH_NAMES = YES 109 | 110 | # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 111 | # can be used to strip a user-defined part of the path. Stripping is 112 | # only done if one of the specified strings matches the left-hand part of 113 | # the path. The tag can be used to show relative paths in the file list. 114 | # If left blank the directory from which doxygen is run is used as the 115 | # path to strip. 116 | 117 | STRIP_FROM_PATH = 118 | 119 | # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of 120 | # the path mentioned in the documentation of a class, which tells 121 | # the reader which header file to include in order to use a class. 122 | # If left blank only the name of the header file containing the class 123 | # definition is used. Otherwise one should specify the include paths that 124 | # are normally passed to the compiler using the -I flag. 125 | 126 | STRIP_FROM_INC_PATH = 127 | 128 | # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 129 | # (but less readable) file names. This can be useful is your file systems 130 | # doesn't support long names like on DOS, Mac, or CD-ROM. 131 | 132 | SHORT_NAMES = NO 133 | 134 | # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 135 | # will interpret the first line (until the first dot) of a JavaDoc-style 136 | # comment as the brief description. If set to NO, the JavaDoc 137 | # comments will behave just like regular Qt-style comments 138 | # (thus requiring an explicit @brief command for a brief description.) 139 | 140 | JAVADOC_AUTOBRIEF = YES 141 | 142 | # If the QT_AUTOBRIEF tag is set to YES then Doxygen will 143 | # interpret the first line (until the first dot) of a Qt-style 144 | # comment as the brief description. If set to NO, the comments 145 | # will behave just like regular Qt-style comments (thus requiring 146 | # an explicit \brief command for a brief description.) 147 | 148 | QT_AUTOBRIEF = NO 149 | 150 | # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 151 | # treat a multi-line C++ special comment block (i.e. a block of //! or /// 152 | # comments) as a brief description. This used to be the default behaviour. 153 | # The new default is to treat a multi-line C++ comment block as a detailed 154 | # description. Set this tag to YES if you prefer the old behaviour instead. 155 | 156 | MULTILINE_CPP_IS_BRIEF = NO 157 | 158 | # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 159 | # member inherits the documentation from any documented member that it 160 | # re-implements. 161 | 162 | INHERIT_DOCS = YES 163 | 164 | # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce 165 | # a new page for each member. If set to NO, the documentation of a member will 166 | # be part of the file/class/namespace that contains it. 167 | 168 | SEPARATE_MEMBER_PAGES = NO 169 | 170 | # The TAB_SIZE tag can be used to set the number of spaces in a tab. 171 | # Doxygen uses this value to replace tabs by spaces in code fragments. 172 | 173 | TAB_SIZE = 8 174 | 175 | # This tag can be used to specify a number of aliases that acts 176 | # as commands in the documentation. An alias has the form "name=value". 177 | # For example adding "sideeffect=\par Side Effects:\n" will allow you to 178 | # put the command \sideeffect (or @sideeffect) in the documentation, which 179 | # will result in a user-defined paragraph with heading "Side Effects:". 180 | # You can put \n's in the value part of an alias to insert newlines. 181 | 182 | ALIASES = 183 | 184 | # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C 185 | # sources only. Doxygen will then generate output that is more tailored for C. 186 | # For instance, some of the names that are used will be different. The list 187 | # of all members will be omitted, etc. 188 | 189 | OPTIMIZE_OUTPUT_FOR_C = NO 190 | 191 | # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java 192 | # sources only. Doxygen will then generate output that is more tailored for 193 | # Java. For instance, namespaces will be presented as packages, qualified 194 | # scopes will look different, etc. 195 | 196 | OPTIMIZE_OUTPUT_JAVA = NO 197 | 198 | # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran 199 | # sources only. Doxygen will then generate output that is more tailored for 200 | # Fortran. 201 | 202 | OPTIMIZE_FOR_FORTRAN = NO 203 | 204 | # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL 205 | # sources. Doxygen will then generate output that is tailored for 206 | # VHDL. 207 | 208 | OPTIMIZE_OUTPUT_VHDL = NO 209 | 210 | # Doxygen selects the parser to use depending on the extension of the files it parses. 211 | # With this tag you can assign which parser to use for a given extension. 212 | # Doxygen has a built-in mapping, but you can override or extend it using this tag. 213 | # The format is ext=language, where ext is a file extension, and language is one of 214 | # the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, 215 | # Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat 216 | # .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), 217 | # use: inc=Fortran f=C. Note that for custom extensions you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. 218 | 219 | EXTENSION_MAPPING = 220 | 221 | # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want 222 | # to include (a tag file for) the STL sources as input, then you should 223 | # set this tag to YES in order to let doxygen match functions declarations and 224 | # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. 225 | # func(std::string) {}). This also make the inheritance and collaboration 226 | # diagrams that involve STL classes more complete and accurate. 227 | 228 | BUILTIN_STL_SUPPORT = NO 229 | 230 | # If you use Microsoft's C++/CLI language, you should set this option to YES to 231 | # enable parsing support. 232 | 233 | CPP_CLI_SUPPORT = NO 234 | 235 | # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. 236 | # Doxygen will parse them like normal C++ but will assume all classes use public 237 | # instead of private inheritance when no explicit protection keyword is present. 238 | 239 | SIP_SUPPORT = NO 240 | 241 | # For Microsoft's IDL there are propget and propput attributes to indicate getter 242 | # and setter methods for a property. Setting this option to YES (the default) 243 | # will make doxygen to replace the get and set methods by a property in the 244 | # documentation. This will only work if the methods are indeed getting or 245 | # setting a simple type. If this is not the case, or you want to show the 246 | # methods anyway, you should set this option to NO. 247 | 248 | IDL_PROPERTY_SUPPORT = YES 249 | 250 | # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 251 | # tag is set to YES, then doxygen will reuse the documentation of the first 252 | # member in the group (if any) for the other members of the group. By default 253 | # all members of a group must be documented explicitly. 254 | 255 | DISTRIBUTE_GROUP_DOC = NO 256 | 257 | # Set the SUBGROUPING tag to YES (the default) to allow class member groups of 258 | # the same type (for instance a group of public functions) to be put as a 259 | # subgroup of that type (e.g. under the Public Functions section). Set it to 260 | # NO to prevent subgrouping. Alternatively, this can be done per class using 261 | # the \nosubgrouping command. 262 | 263 | SUBGROUPING = YES 264 | 265 | # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum 266 | # is documented as struct, union, or enum with the name of the typedef. So 267 | # typedef struct TypeS {} TypeT, will appear in the documentation as a struct 268 | # with name TypeT. When disabled the typedef will appear as a member of a file, 269 | # namespace, or class. And the struct will be named TypeS. This can typically 270 | # be useful for C code in case the coding convention dictates that all compound 271 | # types are typedef'ed and only the typedef is referenced, never the tag name. 272 | 273 | TYPEDEF_HIDES_STRUCT = NO 274 | 275 | # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to 276 | # determine which symbols to keep in memory and which to flush to disk. 277 | # When the cache is full, less often used symbols will be written to disk. 278 | # For small to medium size projects (<1000 input files) the default value is 279 | # probably good enough. For larger projects a too small cache size can cause 280 | # doxygen to be busy swapping symbols to and from disk most of the time 281 | # causing a significant performance penality. 282 | # If the system has enough physical memory increasing the cache will improve the 283 | # performance by keeping more symbols in memory. Note that the value works on 284 | # a logarithmic scale so increasing the size by one will rougly double the 285 | # memory usage. The cache size is given by this formula: 286 | # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, 287 | # corresponding to a cache size of 2^16 = 65536 symbols 288 | 289 | SYMBOL_CACHE_SIZE = 0 290 | 291 | #--------------------------------------------------------------------------- 292 | # Build related configuration options 293 | #--------------------------------------------------------------------------- 294 | 295 | # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 296 | # documentation are documented, even if no documentation was available. 297 | # Private class members and static file members will be hidden unless 298 | # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES 299 | 300 | EXTRACT_ALL = NO 301 | 302 | # If the EXTRACT_PRIVATE tag is set to YES all private members of a class 303 | # will be included in the documentation. 304 | 305 | EXTRACT_PRIVATE = NO 306 | 307 | # If the EXTRACT_STATIC tag is set to YES all static members of a file 308 | # will be included in the documentation. 309 | 310 | EXTRACT_STATIC = NO 311 | 312 | # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 313 | # defined locally in source files will be included in the documentation. 314 | # If set to NO only classes defined in header files are included. 315 | 316 | EXTRACT_LOCAL_CLASSES = YES 317 | 318 | # This flag is only useful for Objective-C code. When set to YES local 319 | # methods, which are defined in the implementation section but not in 320 | # the interface are included in the documentation. 321 | # If set to NO (the default) only methods in the interface are included. 322 | 323 | EXTRACT_LOCAL_METHODS = NO 324 | 325 | # If this flag is set to YES, the members of anonymous namespaces will be 326 | # extracted and appear in the documentation as a namespace called 327 | # 'anonymous_namespace{file}', where file will be replaced with the base 328 | # name of the file that contains the anonymous namespace. By default 329 | # anonymous namespace are hidden. 330 | 331 | EXTRACT_ANON_NSPACES = NO 332 | 333 | # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 334 | # undocumented members of documented classes, files or namespaces. 335 | # If set to NO (the default) these members will be included in the 336 | # various overviews, but no documentation section is generated. 337 | # This option has no effect if EXTRACT_ALL is enabled. 338 | 339 | HIDE_UNDOC_MEMBERS = NO 340 | 341 | # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 342 | # undocumented classes that are normally visible in the class hierarchy. 343 | # If set to NO (the default) these classes will be included in the various 344 | # overviews. This option has no effect if EXTRACT_ALL is enabled. 345 | 346 | HIDE_UNDOC_CLASSES = NO 347 | 348 | # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all 349 | # friend (class|struct|union) declarations. 350 | # If set to NO (the default) these declarations will be included in the 351 | # documentation. 352 | 353 | HIDE_FRIEND_COMPOUNDS = NO 354 | 355 | # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any 356 | # documentation blocks found inside the body of a function. 357 | # If set to NO (the default) these blocks will be appended to the 358 | # function's detailed documentation block. 359 | 360 | HIDE_IN_BODY_DOCS = NO 361 | 362 | # The INTERNAL_DOCS tag determines if documentation 363 | # that is typed after a \internal command is included. If the tag is set 364 | # to NO (the default) then the documentation will be excluded. 365 | # Set it to YES to include the internal documentation. 366 | 367 | INTERNAL_DOCS = NO 368 | 369 | # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 370 | # file names in lower-case letters. If set to YES upper-case letters are also 371 | # allowed. This is useful if you have classes or files whose names only differ 372 | # in case and if your file system supports case sensitive file names. Windows 373 | # and Mac users are advised to set this option to NO. 374 | 375 | CASE_SENSE_NAMES = NO 376 | 377 | # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 378 | # will show members with their full class and namespace scopes in the 379 | # documentation. If set to YES the scope will be hidden. 380 | 381 | HIDE_SCOPE_NAMES = NO 382 | 383 | # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 384 | # will put a list of the files that are included by a file in the documentation 385 | # of that file. 386 | 387 | SHOW_INCLUDE_FILES = YES 388 | 389 | # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 390 | # is inserted in the documentation for inline members. 391 | 392 | INLINE_INFO = YES 393 | 394 | # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 395 | # will sort the (detailed) documentation of file and class members 396 | # alphabetically by member name. If set to NO the members will appear in 397 | # declaration order. 398 | 399 | SORT_MEMBER_DOCS = YES 400 | 401 | # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the 402 | # brief documentation of file, namespace and class members alphabetically 403 | # by member name. If set to NO (the default) the members will appear in 404 | # declaration order. 405 | 406 | SORT_BRIEF_DOCS = NO 407 | 408 | # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the (brief and detailed) documentation of class members so that constructors and destructors are listed first. If set to NO (the default) the constructors will appear in the respective orders defined by SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. 409 | 410 | SORT_MEMBERS_CTORS_1ST = NO 411 | 412 | # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the 413 | # hierarchy of group names into alphabetical order. If set to NO (the default) 414 | # the group names will appear in their defined order. 415 | 416 | SORT_GROUP_NAMES = NO 417 | 418 | # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be 419 | # sorted by fully-qualified names, including namespaces. If set to 420 | # NO (the default), the class list will be sorted only by class name, 421 | # not including the namespace part. 422 | # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. 423 | # Note: This option applies only to the class list, not to the 424 | # alphabetical list. 425 | 426 | SORT_BY_SCOPE_NAME = NO 427 | 428 | # The GENERATE_TODOLIST tag can be used to enable (YES) or 429 | # disable (NO) the todo list. This list is created by putting \todo 430 | # commands in the documentation. 431 | 432 | GENERATE_TODOLIST = YES 433 | 434 | # The GENERATE_TESTLIST tag can be used to enable (YES) or 435 | # disable (NO) the test list. This list is created by putting \test 436 | # commands in the documentation. 437 | 438 | GENERATE_TESTLIST = YES 439 | 440 | # The GENERATE_BUGLIST tag can be used to enable (YES) or 441 | # disable (NO) the bug list. This list is created by putting \bug 442 | # commands in the documentation. 443 | 444 | GENERATE_BUGLIST = YES 445 | 446 | # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 447 | # disable (NO) the deprecated list. This list is created by putting 448 | # \deprecated commands in the documentation. 449 | 450 | GENERATE_DEPRECATEDLIST= YES 451 | 452 | # The ENABLED_SECTIONS tag can be used to enable conditional 453 | # documentation sections, marked by \if sectionname ... \endif. 454 | 455 | ENABLED_SECTIONS = 456 | 457 | # The MAX_INITIALIZER_LINES tag determines the maximum number of lines 458 | # the initial value of a variable or define consists of for it to appear in 459 | # the documentation. If the initializer consists of more lines than specified 460 | # here it will be hidden. Use a value of 0 to hide initializers completely. 461 | # The appearance of the initializer of individual variables and defines in the 462 | # documentation can be controlled using \showinitializer or \hideinitializer 463 | # command in the documentation regardless of this setting. 464 | 465 | MAX_INITIALIZER_LINES = 30 466 | 467 | # Set the SHOW_USED_FILES tag to NO to disable the list of files generated 468 | # at the bottom of the documentation of classes and structs. If set to YES the 469 | # list will mention the files that were used to generate the documentation. 470 | 471 | SHOW_USED_FILES = YES 472 | 473 | # If the sources in your project are distributed over multiple directories 474 | # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy 475 | # in the documentation. The default is NO. 476 | 477 | SHOW_DIRECTORIES = NO 478 | 479 | # Set the SHOW_FILES tag to NO to disable the generation of the Files page. 480 | # This will remove the Files entry from the Quick Index and from the 481 | # Folder Tree View (if specified). The default is YES. 482 | 483 | SHOW_FILES = YES 484 | 485 | # Set the SHOW_NAMESPACES tag to NO to disable the generation of the 486 | # Namespaces page. 487 | # This will remove the Namespaces entry from the Quick Index 488 | # and from the Folder Tree View (if specified). The default is YES. 489 | 490 | SHOW_NAMESPACES = YES 491 | 492 | # The FILE_VERSION_FILTER tag can be used to specify a program or script that 493 | # doxygen should invoke to get the current version for each file (typically from 494 | # the version control system). Doxygen will invoke the program by executing (via 495 | # popen()) the command , where is the value of 496 | # the FILE_VERSION_FILTER tag, and is the name of an input file 497 | # provided by doxygen. Whatever the program writes to standard output 498 | # is used as the file version. See the manual for examples. 499 | 500 | FILE_VERSION_FILTER = 501 | 502 | # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by 503 | # doxygen. The layout file controls the global structure of the generated output files 504 | # in an output format independent way. The create the layout file that represents 505 | # doxygen's defaults, run doxygen with the -l option. You can optionally specify a 506 | # file name after the option, if omitted DoxygenLayout.xml will be used as the name 507 | # of the layout file. 508 | 509 | LAYOUT_FILE = 510 | 511 | #--------------------------------------------------------------------------- 512 | # configuration options related to warning and progress messages 513 | #--------------------------------------------------------------------------- 514 | 515 | # The QUIET tag can be used to turn on/off the messages that are generated 516 | # by doxygen. Possible values are YES and NO. If left blank NO is used. 517 | 518 | QUIET = NO 519 | 520 | # The WARNINGS tag can be used to turn on/off the warning messages that are 521 | # generated by doxygen. Possible values are YES and NO. If left blank 522 | # NO is used. 523 | 524 | WARNINGS = YES 525 | 526 | # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 527 | # for undocumented members. If EXTRACT_ALL is set to YES then this flag will 528 | # automatically be disabled. 529 | 530 | WARN_IF_UNDOCUMENTED = YES 531 | 532 | # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for 533 | # potential errors in the documentation, such as not documenting some 534 | # parameters in a documented function, or documenting parameters that 535 | # don't exist or using markup commands wrongly. 536 | 537 | WARN_IF_DOC_ERROR = YES 538 | 539 | # This WARN_NO_PARAMDOC option can be abled to get warnings for 540 | # functions that are documented, but have no documentation for their parameters 541 | # or return value. If set to NO (the default) doxygen will only warn about 542 | # wrong or incomplete parameter documentation, but not about the absence of 543 | # documentation. 544 | 545 | WARN_NO_PARAMDOC = NO 546 | 547 | # The WARN_FORMAT tag determines the format of the warning messages that 548 | # doxygen can produce. The string should contain the $file, $line, and $text 549 | # tags, which will be replaced by the file and line number from which the 550 | # warning originated and the warning text. Optionally the format may contain 551 | # $version, which will be replaced by the version of the file (if it could 552 | # be obtained via FILE_VERSION_FILTER) 553 | 554 | WARN_FORMAT = "$file:$line: $text" 555 | 556 | # The WARN_LOGFILE tag can be used to specify a file to which warning 557 | # and error messages should be written. If left blank the output is written 558 | # to stderr. 559 | 560 | WARN_LOGFILE = 561 | 562 | #--------------------------------------------------------------------------- 563 | # configuration options related to the input files 564 | #--------------------------------------------------------------------------- 565 | 566 | # The INPUT tag can be used to specify the files and/or directories that contain 567 | # documented source files. You may enter file names like "myfile.cpp" or 568 | # directories like "/usr/src/myproject". Separate the files or directories 569 | # with spaces. 570 | 571 | INPUT = 572 | 573 | # This tag can be used to specify the character encoding of the source files 574 | # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is 575 | # also the default input encoding. Doxygen uses libiconv (or the iconv built 576 | # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for 577 | # the list of possible encodings. 578 | 579 | INPUT_ENCODING = UTF-8 580 | 581 | # If the value of the INPUT tag contains directories, you can use the 582 | # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 583 | # and *.h) to filter out the source-files in the directories. If left 584 | # blank the following patterns are tested: 585 | # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx 586 | # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 587 | 588 | FILE_PATTERNS = *.lua 589 | 590 | # The RECURSIVE tag can be used to turn specify whether or not subdirectories 591 | # should be searched for input files as well. Possible values are YES and NO. 592 | # If left blank NO is used. 593 | 594 | RECURSIVE = YES 595 | 596 | # The EXCLUDE tag can be used to specify files and/or directories that should 597 | # excluded from the INPUT source files. This way you can easily exclude a 598 | # subdirectory from a directory tree whose root is specified with the INPUT tag. 599 | 600 | EXCLUDE = 601 | 602 | # The EXCLUDE_SYMLINKS tag can be used select whether or not files or 603 | # directories that are symbolic links (a Unix filesystem feature) are excluded 604 | # from the input. 605 | 606 | EXCLUDE_SYMLINKS = NO 607 | 608 | # If the value of the INPUT tag contains directories, you can use the 609 | # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 610 | # certain files from those directories. Note that the wildcards are matched 611 | # against the file with absolute path, so to exclude all test directories 612 | # for example use the pattern */test/* 613 | 614 | EXCLUDE_PATTERNS = 615 | 616 | # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names 617 | # (namespaces, classes, functions, etc.) that should be excluded from the 618 | # output. The symbol name can be a fully qualified name, a word, or if the 619 | # wildcard * is used, a substring. Examples: ANamespace, AClass, 620 | # AClass::ANamespace, ANamespace::*Test 621 | 622 | EXCLUDE_SYMBOLS = 623 | 624 | # The EXAMPLE_PATH tag can be used to specify one or more files or 625 | # directories that contain example code fragments that are included (see 626 | # the \include command). 627 | 628 | EXAMPLE_PATH = 629 | 630 | # If the value of the EXAMPLE_PATH tag contains directories, you can use the 631 | # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 632 | # and *.h) to filter out the source-files in the directories. If left 633 | # blank all files are included. 634 | 635 | EXAMPLE_PATTERNS = 636 | 637 | # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 638 | # searched for input files to be used with the \include or \dontinclude 639 | # commands irrespective of the value of the RECURSIVE tag. 640 | # Possible values are YES and NO. If left blank NO is used. 641 | 642 | EXAMPLE_RECURSIVE = NO 643 | 644 | # The IMAGE_PATH tag can be used to specify one or more files or 645 | # directories that contain image that are included in the documentation (see 646 | # the \image command). 647 | 648 | IMAGE_PATH = 649 | 650 | # The INPUT_FILTER tag can be used to specify a program that doxygen should 651 | # invoke to filter for each input file. Doxygen will invoke the filter program 652 | # by executing (via popen()) the command , where 653 | # is the value of the INPUT_FILTER tag, and is the name of an 654 | # input file. Doxygen will then use the output that the filter program writes 655 | # to standard output. 656 | # If FILTER_PATTERNS is specified, this tag will be 657 | # ignored. 658 | 659 | INPUT_FILTER = 660 | 661 | # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern 662 | # basis. 663 | # Doxygen will compare the file name with each pattern and apply the 664 | # filter if there is a match. 665 | # The filters are a list of the form: 666 | # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further 667 | # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER 668 | # is applied to all files. 669 | 670 | FILTER_PATTERNS = *.lua=./lua2dox 671 | 672 | # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 673 | # INPUT_FILTER) will be used to filter the input files when producing source 674 | # files to browse (i.e. when SOURCE_BROWSER is set to YES). 675 | 676 | FILTER_SOURCE_FILES = NO 677 | 678 | #--------------------------------------------------------------------------- 679 | # configuration options related to source browsing 680 | #--------------------------------------------------------------------------- 681 | 682 | # If the SOURCE_BROWSER tag is set to YES then a list of source files will 683 | # be generated. Documented entities will be cross-referenced with these sources. 684 | # Note: To get rid of all source code in the generated output, make sure also 685 | # VERBATIM_HEADERS is set to NO. 686 | 687 | SOURCE_BROWSER = NO 688 | 689 | # Setting the INLINE_SOURCES tag to YES will include the body 690 | # of functions and classes directly in the documentation. 691 | 692 | INLINE_SOURCES = NO 693 | 694 | # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 695 | # doxygen to hide any special comment blocks from generated source code 696 | # fragments. Normal C and C++ comments will always remain visible. 697 | 698 | STRIP_CODE_COMMENTS = YES 699 | 700 | # If the REFERENCED_BY_RELATION tag is set to YES 701 | # then for each documented function all documented 702 | # functions referencing it will be listed. 703 | 704 | REFERENCED_BY_RELATION = NO 705 | 706 | # If the REFERENCES_RELATION tag is set to YES 707 | # then for each documented function all documented entities 708 | # called/used by that function will be listed. 709 | 710 | REFERENCES_RELATION = NO 711 | 712 | # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) 713 | # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from 714 | # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will 715 | # link to the source code. 716 | # Otherwise they will link to the documentation. 717 | 718 | REFERENCES_LINK_SOURCE = YES 719 | 720 | # If the USE_HTAGS tag is set to YES then the references to source code 721 | # will point to the HTML generated by the htags(1) tool instead of doxygen 722 | # built-in source browser. The htags tool is part of GNU's global source 723 | # tagging system (see http://www.gnu.org/software/global/global.html). You 724 | # will need version 4.8.6 or higher. 725 | 726 | USE_HTAGS = NO 727 | 728 | # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 729 | # will generate a verbatim copy of the header file for each class for 730 | # which an include is specified. Set to NO to disable this. 731 | 732 | VERBATIM_HEADERS = YES 733 | 734 | #--------------------------------------------------------------------------- 735 | # configuration options related to the alphabetical class index 736 | #--------------------------------------------------------------------------- 737 | 738 | # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 739 | # of all compounds will be generated. Enable this if the project 740 | # contains a lot of classes, structs, unions or interfaces. 741 | 742 | ALPHABETICAL_INDEX = NO 743 | 744 | # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 745 | # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 746 | # in which this list will be split (can be a number in the range [1..20]) 747 | 748 | COLS_IN_ALPHA_INDEX = 5 749 | 750 | # In case all classes in a project start with a common prefix, all 751 | # classes will be put under the same header in the alphabetical index. 752 | # The IGNORE_PREFIX tag can be used to specify one or more prefixes that 753 | # should be ignored while generating the index headers. 754 | 755 | IGNORE_PREFIX = 756 | 757 | #--------------------------------------------------------------------------- 758 | # configuration options related to the HTML output 759 | #--------------------------------------------------------------------------- 760 | 761 | # If the GENERATE_HTML tag is set to YES (the default) Doxygen will 762 | # generate HTML output. 763 | 764 | GENERATE_HTML = YES 765 | 766 | # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 767 | # If a relative path is entered the value of OUTPUT_DIRECTORY will be 768 | # put in front of it. If left blank `html' will be used as the default path. 769 | 770 | HTML_OUTPUT = html 771 | 772 | # The HTML_FILE_EXTENSION tag can be used to specify the file extension for 773 | # each generated HTML page (for example: .htm,.php,.asp). If it is left blank 774 | # doxygen will generate files with .html extension. 775 | 776 | HTML_FILE_EXTENSION = .html 777 | 778 | # The HTML_HEADER tag can be used to specify a personal HTML header for 779 | # each generated HTML page. If it is left blank doxygen will generate a 780 | # standard header. 781 | 782 | HTML_HEADER = 783 | 784 | # The HTML_FOOTER tag can be used to specify a personal HTML footer for 785 | # each generated HTML page. If it is left blank doxygen will generate a 786 | # standard footer. 787 | 788 | HTML_FOOTER = 789 | 790 | # The HTML_STYLESHEET tag can be used to specify a user-defined cascading 791 | # style sheet that is used by each HTML page. It can be used to 792 | # fine-tune the look of the HTML output. If the tag is left blank doxygen 793 | # will generate a default style sheet. Note that doxygen will try to copy 794 | # the style sheet file to the HTML output directory, so don't put your own 795 | # stylesheet in the HTML output directory as well, or it will be erased! 796 | 797 | HTML_STYLESHEET = 798 | 799 | # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 800 | # files or namespaces will be aligned in HTML using tables. If set to 801 | # NO a bullet list will be used. 802 | 803 | HTML_ALIGN_MEMBERS = YES 804 | 805 | # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML 806 | # documentation will contain sections that can be hidden and shown after the 807 | # page has loaded. For this to work a browser that supports 808 | # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox 809 | # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). 810 | 811 | HTML_DYNAMIC_SECTIONS = NO 812 | 813 | # If the GENERATE_DOCSET tag is set to YES, additional index files 814 | # will be generated that can be used as input for Apple's Xcode 3 815 | # integrated development environment, introduced with OSX 10.5 (Leopard). 816 | # To create a documentation set, doxygen will generate a Makefile in the 817 | # HTML output directory. Running make will produce the docset in that 818 | # directory and running "make install" will install the docset in 819 | # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find 820 | # it at startup. 821 | # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. 822 | 823 | GENERATE_DOCSET = NO 824 | 825 | # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the 826 | # feed. A documentation feed provides an umbrella under which multiple 827 | # documentation sets from a single provider (such as a company or product suite) 828 | # can be grouped. 829 | 830 | DOCSET_FEEDNAME = "Doxygen generated docs" 831 | 832 | # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that 833 | # should uniquely identify the documentation set bundle. This should be a 834 | # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen 835 | # will append .docset to the name. 836 | 837 | DOCSET_BUNDLE_ID = org.doxygen.Project 838 | 839 | # If the GENERATE_HTMLHELP tag is set to YES, additional index files 840 | # will be generated that can be used as input for tools like the 841 | # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) 842 | # of the generated HTML documentation. 843 | 844 | GENERATE_HTMLHELP = NO 845 | 846 | # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 847 | # be used to specify the file name of the resulting .chm file. You 848 | # can add a path in front of the file if the result should not be 849 | # written to the html output directory. 850 | 851 | CHM_FILE = 852 | 853 | # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 854 | # be used to specify the location (absolute path including file name) of 855 | # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run 856 | # the HTML help compiler on the generated index.hhp. 857 | 858 | HHC_LOCATION = 859 | 860 | # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 861 | # controls if a separate .chi index file is generated (YES) or that 862 | # it should be included in the master .chm file (NO). 863 | 864 | GENERATE_CHI = NO 865 | 866 | # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING 867 | # is used to encode HtmlHelp index (hhk), content (hhc) and project file 868 | # content. 869 | 870 | CHM_INDEX_ENCODING = 871 | 872 | # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 873 | # controls whether a binary table of contents is generated (YES) or a 874 | # normal table of contents (NO) in the .chm file. 875 | 876 | BINARY_TOC = NO 877 | 878 | # The TOC_EXPAND flag can be set to YES to add extra items for group members 879 | # to the contents of the HTML help documentation and to the tree view. 880 | 881 | TOC_EXPAND = NO 882 | 883 | # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER 884 | # are set, an additional index file will be generated that can be used as input for 885 | # Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated 886 | # HTML documentation. 887 | 888 | GENERATE_QHP = NO 889 | 890 | # If the QHG_LOCATION tag is specified, the QCH_FILE tag can 891 | # be used to specify the file name of the resulting .qch file. 892 | # The path specified is relative to the HTML output folder. 893 | 894 | QCH_FILE = 895 | 896 | # The QHP_NAMESPACE tag specifies the namespace to use when generating 897 | # Qt Help Project output. For more information please see 898 | # http://doc.trolltech.com/qthelpproject.html#namespace 899 | 900 | QHP_NAMESPACE = 901 | 902 | # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating 903 | # Qt Help Project output. For more information please see 904 | # http://doc.trolltech.com/qthelpproject.html#virtual-folders 905 | 906 | QHP_VIRTUAL_FOLDER = doc 907 | 908 | # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. 909 | # For more information please see 910 | # http://doc.trolltech.com/qthelpproject.html#custom-filters 911 | 912 | QHP_CUST_FILTER_NAME = 913 | 914 | # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see 915 | # Qt Help Project / Custom Filters. 916 | 917 | QHP_CUST_FILTER_ATTRS = 918 | 919 | # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's 920 | # filter section matches. 921 | # Qt Help Project / Filter Attributes. 922 | 923 | QHP_SECT_FILTER_ATTRS = 924 | 925 | # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can 926 | # be used to specify the location of Qt's qhelpgenerator. 927 | # If non-empty doxygen will try to run qhelpgenerator on the generated 928 | # .qhp file. 929 | 930 | QHG_LOCATION = 931 | 932 | # The DISABLE_INDEX tag can be used to turn on/off the condensed index at 933 | # top of each HTML page. The value NO (the default) enables the index and 934 | # the value YES disables it. 935 | 936 | DISABLE_INDEX = NO 937 | 938 | # This tag can be used to set the number of enum values (range [1..20]) 939 | # that doxygen will group on one line in the generated HTML documentation. 940 | 941 | ENUM_VALUES_PER_LINE = 4 942 | 943 | # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index 944 | # structure should be generated to display hierarchical information. 945 | # If the tag value is set to YES, a side panel will be generated 946 | # containing a tree-like index structure (just like the one that 947 | # is generated for HTML Help). For this to work a browser that supports 948 | # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). 949 | # Windows users are probably better off using the HTML help feature. 950 | 951 | GENERATE_TREEVIEW = NO 952 | 953 | # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, 954 | # and Class Hierarchy pages using a tree view instead of an ordered list. 955 | 956 | USE_INLINE_TREES = NO 957 | 958 | # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 959 | # used to set the initial width (in pixels) of the frame in which the tree 960 | # is shown. 961 | 962 | TREEVIEW_WIDTH = 250 963 | 964 | # Use this tag to change the font size of Latex formulas included 965 | # as images in the HTML documentation. The default is 10. Note that 966 | # when you change the font size after a successful doxygen run you need 967 | # to manually remove any form_*.png images from the HTML output directory 968 | # to force them to be regenerated. 969 | 970 | FORMULA_FONTSIZE = 10 971 | 972 | # When the SEARCHENGINE tag is enable doxygen will generate a search box for the HTML output. The underlying search engine uses javascript 973 | # and DHTML and should work on any modern browser. Note that when using HTML help (GENERATE_HTMLHELP) or Qt help (GENERATE_QHP) 974 | # there is already a search function so this one should typically 975 | # be disabled. 976 | 977 | SEARCHENGINE = YES 978 | 979 | #--------------------------------------------------------------------------- 980 | # configuration options related to the LaTeX output 981 | #--------------------------------------------------------------------------- 982 | 983 | # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 984 | # generate Latex output. 985 | 986 | GENERATE_LATEX = NO 987 | 988 | # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 989 | # If a relative path is entered the value of OUTPUT_DIRECTORY will be 990 | # put in front of it. If left blank `latex' will be used as the default path. 991 | 992 | LATEX_OUTPUT = latex 993 | 994 | # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 995 | # invoked. If left blank `latex' will be used as the default command name. 996 | 997 | LATEX_CMD_NAME = latex 998 | 999 | # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 1000 | # generate index for LaTeX. If left blank `makeindex' will be used as the 1001 | # default command name. 1002 | 1003 | MAKEINDEX_CMD_NAME = makeindex 1004 | 1005 | # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 1006 | # LaTeX documents. This may be useful for small projects and may help to 1007 | # save some trees in general. 1008 | 1009 | COMPACT_LATEX = NO 1010 | 1011 | # The PAPER_TYPE tag can be used to set the paper type that is used 1012 | # by the printer. Possible values are: a4, a4wide, letter, legal and 1013 | # executive. If left blank a4wide will be used. 1014 | 1015 | PAPER_TYPE = a4wide 1016 | 1017 | # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 1018 | # packages that should be included in the LaTeX output. 1019 | 1020 | EXTRA_PACKAGES = 1021 | 1022 | # The LATEX_HEADER tag can be used to specify a personal LaTeX header for 1023 | # the generated latex document. The header should contain everything until 1024 | # the first chapter. If it is left blank doxygen will generate a 1025 | # standard header. Notice: only use this tag if you know what you are doing! 1026 | 1027 | LATEX_HEADER = 1028 | 1029 | # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 1030 | # is prepared for conversion to pdf (using ps2pdf). The pdf file will 1031 | # contain links (just like the HTML output) instead of page references 1032 | # This makes the output suitable for online browsing using a pdf viewer. 1033 | 1034 | PDF_HYPERLINKS = YES 1035 | 1036 | # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 1037 | # plain latex in the generated Makefile. Set this option to YES to get a 1038 | # higher quality PDF documentation. 1039 | 1040 | USE_PDFLATEX = YES 1041 | 1042 | # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 1043 | # command to the generated LaTeX files. This will instruct LaTeX to keep 1044 | # running if errors occur, instead of asking the user for help. 1045 | # This option is also used when generating formulas in HTML. 1046 | 1047 | LATEX_BATCHMODE = NO 1048 | 1049 | # If LATEX_HIDE_INDICES is set to YES then doxygen will not 1050 | # include the index chapters (such as File Index, Compound Index, etc.) 1051 | # in the output. 1052 | 1053 | LATEX_HIDE_INDICES = NO 1054 | 1055 | # If LATEX_SOURCE_CODE is set to YES then doxygen will include source code with syntax highlighting in the LaTeX output. Note that which sources are shown also depends on other settings such as SOURCE_BROWSER. 1056 | 1057 | LATEX_SOURCE_CODE = NO 1058 | 1059 | #--------------------------------------------------------------------------- 1060 | # configuration options related to the RTF output 1061 | #--------------------------------------------------------------------------- 1062 | 1063 | # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 1064 | # The RTF output is optimized for Word 97 and may not look very pretty with 1065 | # other RTF readers or editors. 1066 | 1067 | GENERATE_RTF = NO 1068 | 1069 | # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 1070 | # If a relative path is entered the value of OUTPUT_DIRECTORY will be 1071 | # put in front of it. If left blank `rtf' will be used as the default path. 1072 | 1073 | RTF_OUTPUT = rtf 1074 | 1075 | # If the COMPACT_RTF tag is set to YES Doxygen generates more compact 1076 | # RTF documents. This may be useful for small projects and may help to 1077 | # save some trees in general. 1078 | 1079 | COMPACT_RTF = NO 1080 | 1081 | # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 1082 | # will contain hyperlink fields. The RTF file will 1083 | # contain links (just like the HTML output) instead of page references. 1084 | # This makes the output suitable for online browsing using WORD or other 1085 | # programs which support those fields. 1086 | # Note: wordpad (write) and others do not support links. 1087 | 1088 | RTF_HYPERLINKS = NO 1089 | 1090 | # Load stylesheet definitions from file. Syntax is similar to doxygen's 1091 | # config file, i.e. a series of assignments. You only have to provide 1092 | # replacements, missing definitions are set to their default value. 1093 | 1094 | RTF_STYLESHEET_FILE = 1095 | 1096 | # Set optional variables used in the generation of an rtf document. 1097 | # Syntax is similar to doxygen's config file. 1098 | 1099 | RTF_EXTENSIONS_FILE = 1100 | 1101 | #--------------------------------------------------------------------------- 1102 | # configuration options related to the man page output 1103 | #--------------------------------------------------------------------------- 1104 | 1105 | # If the GENERATE_MAN tag is set to YES (the default) Doxygen will 1106 | # generate man pages 1107 | 1108 | GENERATE_MAN = NO 1109 | 1110 | # The MAN_OUTPUT tag is used to specify where the man pages will be put. 1111 | # If a relative path is entered the value of OUTPUT_DIRECTORY will be 1112 | # put in front of it. If left blank `man' will be used as the default path. 1113 | 1114 | MAN_OUTPUT = man 1115 | 1116 | # The MAN_EXTENSION tag determines the extension that is added to 1117 | # the generated man pages (default is the subroutine's section .3) 1118 | 1119 | MAN_EXTENSION = .3 1120 | 1121 | # If the MAN_LINKS tag is set to YES and Doxygen generates man output, 1122 | # then it will generate one additional man file for each entity 1123 | # documented in the real man page(s). These additional files 1124 | # only source the real man page, but without them the man command 1125 | # would be unable to find the correct page. The default is NO. 1126 | 1127 | MAN_LINKS = NO 1128 | 1129 | #--------------------------------------------------------------------------- 1130 | # configuration options related to the XML output 1131 | #--------------------------------------------------------------------------- 1132 | 1133 | # If the GENERATE_XML tag is set to YES Doxygen will 1134 | # generate an XML file that captures the structure of 1135 | # the code including all documentation. 1136 | 1137 | GENERATE_XML = NO 1138 | 1139 | # The XML_OUTPUT tag is used to specify where the XML pages will be put. 1140 | # If a relative path is entered the value of OUTPUT_DIRECTORY will be 1141 | # put in front of it. If left blank `xml' will be used as the default path. 1142 | 1143 | XML_OUTPUT = xml 1144 | 1145 | # The XML_SCHEMA tag can be used to specify an XML schema, 1146 | # which can be used by a validating XML parser to check the 1147 | # syntax of the XML files. 1148 | 1149 | XML_SCHEMA = 1150 | 1151 | # The XML_DTD tag can be used to specify an XML DTD, 1152 | # which can be used by a validating XML parser to check the 1153 | # syntax of the XML files. 1154 | 1155 | XML_DTD = 1156 | 1157 | # If the XML_PROGRAMLISTING tag is set to YES Doxygen will 1158 | # dump the program listings (including syntax highlighting 1159 | # and cross-referencing information) to the XML output. Note that 1160 | # enabling this will significantly increase the size of the XML output. 1161 | 1162 | XML_PROGRAMLISTING = YES 1163 | 1164 | #--------------------------------------------------------------------------- 1165 | # configuration options for the AutoGen Definitions output 1166 | #--------------------------------------------------------------------------- 1167 | 1168 | # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 1169 | # generate an AutoGen Definitions (see autogen.sf.net) file 1170 | # that captures the structure of the code including all 1171 | # documentation. Note that this feature is still experimental 1172 | # and incomplete at the moment. 1173 | 1174 | GENERATE_AUTOGEN_DEF = NO 1175 | 1176 | #--------------------------------------------------------------------------- 1177 | # configuration options related to the Perl module output 1178 | #--------------------------------------------------------------------------- 1179 | 1180 | # If the GENERATE_PERLMOD tag is set to YES Doxygen will 1181 | # generate a Perl module file that captures the structure of 1182 | # the code including all documentation. Note that this 1183 | # feature is still experimental and incomplete at the 1184 | # moment. 1185 | 1186 | GENERATE_PERLMOD = NO 1187 | 1188 | # If the PERLMOD_LATEX tag is set to YES Doxygen will generate 1189 | # the necessary Makefile rules, Perl scripts and LaTeX code to be able 1190 | # to generate PDF and DVI output from the Perl module output. 1191 | 1192 | PERLMOD_LATEX = NO 1193 | 1194 | # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be 1195 | # nicely formatted so it can be parsed by a human reader. 1196 | # This is useful 1197 | # if you want to understand what is going on. 1198 | # On the other hand, if this 1199 | # tag is set to NO the size of the Perl module output will be much smaller 1200 | # and Perl will parse it just the same. 1201 | 1202 | PERLMOD_PRETTY = YES 1203 | 1204 | # The names of the make variables in the generated doxyrules.make file 1205 | # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. 1206 | # This is useful so different doxyrules.make files included by the same 1207 | # Makefile don't overwrite each other's variables. 1208 | 1209 | PERLMOD_MAKEVAR_PREFIX = 1210 | 1211 | #--------------------------------------------------------------------------- 1212 | # Configuration options related to the preprocessor 1213 | #--------------------------------------------------------------------------- 1214 | 1215 | # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 1216 | # evaluate all C-preprocessor directives found in the sources and include 1217 | # files. 1218 | 1219 | ENABLE_PREPROCESSING = YES 1220 | 1221 | # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 1222 | # names in the source code. If set to NO (the default) only conditional 1223 | # compilation will be performed. Macro expansion can be done in a controlled 1224 | # way by setting EXPAND_ONLY_PREDEF to YES. 1225 | 1226 | MACRO_EXPANSION = NO 1227 | 1228 | # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 1229 | # then the macro expansion is limited to the macros specified with the 1230 | # PREDEFINED and EXPAND_AS_DEFINED tags. 1231 | 1232 | EXPAND_ONLY_PREDEF = NO 1233 | 1234 | # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 1235 | # in the INCLUDE_PATH (see below) will be search if a #include is found. 1236 | 1237 | SEARCH_INCLUDES = YES 1238 | 1239 | # The INCLUDE_PATH tag can be used to specify one or more directories that 1240 | # contain include files that are not input files but should be processed by 1241 | # the preprocessor. 1242 | 1243 | INCLUDE_PATH = 1244 | 1245 | # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 1246 | # patterns (like *.h and *.hpp) to filter out the header-files in the 1247 | # directories. If left blank, the patterns specified with FILE_PATTERNS will 1248 | # be used. 1249 | 1250 | INCLUDE_FILE_PATTERNS = 1251 | 1252 | # The PREDEFINED tag can be used to specify one or more macro names that 1253 | # are defined before the preprocessor is started (similar to the -D option of 1254 | # gcc). The argument of the tag is a list of macros of the form: name 1255 | # or name=definition (no spaces). If the definition and the = are 1256 | # omitted =1 is assumed. To prevent a macro definition from being 1257 | # undefined via #undef or recursively expanded use the := operator 1258 | # instead of the = operator. 1259 | 1260 | PREDEFINED = 1261 | 1262 | # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 1263 | # this tag can be used to specify a list of macro names that should be expanded. 1264 | # The macro definition that is found in the sources will be used. 1265 | # Use the PREDEFINED tag if you want to use a different macro definition. 1266 | 1267 | EXPAND_AS_DEFINED = 1268 | 1269 | # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 1270 | # doxygen's preprocessor will remove all function-like macros that are alone 1271 | # on a line, have an all uppercase name, and do not end with a semicolon. Such 1272 | # function macros are typically used for boiler-plate code, and will confuse 1273 | # the parser if not removed. 1274 | 1275 | SKIP_FUNCTION_MACROS = YES 1276 | 1277 | #--------------------------------------------------------------------------- 1278 | # Configuration::additions related to external references 1279 | #--------------------------------------------------------------------------- 1280 | 1281 | # The TAGFILES option can be used to specify one or more tagfiles. 1282 | # Optionally an initial location of the external documentation 1283 | # can be added for each tagfile. The format of a tag file without 1284 | # this location is as follows: 1285 | # 1286 | # TAGFILES = file1 file2 ... 1287 | # Adding location for the tag files is done as follows: 1288 | # 1289 | # TAGFILES = file1=loc1 "file2 = loc2" ... 1290 | # where "loc1" and "loc2" can be relative or absolute paths or 1291 | # URLs. If a location is present for each tag, the installdox tool 1292 | # does not have to be run to correct the links. 1293 | # Note that each tag file must have a unique name 1294 | # (where the name does NOT include the path) 1295 | # If a tag file is not located in the directory in which doxygen 1296 | # is run, you must also specify the path to the tagfile here. 1297 | 1298 | TAGFILES = 1299 | 1300 | # When a file name is specified after GENERATE_TAGFILE, doxygen will create 1301 | # a tag file that is based on the input files it reads. 1302 | 1303 | GENERATE_TAGFILE = 1304 | 1305 | # If the ALLEXTERNALS tag is set to YES all external classes will be listed 1306 | # in the class index. If set to NO only the inherited external classes 1307 | # will be listed. 1308 | 1309 | ALLEXTERNALS = NO 1310 | 1311 | # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 1312 | # in the modules index. If set to NO, only the current project's groups will 1313 | # be listed. 1314 | 1315 | EXTERNAL_GROUPS = YES 1316 | 1317 | # The PERL_PATH should be the absolute path and name of the perl script 1318 | # interpreter (i.e. the result of `which perl'). 1319 | 1320 | PERL_PATH = /usr/bin/perl 1321 | 1322 | #--------------------------------------------------------------------------- 1323 | # Configuration options related to the dot tool 1324 | #--------------------------------------------------------------------------- 1325 | 1326 | # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 1327 | # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base 1328 | # or super classes. Setting the tag to NO turns the diagrams off. Note that 1329 | # this option is superseded by the HAVE_DOT option below. This is only a 1330 | # fallback. It is recommended to install and use dot, since it yields more 1331 | # powerful graphs. 1332 | 1333 | CLASS_DIAGRAMS = YES 1334 | 1335 | # You can define message sequence charts within doxygen comments using the \msc 1336 | # command. Doxygen will then run the mscgen tool (see 1337 | # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the 1338 | # documentation. The MSCGEN_PATH tag allows you to specify the directory where 1339 | # the mscgen tool resides. If left empty the tool is assumed to be found in the 1340 | # default search path. 1341 | 1342 | MSCGEN_PATH = 1343 | 1344 | # If set to YES, the inheritance and collaboration graphs will hide 1345 | # inheritance and usage relations if the target is undocumented 1346 | # or is not a class. 1347 | 1348 | HIDE_UNDOC_RELATIONS = YES 1349 | 1350 | # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 1351 | # available from the path. This tool is part of Graphviz, a graph visualization 1352 | # toolkit from AT&T and Lucent Bell Labs. The other options in this section 1353 | # have no effect if this option is set to NO (the default) 1354 | 1355 | HAVE_DOT = NO 1356 | 1357 | # By default doxygen will write a font called FreeSans.ttf to the output 1358 | # directory and reference it in all dot files that doxygen generates. This 1359 | # font does not include all possible unicode characters however, so when you need 1360 | # these (or just want a differently looking font) you can specify the font name 1361 | # using DOT_FONTNAME. You need need to make sure dot is able to find the font, 1362 | # which can be done by putting it in a standard location or by setting the 1363 | # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory 1364 | # containing the font. 1365 | 1366 | DOT_FONTNAME = FreeSans 1367 | 1368 | # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. 1369 | # The default size is 10pt. 1370 | 1371 | DOT_FONTSIZE = 10 1372 | 1373 | # By default doxygen will tell dot to use the output directory to look for the 1374 | # FreeSans.ttf font (which doxygen will put there itself). If you specify a 1375 | # different font using DOT_FONTNAME you can set the path where dot 1376 | # can find it using this tag. 1377 | 1378 | DOT_FONTPATH = 1379 | 1380 | # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 1381 | # will generate a graph for each documented class showing the direct and 1382 | # indirect inheritance relations. Setting this tag to YES will force the 1383 | # the CLASS_DIAGRAMS tag to NO. 1384 | 1385 | CLASS_GRAPH = YES 1386 | 1387 | # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 1388 | # will generate a graph for each documented class showing the direct and 1389 | # indirect implementation dependencies (inheritance, containment, and 1390 | # class references variables) of the class with other documented classes. 1391 | 1392 | COLLABORATION_GRAPH = YES 1393 | 1394 | # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen 1395 | # will generate a graph for groups, showing the direct groups dependencies 1396 | 1397 | GROUP_GRAPHS = YES 1398 | 1399 | # If the UML_LOOK tag is set to YES doxygen will generate inheritance and 1400 | # collaboration diagrams in a style similar to the OMG's Unified Modeling 1401 | # Language. 1402 | 1403 | UML_LOOK = NO 1404 | 1405 | # If set to YES, the inheritance and collaboration graphs will show the 1406 | # relations between templates and their instances. 1407 | 1408 | TEMPLATE_RELATIONS = NO 1409 | 1410 | # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 1411 | # tags are set to YES then doxygen will generate a graph for each documented 1412 | # file showing the direct and indirect include dependencies of the file with 1413 | # other documented files. 1414 | 1415 | INCLUDE_GRAPH = YES 1416 | 1417 | # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 1418 | # HAVE_DOT tags are set to YES then doxygen will generate a graph for each 1419 | # documented header file showing the documented files that directly or 1420 | # indirectly include this file. 1421 | 1422 | INCLUDED_BY_GRAPH = YES 1423 | 1424 | # If the CALL_GRAPH and HAVE_DOT options are set to YES then 1425 | # doxygen will generate a call dependency graph for every global function 1426 | # or class method. Note that enabling this option will significantly increase 1427 | # the time of a run. So in most cases it will be better to enable call graphs 1428 | # for selected functions only using the \callgraph command. 1429 | 1430 | CALL_GRAPH = NO 1431 | 1432 | # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then 1433 | # doxygen will generate a caller dependency graph for every global function 1434 | # or class method. Note that enabling this option will significantly increase 1435 | # the time of a run. So in most cases it will be better to enable caller 1436 | # graphs for selected functions only using the \callergraph command. 1437 | 1438 | CALLER_GRAPH = NO 1439 | 1440 | # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 1441 | # will graphical hierarchy of all classes instead of a textual one. 1442 | 1443 | GRAPHICAL_HIERARCHY = YES 1444 | 1445 | # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES 1446 | # then doxygen will show the dependencies a directory has on other directories 1447 | # in a graphical way. The dependency relations are determined by the #include 1448 | # relations between the files in the directories. 1449 | 1450 | DIRECTORY_GRAPH = YES 1451 | 1452 | # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 1453 | # generated by dot. Possible values are png, jpg, or gif 1454 | # If left blank png will be used. 1455 | 1456 | DOT_IMAGE_FORMAT = png 1457 | 1458 | # The tag DOT_PATH can be used to specify the path where the dot tool can be 1459 | # found. If left blank, it is assumed the dot tool can be found in the path. 1460 | 1461 | DOT_PATH = 1462 | 1463 | # The DOTFILE_DIRS tag can be used to specify one or more directories that 1464 | # contain dot files that are included in the documentation (see the 1465 | # \dotfile command). 1466 | 1467 | DOTFILE_DIRS = 1468 | 1469 | # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of 1470 | # nodes that will be shown in the graph. If the number of nodes in a graph 1471 | # becomes larger than this value, doxygen will truncate the graph, which is 1472 | # visualized by representing a node as a red box. Note that doxygen if the 1473 | # number of direct children of the root node in a graph is already larger than 1474 | # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note 1475 | # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. 1476 | 1477 | DOT_GRAPH_MAX_NODES = 50 1478 | 1479 | # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the 1480 | # graphs generated by dot. A depth value of 3 means that only nodes reachable 1481 | # from the root by following a path via at most 3 edges will be shown. Nodes 1482 | # that lay further from the root node will be omitted. Note that setting this 1483 | # option to 1 or 2 may greatly reduce the computation time needed for large 1484 | # code bases. Also note that the size of a graph can be further restricted by 1485 | # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. 1486 | 1487 | MAX_DOT_GRAPH_DEPTH = 0 1488 | 1489 | # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent 1490 | # background. This is disabled by default, because dot on Windows does not 1491 | # seem to support this out of the box. Warning: Depending on the platform used, 1492 | # enabling this option may lead to badly anti-aliased labels on the edges of 1493 | # a graph (i.e. they become hard to read). 1494 | 1495 | DOT_TRANSPARENT = NO 1496 | 1497 | # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output 1498 | # files in one run (i.e. multiple -o and -T options on the command line). This 1499 | # makes dot run faster, but since only newer versions of dot (>1.8.10) 1500 | # support this, this feature is disabled by default. 1501 | 1502 | DOT_MULTI_TARGETS = NO 1503 | 1504 | # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 1505 | # generate a legend page explaining the meaning of the various boxes and 1506 | # arrows in the dot generated graphs. 1507 | 1508 | GENERATE_LEGEND = YES 1509 | 1510 | # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 1511 | # remove the intermediate dot files that are used to generate 1512 | # the various graphs. 1513 | 1514 | DOT_CLEANUP = YES -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | :Firth language, reference implementation. 2 | Copyright © 2015 Brigham Toskin 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # :Firth 2 | 3 | :Firth (n.) – a Forth-like language designed for DSL creation, implemented in Lua. 4 | 5 | ## Alpha Warning! 6 | This code is incomplete, broken in places, and generally not ready for prime 7 | time. I decided to make it available on Github because, why not? 8 | 9 | ## What's All This, Then? 10 | I had previously created a forth-like language / parser / interpreter / virtual 11 | machine / _thing_, and had a great time doing it. It was far more successful an 12 | endeavor than I had dared to hope. Unfortunately, the project ended up growing 13 | into an unmaintainable, convoluted mess, and it was thus abandoned. 14 | 15 | So what went wrong? Well, for one, the project originally started out as an 16 | extremely simple INI file reader for parsing static polygon definitions to be 17 | rendered at runtime. Over successive iterations of refactoring it to be more 18 | interactive, flexible, and turing-complete, it eventually became a full-fledged 19 | stack-based language, with a compiler, virtual machine, and runtime library. 20 | 21 | I blame the constant goading and "what if you..?" comments I received from my 22 | friend Paul during development. 23 | 24 | It grew into something it was never intended to be, and—lacking a sound 25 | foundation—crumbled under the weight of this added complexity. That said, I do 26 | still hold that the core ideas were sound and reasonable. Now, years later, I 27 | am keen to take another go at it, coming into the experiment with both eyes open. 28 | I learned a lot from that initial foray into forthitude, and have a lot of new 29 | ideas about how to do things and how to make it an awesome language and 30 | programming environment to work with. 31 | 32 | We'll get into more details about what the language is, how it works, and what 33 | to do with it as it develops. 34 | 35 | ## Building and Running 36 | Currently, it is implemented completely in the LuaJIT dialect of Lua 5.1. This 37 | means it has a few Lua 5.2-isms, and should generally run on PUC-Rio 5.2 as well. 38 | It doesn't depend on any 3rd party libraries or C extensions. That said, it is 39 | recommended to use [LuaJIT](http://luajit.org/), version 2.0.2 or later, for optimal performance. 40 | 41 | You can require the libraries from Lua code, run the REPL from the command line, 42 | or load the whole thing at runtime from the Lua API. Details to follow. 43 | 44 | To build the Doxygen documentation, you need Perl 5 and the Doxygen::Lua 45 | perl module (available on CPAN) installed on your system. 46 | 47 | ## Examples 48 | The _examples_ directory is where to look for code that shows off the :Firth's 49 | power and abilities. Currently, there is only iniloader.firth, and a sample input file. 50 | You would run it from the :Firth REPL thusly: 51 | 52 | ```forth 53 | " examples/iniloader.firth" loadfile 54 | inifile: examples/test.ini 55 | BLERG @ . 56 | ``` 57 | 58 | You should see output `127.0.0.1:8080 ok` if it all ran successfully 59 | 60 | ## License 61 | Copyright © 2015 Brigham Toskin. 62 | [MIT License](https://github.com/IonoclastBrigham/firth/blob/master/LICENSE). 63 | -------------------------------------------------------------------------------- /Syntax/BBEdit/Firth.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BBEditDocumentType 6 | CodelessLanguageModule 7 | 8 | BBLMLanguageDisplayName 9 | :Firth 10 | BBLMLanguageCode 11 | clfr 12 | 13 | BBLMSuffixMap 14 | 15 | 16 | BBLMLanguageSuffix 17 | .firth 18 | 19 | 20 | 21 | 22 | BBLMColorsSyntax 23 | 24 | 25 | BBLMIsCaseSensitive 26 | 27 | 28 | BBLMKeywordList 29 | 30 | : 31 | ; 32 | 33 | dup 34 | over 35 | drop 36 | clear 37 | swap 38 | rot 39 | -rot 40 | pick 41 | 42 | C> 43 | >C 44 | C@ 45 | 2C> 46 | 2>C 47 | 2C@ 48 | 49 | binop 50 | binopconst 51 | 52 | if 53 | else 54 | loops 55 | do 56 | end 57 | 58 | true 59 | false 60 | not 61 | 2not 62 | nil 63 | {} 64 | 65 | .raw 66 | . 67 | .x 68 | .. 69 | .S 70 | .C 71 | 72 | loadfile 73 | exit 74 | setcompiling 75 | settarget 76 | interpretpending 77 | interpret 78 | compiling? 79 | parse 80 | parsematch 81 | >ts 82 | push 83 | create 84 | alias: 85 | buildfunc 86 | bindfunc 87 | does> 88 | dict 89 | last 90 | defined? 91 | @@ 92 | !! 93 | immediate 94 | char 95 | call 96 | 97 | dump 98 | dumpword: 99 | trace 100 | notrace 101 | tracing? 102 | path 103 | calls: 104 | calledby: 105 | tstart 106 | tend 107 | 108 | & 109 | | 110 | ^ 111 | ~ 112 | 113 | NOP 114 | 115 | 116 | BBLMCommentLineDefault 117 | // 118 | 119 | BBLMScansFunctions 120 | 121 | 122 | Language Features 123 | 124 | Identifier and Keyword Characters 125 | !$%&*+-./:<=>?!@^_~\[\]\\{}|'"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz 126 | 127 | Comment Pattern 128 | 136 | 137 | String Pattern 138 | 145 | 146 | Function Pattern 147 | ^\s*) 150 | (?P 151 | :\s+ 152 | (?P 153 | [!$%&*+-./:<=>?!@^_~\[\]\\{}|'"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz]+ 154 | ) 155 | (?s:.+?) 156 | (?:;|;immed) 157 | ) 158 | ) 159 | ]]> 160 | 161 | Skip Pattern 162 | string) | 165 | (?P>comment) 166 | ) 167 | ]]> 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /Syntax/Emacs/firth-mode.el: -------------------------------------------------------------------------------- 1 | ;; firth-mode.el 2 | ;; :Firth source editing major mode 3 | ;; 4 | ;; Copyright © 2015 Brigham Toskin 5 | ;; 6 | ;; To automatically load firth-mode for .firth files, install firth-mode.el on 7 | ;; your library load path and add the following lines to your .emacs file: 8 | ;; (autoload 'firth-mode "firth-mode" nil t) 9 | ;; (add-to-list 'auto-mode-alist '("\\.firth\\'" . firth-mode)) 10 | 11 | ; keymap for macro keys 12 | (defvar firth-mode-map 13 | (let ((map (make-keymap))) 14 | (define-key map "\C-j" 'newline-and-indent) 15 | map) 16 | "Keymap for :Firth Major Mode") 17 | 18 | ;;; Syntax Highlighting ;;; 19 | 20 | (defface firth-prims 21 | '((t (:inherit font-lock-builtin-face))) 22 | ":Firth prims font." 23 | :group 'font-lock-faces) 24 | (defvar firth-prims 'firth-prims) 25 | 26 | (defface firth-consts 27 | '((t (:inherit font-lock-constant-face))) 28 | ":Firth const font." 29 | :group 'font-lock-faces) 30 | (defvar firth-consts 'firth-consts) 31 | 32 | (defface firth-defining-words 33 | '((t (:inherit firth-prims :weight bold))) 34 | ":Firth core defining words." 35 | :group 'font-lock-faces) 36 | (defvar firth-defining-words 'firth-defining-words) 37 | 38 | (defface firth-word-def-name 39 | '((t (:inherit font-lock-function-name-face :weight bold))) 40 | ":Firth new word definition name." 41 | :group 'font-lock-faces) 42 | (defvar firth-word-def-name 'firth-word-def-name) 43 | 44 | (defface firth-ticked-word 45 | '((t (:inherit font-lock-keyword-face :slant oblique))) 46 | "Ticked :Firth words." 47 | :group 'font-lock-faces) 48 | (defvar firth-ticked-word 'firth-ticked-word) 49 | 50 | (defface firth-core-words 51 | '((t (:inherit font-lock-keyword-face))) 52 | ":Firth word def names." 53 | :group 'font-lock-faces) 54 | (defvar firth-core-words 'firth-core-words) 55 | 56 | (defconst firth-prims-pattern 57 | (list 58 | `(,(regexp-opt 59 | '("dup" "over" "drop" "clear" "swap" "rot" "-rot" "pick" 60 | "C>" ">C" "C@" "2C>" "2>C" "2C@" 61 | "binop" "binopconst" 62 | "if" "else" "loops" "do" "end" 63 | "not" "2not" 64 | ".raw" "." ".x" ".." ".S" ".C" 65 | "loadfile" "exit" 66 | "setcompiling" "settarget" "interpret" "interpretpending" 67 | "compiling?" "parse" "parsematch" ">ts" "push" "create" 68 | "buildfunc" "bindfunc" "dict" "last" "defined?" 69 | "@@" "!!" "char" "call" "execute" 70 | "dump" "dumpword:" "trace" "notrace" "tracing?" "path" 71 | "calls:" "calledby:" "tstart" "tend" 72 | "&" "|" "^" 73 | "NOP") 'words) (1 firth-prims))) 74 | "Minimal Highlighting for :Firth") 75 | 76 | (defconst firth-consts-pattern 77 | (list 78 | ;; strings and chars 79 | '("\\<\"\\s-.*?\"\\.?\\>" . font-lock-string-face) 80 | '("\\" . font-lock-string-face) 82 | 83 | ;; booleans, nil refs 84 | `(,(regexp-opt '("true" "false" "nil") 'words) . firth-consts) 85 | 86 | ;; integer and float numbers 87 | '("\\<[+-]?[[:digit:]]+\\>" . firth-consts) 88 | '("\\<[+-]?[[:digit:]]+\\.[[:digit:]]+\\(?:[Ee][+-]?[[:digit:]]+\\)?\\>" . firth-consts) 89 | '("\\<0[Xx][[:xdigit:]]+\\>" . firth-consts)) 90 | "Additional constants for highlighting :Firth Mode") 91 | 92 | (defconst firth-comments-pattern 93 | (list 94 | '("\\<\\(//\\)\\>\\(.*\\)" (1 font-lock-comment-face) (2 font-lock-comment-face)) 95 | '("\\<(.*)\\>" . font-lock-comment-face)) 96 | ":Firth standard comments.") 97 | 98 | (defconst firth-agregate-literals-pattern 99 | (list 100 | '("\\<{}\\|\\[\\]\\>" . firth-consts) 101 | '("\\<\\(?:DATA\\|VERSION\\)\\>" . firth-consts)) 102 | "Additional literals for agregate types for :Firth Mode") 103 | 104 | (defconst firth-definitions-pattern 105 | (list 106 | ;; new word definition names 107 | `(,(concat 108 | "\\<\\:\\s-+" ; opening colon 109 | "\\(\\w+\\)" ; capture name 110 | "[[:space:][:word:]]+?" ; def body 111 | "\\(?:bindfunc\\|;\\(?:immed\\|defer\\)?\\)\\>") ; matches def close 112 | (1 'firth-word-def-name)) 113 | 114 | ;; aliases, variables, etc. 115 | `(,(concat 116 | "\\<\\(?:alias\\|var\\|val\\|const\\):" 117 | "\\s-+\\(\\w+\\)\\>") 118 | (1 firth-word-def-name)) 119 | 120 | ;; postponed calls and ticked words 121 | '("\\<\\(?:postpone\\|call:\\|'\\|does>\\|`\\)\\s-+\\(\\w+\\)\\>" 122 | (1 firth-ticked-word)) 123 | 124 | ;; the defining words themselves 125 | `(,(regexp-opt 126 | '(":" ";" "immediate" ";immed" "?deferred" ";defer" 127 | "variable" "var:" "val:" "const:" "alias:" "does>") 'words) 128 | . firth-defining-words)) 129 | "Important defining words from prims and core.") 130 | 131 | (defconst firth-core-words-pattern 132 | (list 133 | `(,(regexp-opt 134 | '("nextword" "postpone" "call:" "xt" 135 | "CR" 136 | "'" "`" "@" "!" 137 | "2dup" "3dup" "2drop" "3drop" 138 | "+" "-" "*" "/" "%" "**" 139 | "1+" "1-" "-1*" "inc" "dec" "neg" 140 | "2+" "2-" "2*" "2/" "double" "halve" 141 | "5+" "5-" "5*" "5/" 142 | "10+" "10-" "10*" "10/" 143 | "100+" "100-" "100*" "100/" "percent" 144 | ">" "<" "=" ">=" "<=" "~=" 145 | "greater?" "less?" "equal?" "greatereq?" "lesseq?" "noteq?" 146 | "and" "or" "nand" "nor" "xor" 147 | "copyright" "©" 148 | "NOOP") 'words) 149 | . firth-core-words) 150 | '("\\<.\"\\s-.*?\"\\>" . font-lock-string-face)) ; print string 151 | "Useful words from :Firth core library.") 152 | 153 | ;; Define some default groupings for syntax highlighting options. 154 | ;; Users of firth-mode may choose one of the below, or set their 155 | ;; own desired mix of highlighting patterns in their .emacs file. 156 | (defconst firth-min-syntax-pattern 157 | (append 158 | firth-prims-pattern 159 | firth-consts-pattern) 160 | "Minimally useful syntax highlighting for :Firth") 161 | 162 | (defconst firth-nicer-syntax-pattern 163 | (append 164 | firth-comments-pattern 165 | firth-min-syntax-pattern 166 | firth-agregate-literals-pattern) 167 | "Nicer syntax highlighting for :Firth with agregates and standard comments.") 168 | 169 | (defconst firth-full-syntax-pattern 170 | (append 171 | firth-comments-pattern 172 | firth-definitions-pattern 173 | firth-consts-pattern 174 | firth-core-words-pattern 175 | firth-prims-pattern 176 | firth-agregate-literals-pattern) 177 | "Full syntax highlighting for :Firth, including defs and core lib.") 178 | 179 | (defvar firth-font-lock-keywords-pattern firth-full-syntax-pattern 180 | "Default Highlighting Expression for :Firth Mode") 181 | 182 | ;; override how some characters are handled by emacs 183 | (defvar firth-mode-syntax-table 184 | (let ((stab (make-syntax-table))) 185 | ;; mark typical "punctuation" chars as valid parts of words 186 | (modify-syntax-entry ?_ "w" stab) 187 | (modify-syntax-entry ?: "w" stab) 188 | (modify-syntax-entry ?; "w" stab) 189 | (modify-syntax-entry ?" "w" stab) ;" 190 | (modify-syntax-entry ?' "w" stab) 191 | (modify-syntax-entry ?. "w" stab) 192 | (modify-syntax-entry ?< "w" stab) 193 | (modify-syntax-entry ?> "w" stab) 194 | (modify-syntax-entry ?( "w" stab) 195 | (modify-syntax-entry ?) "w" stab) 196 | (modify-syntax-entry ?[ "w" stab) 197 | (modify-syntax-entry ?] "w" stab) 198 | (modify-syntax-entry ?{ "w" stab) 199 | (modify-syntax-entry ?} "w" stab) 200 | (modify-syntax-entry ?\\ "w" stab) 201 | (modify-syntax-entry ?+ "w" stab) 202 | (modify-syntax-entry ?- "w" stab) 203 | (modify-syntax-entry ?* "w" stab) 204 | (modify-syntax-entry ?/ "w" stab) 205 | (modify-syntax-entry ?% "w" stab) 206 | (modify-syntax-entry ?= "w" stab) 207 | (modify-syntax-entry ?~ "w" stab) 208 | (modify-syntax-entry ?& "w" stab) 209 | (modify-syntax-entry ?| "w" stab) 210 | (modify-syntax-entry ?^ "w" stab) 211 | (modify-syntax-entry ?! "w" stab) 212 | (modify-syntax-entry ?? "w" stab) 213 | (modify-syntax-entry ?@ "w" stab) 214 | (modify-syntax-entry ?© "w" stab) 215 | (modify-syntax-entry ?` "w" stab) 216 | 217 | ;; define what comments look like 218 | ;; (modify-syntax-entry ?( "< n" stab) 219 | ;; (modify-syntax-entry ?) "> n" stab) 220 | ;; (modify-syntax-entry ?/ "< 12" stab) 221 | ;; (modify-syntax-entry ?\n "> " stab) 222 | (setq comment-start "//") 223 | 224 | stab) 225 | "Syntax Table for :Firth Mode") 226 | 227 | ;;; Indentation ;;; 228 | 229 | ;; TODO 230 | 231 | ;;; :Firth Mode Init and Entry ;;; 232 | 233 | ; let users hook loading firth mode 234 | (defvar firth-mode-hook nil) 235 | 236 | (defun firth-mode () 237 | "Major Mode for Editing :Firth Files." 238 | (interactive) 239 | (kill-all-local-variables) 240 | (set-syntax-table firth-mode-syntax-table) 241 | (use-local-map firth-mode-map) 242 | (set (make-local-variable 'font-lock-defaults) '(firth-font-lock-keywords-pattern)) 243 | ; (set (make-local-variable 'indent-line-function) '(firth-indent-line)) 244 | (setq major-mode 'firth-mode) 245 | (setq mode-name ":Firth") 246 | (run-hooks 'firth-mode-hook)) 247 | 248 | (provide 'firth-mode) 249 | -------------------------------------------------------------------------------- /examples/iniloader.firth: -------------------------------------------------------------------------------- 1 | // iniloader.firth 2 | // :Firth DSL creation example 3 | // 4 | // Copyright © 2015 Brigham Toskin 5 | // This file is part of the Firth language reference implementation. Usage 6 | // and redistribution of this software is governed by the terms of a modified 7 | // MIT-style license. You should have received a copy of the license with the 8 | // source distribution in the file LICENSE; if not, you may find it online at: 9 | // 10 | // 11 | // Formatting: 12 | // utf-8 ; unix ; 80 cols ; tabwidth 4 13 | // ///////////////////////////////////////////////////////////////////////////// 14 | 15 | 16 | false val: INIDONE 17 | : = ( var -- ) ( TS: val ) word swap ! ; 18 | : inidef ( -- ) ( TS: name = val ) 19 | word dup char } equal? if 20 | true ` INIDONE ! drop 21 | else 22 | variable word call 23 | end ; 24 | : { ( -- ) true while inidef INIDONE not end ; 25 | : inifile: ( -- ) ( TS: path ) 26 | false ` INIDONE ! 27 | word loadfile ; -------------------------------------------------------------------------------- /examples/test.ini: -------------------------------------------------------------------------------- 1 | // test.ini 2 | // :Firth DSL creation example 3 | 4 | { 5 | FOO = BAR 6 | BAZ = 123 7 | BLERG = 127.0.0.1:8080 8 | } -------------------------------------------------------------------------------- /firth.lua: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | --! @file firth.lua 3 | --! @brief Minimal frontend for Firth language. 4 | --! @author btoskin - 5 | --! @copyright © 2015 Brigham Toskin 6 | --! 7 | --!

This file is part of the Firth language reference implementation. Usage 8 | --! and redistribution of this software is governed by the terms of a modified 9 | --! MIT-style license. You should have received a copy of the license with the 10 | --! source distribution in the file LICENSE; if not, you may find it online at 11 | --!

12 | --! 13 | --! @see compiler.lua 14 | --! @see stringio.lua 15 | -- 16 | -- Formatting: 17 | -- utf-8 ; unix ; 80 cols ; tabwidth 4 18 | -------------------------------------------------------------------------------- 19 | 20 | 21 | --! @cond 22 | local stringio = require "firth.stringio" 23 | local compiler = require "firth.compiler" 24 | --! @endcond 25 | 26 | 27 | --! The Firth repl and such. 28 | --! This is rather shoddy code, atm. 29 | local firth 30 | firth = { 31 | instance = function() 32 | if not firth.c then 33 | firth.c = compiler.new() 34 | else 35 | firth.c.running = true 36 | end 37 | return firth.c 38 | end, 39 | 40 | doline = function(c, line, linenum) 41 | local success, msg = pcall(c.interpret, c, line, linenum) 42 | if not success then stringio.printline(msg) end 43 | return success 44 | end, 45 | 46 | --! @fn repl() 47 | --! @brief Executes the Firth Read-Eval-Print Loop. 48 | repl = function() 49 | local c = firth.instance() 50 | c.path = "REPL::INIT" 51 | firth.doline(c, 'copyright CR', 0) 52 | c.path = "stdin" 53 | local linenum 54 | while c.running do 55 | if c.compiling then 56 | linenum = linenum + 1 57 | stringio.print('>>\t') 58 | else 59 | linenum = 1 60 | stringio.print('ok ') 61 | end 62 | firth.doline(c, stringio.readline(), linenum) 63 | end 64 | stringio.printline() 65 | end, 66 | 67 | --! @fn cli() 68 | --! @brief runs each argument as a line of firth code. 69 | cli = function(args) 70 | local c = firth.instance() 71 | for i, code in ipairs(args) do 72 | c.path = "Argument"..i 73 | if not firth.doline(c, code, i) then 74 | break 75 | end 76 | end 77 | if c.stack.height > 0 then c:interpret(".S") end 78 | c.path = "stdin" 79 | end, 80 | } 81 | 82 | local args = {...} 83 | if #args > 0 then 84 | firth.cli(args) 85 | else 86 | args = nil 87 | firth.repl() 88 | end 89 | -------------------------------------------------------------------------------- /firth/compiler+prims.lua: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | --! @file compiler-prims.lua 3 | --! @brief Language primitives for Firth language. 4 | --! @author btoskin - 5 | --! @copyright © 2015 Brigham Toskin 6 | --! 7 | --!

This file is part of the Firth language reference implementation. Usage 8 | --! and redistribution of this software is governed by the terms of a modified 9 | --! MIT-style license. You should have received a copy of the license with the 10 | --! source distribution in the file LICENSE; if not, you may find it online at 11 | --!

12 | --! 13 | --! @see compiler.lua 14 | --! @see stack.lua 15 | --! @see stringio.lua 16 | -- 17 | -- Formatting: 18 | -- utf-8 ; unix ; 80 cols ; tabwidth 4 19 | -------------------------------------------------------------------------------- 20 | 21 | 22 | --! @cond 23 | local os = require "os" 24 | local clock = os.clock 25 | local string = require "string" 26 | 27 | local stringio = require "firth.stringio" 28 | local stack_new = (require "firth.stack").new 29 | 30 | local exports = {} 31 | 32 | local function buildentries(dict, map) 33 | for name, entry in pairs(dict) do 34 | local func = entry.func 35 | assert(type(func) == "function", "INIT ERROR: "..name) 36 | 37 | map[func] = name 38 | entry.name = name 39 | entry.calls = {} 40 | entry.calledby = {} 41 | end 42 | end 43 | --! @endcond 44 | 45 | function exports.initialize(compiler) 46 | assert(type(compiler) == "table", "INVALID COMPILER REF") 47 | local dictionary, funcmap = compiler.dictionary, compiler.funcmap 48 | local stack, cstack = compiler.stack, compiler.cstack 49 | 50 | -- stack ops -- 51 | 52 | local function dup() 53 | stack:dup() 54 | end 55 | 56 | local function over() 57 | stack:over() 58 | end 59 | 60 | local function drop() 61 | stack:drop() 62 | end 63 | 64 | local function clear() 65 | stack:clear() 66 | end 67 | 68 | local function swap() 69 | stack:swap() 70 | end 71 | 72 | local function rot() 73 | stack:rot() 74 | end 75 | 76 | local function revrot() 77 | stack:revrot() 78 | end 79 | 80 | local function pick() 81 | return stack:pick(stack:pop()) 82 | end 83 | 84 | local function c_from() 85 | stack:push(cstack:pop()) 86 | end 87 | 88 | local function to_c() 89 | cstack:push(stack:pop()) 90 | end 91 | 92 | local function c_fetch() 93 | stack:push(cstack:top()) 94 | end 95 | 96 | local function two_c_from() 97 | compiler:assert(cstack.height >= 2, "2C>", "CSTACK UNDERFLOW") 98 | local height = stack.height 99 | stack[height + 2], stack[height + 1] = cstack:pop(), cstack:pop() 100 | stack.height = height + 2 101 | end 102 | 103 | local function two_to_c() 104 | compiler:assert(stack.height >= 2, "2>C", "UNDERFLOW") 105 | local height = cstack.height 106 | cstack[height + 2], cstack[height + 1] = stack:pop(), stack:pop() 107 | cstack.height = height + 2 108 | end 109 | 110 | local function two_c_fetch() 111 | local height, cheight = stack.height, cstack.height 112 | compiler:assert(cheight >= 2, "2C@", "CSTACK UNDERFLOW") 113 | stack[height + 2], stack[height + 1] = cstack[cheight], cstack[cheight - 1] 114 | stack.height = height + 2 115 | end 116 | 117 | --! ( s x -- s ) ( s: -- x ) 118 | local function to_s() 119 | local val = stack:pop() 120 | local s = stack:top() 121 | s:push(val) 122 | end 123 | 124 | --! ( s -- s x ) ( s: x -- ) 125 | local function s_from() 126 | local s = stack:top() 127 | local val = s:pop() 128 | stack:push(val) 129 | end 130 | 131 | --! ( s -- s x ) ( s: x -- x ) 132 | local function s_fetch() 133 | local s = stack:top() 134 | local val = s:top() 135 | stack:push(val) 136 | end 137 | 138 | --! ( s x1 x2 -- s ) ( s: -- x1 x2 ) 139 | local function two_to_s() 140 | local x2, x1 = stack:pop(), stack:pop() 141 | local s = stack:top() 142 | local height = s.height 143 | s[height + 1], s[height + 2] = x1, x2 144 | s.height = height + 2 145 | end 146 | 147 | --! ( s -- s x1 x2 ) ( s: x1 x2 -- ) 148 | local function two_s_from() 149 | local s = stack:top() 150 | local height = stack.height 151 | local x2, x1 = s:pop(), s:pop() 152 | stack[height + 1], stack[height + 2] = x1, x2 153 | stack.height = height + 2 154 | end 155 | 156 | --! ( s -- s x1 x2 ) ( s: x1 x2 -- x1 x2 ) 157 | local function two_s_fetch() 158 | local s = stack:top() 159 | local height, sheight = stack.height, s.height 160 | compiler:assert(sheight >= 2, "2[]@", "UNDERFLOW") 161 | local x2, x1 = s[sheight], s[sheight - 1] 162 | stack[height + 1], stack[height + 2] = x1, x2 163 | stack.height = height + 2 164 | end 165 | 166 | -- flow control -- 167 | 168 | -- this is not meant to be a prim word available to firth code. 169 | -- is there a reasonable usecase for doing so? 170 | local function beginblock() 171 | local compiling = compiler.compiling 172 | if not compiling then 173 | compiler:create() 174 | compiler.compiling = true 175 | end 176 | cstack:push(compiling) 177 | end 178 | 179 | local function ifstmt() 180 | beginblock() 181 | local cond = compiler:poptmp() 182 | compiler:append("if %s then", cond) 183 | end 184 | 185 | local function elsestmt() 186 | compiler:append("else") 187 | end 188 | 189 | local function forstmt() 190 | beginblock() 191 | local i = compiler:newtmp() 192 | local last, first = compiler:poptmp(), compiler:poptmp() 193 | compiler:append("for %s = %s, %s do", i, first, last) 194 | compiler:push(i) 195 | end 196 | 197 | --! ( container -- itr, st, first ) 198 | local function iterator() 199 | local container = stack:pop() 200 | if type(container) ~= "table" then 201 | compiler:runtimeerror("(loop iterator)", "TARGET NOT ITERABLE") 202 | end 203 | local mt = getmetatable(container) 204 | if mt and mt.__itr then 205 | stack:pushv(container:__itr()) 206 | else 207 | stack:pushv(pairs(container)) 208 | stack:push(nil) 209 | end 210 | end 211 | 212 | --! ( container -- ) 213 | local function foreachstmt() 214 | beginblock() 215 | compiler:call("iterator") 216 | local first, st, itr = compiler:poptmp(), compiler:poptmp(), compiler:poptmp() 217 | local key, value = compiler:newtmp(), compiler:newtmp() 218 | compiler:append("for %s, %s in %s, %s, %s do", 219 | key, value, itr, st, first) 220 | compiler:push(key) 221 | compiler:push(value) 222 | end 223 | 224 | --! ( b -- ) 225 | local function whilestmt() 226 | beginblock() 227 | compiler:append("while(stack:pop()) do") 228 | end 229 | 230 | local function dostmt() 231 | beginblock() 232 | compiler:append("do") 233 | end 234 | 235 | local function endstmt() 236 | compiler:append("end") 237 | local compiling = cstack:pop() 238 | compiler.compiling = compiling 239 | if not compiling then 240 | compiler:buildfunc() 241 | compiler:execfunc() 242 | end 243 | end 244 | 245 | -- inline operations -- 246 | 247 | --! ( operator -- ) 248 | local function binop() 249 | local op = stack:pop() 250 | 251 | local roperand = compiler:poptmp() 252 | local loperand = compiler:toptmp() 253 | local lval = compiler:newtmp(string.format("%s %s %s", loperand, op, roperand)) 254 | compiler:append("rawset(stack, stack.height, %s)", lval) 255 | end 256 | 257 | local function binopconst() 258 | local op = stack:pop() 259 | local cval = stack:pop() 260 | 261 | local val = compiler:toptmp() 262 | local newval = compiler:newtmp(string.format("%s %s %s", val, op, cval)) 263 | compiler:append("rawset(stack, stack.height, %s)", newval) 264 | end 265 | 266 | -- boolean stuff -- 267 | 268 | local function pushtrue() 269 | compiler:push(true) 270 | end 271 | 272 | local function pushfalse() 273 | compiler:push(false) 274 | end 275 | 276 | local function pushnot() 277 | if compiler.compiling then 278 | compiler:append("stack[stack.height] = not stack:top()") 279 | else 280 | stack[stack.height] = not stack:top() 281 | end 282 | end 283 | 284 | local function push2not() 285 | local height = compiler:newtmp("stack.height") 286 | compiler:cassert(height.." > 1", "UNDERFLOW") 287 | local prev = compiler:newtmp(height.." - 1") 288 | compiler:append("stack[%s], stack[%s] = not stack[%s], not stack[%s]", 289 | height, prev, height, prev) 290 | end 291 | 292 | local function pushnil() 293 | compiler:push(nil) 294 | end 295 | 296 | local function pushtable() 297 | compiler:push({}) 298 | end 299 | 300 | local function pushstack() 301 | compiler:push(stack_new()) 302 | end 303 | 304 | local function pushtype() 305 | local typ 306 | if compiler.compiling then 307 | local val = compiler:poptmp() 308 | typ = compiler:newtmpfmt("type(%s)", val) 309 | else 310 | typ = type(stack:pop()) 311 | end 312 | compiler:push(typ) 313 | end 314 | 315 | -- bitwise boolean ops -- 316 | 317 | -- only available in LuaJIT 2.0+ or Lua 5.2+ 318 | local bit = bit or bit32 319 | local band, bor, bxor, bnot 320 | if bit then 321 | local bitand, bitor, bitxor, bitnot = bit.band, bit.bor, bit.bxor, bit.bnot 322 | 323 | function band() 324 | stack:push(bitand(stack:pop(), stack:pop())) 325 | end 326 | 327 | function bor() 328 | stack:push(bitor(stack:pop(), stack:pop())) 329 | end 330 | 331 | function bxor() 332 | stack:push(bitxor(stack:pop(), stack:pop())) 333 | end 334 | 335 | function bnot() 336 | stack:push(bitnot(stack:pop())) 337 | end 338 | end 339 | 340 | -- os and io primitives -- 341 | 342 | local function rawprint() 343 | local tos = stack:pop() 344 | compiler:assert(type(tos) == "string", ".raw", "NOT A STRING") 345 | stringio.print(tos) 346 | end 347 | 348 | local function dotprint() 349 | -- have to actually pop, to avoid cycles 350 | stack:push(tostring(stack:pop())..' ') 351 | rawprint() 352 | end 353 | 354 | local function dotprinthex() 355 | local tos = stack:top() 356 | stack[stack.height] = string.format("0x%X", tonumber(tos)) 357 | dotprint() 358 | end 359 | 360 | local function dotprints() 361 | -- stringio.print(tostring(stack), ' ') 362 | stack:push(stack) 363 | dotprint() 364 | end 365 | 366 | local function dotprintc() 367 | -- stringio.print(tostring(cstack), ' ') 368 | stack:push(cstack) 369 | dotprint() 370 | end 371 | 372 | -- parser/compiler control words -- 373 | 374 | local function loadfile() 375 | compiler:loadfile(stack:pop()) 376 | end 377 | 378 | local function exit() 379 | compiler.running = false 380 | end 381 | 382 | --! ( -- ) 383 | local function setcompiling() 384 | compiler.compiling = true 385 | end 386 | 387 | --! ( name -- entry ) 388 | local function create() 389 | compiler:create(stack:pop()) 390 | end 391 | 392 | --! ( entry -- ) 393 | local function alias() 394 | local entry = stack:pop() 395 | local newname = compiler:nexttoken() 396 | dictionary[newname] = entry 397 | end 398 | 399 | --! ( entry -- C: oldentry? ) 400 | local function settarget() 401 | compiler:settarget(stack:pop()) 402 | end 403 | 404 | local function interpret() 405 | if compiler.compiling then 406 | compiler.compiling = false 407 | end 408 | end 409 | 410 | --! ( -- bool ) 411 | local function compiling() 412 | stack:push(compiler.compiling) 413 | end 414 | 415 | --! ( c -- word ) Parses next token from input stream, and pushes it as a string. 416 | local function parse() 417 | local delim = stack:pop() 418 | local success, token = pcall(compiler.nexttoken, compiler, delim) 419 | compiler:assert(success, "parse", "UNABLE TO RETRIEVE TOKEN") 420 | stack:push(token) 421 | end 422 | 423 | --! ( pattern -- tok ) 424 | local function parsematch() 425 | local pattern = stack:pop() 426 | local success, token, src = pcall(stringio.matchtoken, compiler.src, pattern) 427 | compiler:assert(success, "parsematch", "UNABLE TO RETRIEVE TOKEN") 428 | stack:push(token) 429 | compiler.src = src 430 | end 431 | 432 | --! ( str -- ) 433 | local function ungettoken() 434 | compiler.src = tostring(stack:pop())..' '..compiler.src 435 | end 436 | 437 | --! ( C: entry -- C: entry' ) 438 | local function push() 439 | if not compiler.compiling then return end 440 | 441 | local val = stack:pop() 442 | 443 | -- compiler:push() can't tell if its arg is a literal string from a 444 | -- firth word, or meant to be a compiled expression from a codegen 445 | -- function. so, we need to determine that here and choose the right 446 | -- version of push. 447 | if type(val) == "string" then 448 | compiler:pushstring(val) 449 | else 450 | compiler:push(val) 451 | end 452 | end 453 | 454 | --! ( C: entry -- C: entry' ) 455 | local function buildfunc() 456 | compiler:buildfunc() 457 | end 458 | 459 | --! ( C: entry -- ) 460 | local function bindfunc() 461 | compiler:bindfunc() 462 | end 463 | 464 | --! ( entry -- ) 465 | local function does() 466 | local varentry = compiler:toptmp() 467 | local doesname = compiler:newtmpstr(compiler:nexttoken()) 468 | local doesentry = compiler:newtmpfmt("compiler:lookup(%s)", doesname) 469 | local doesfunc = compiler:newtmpfmt("%s.func", doesentry) 470 | local closure = [[ 471 | varentry.func = function() 472 | stack:push(varentry) 473 | doesfunc() 474 | end]] 475 | closure = closure:gsub("varentry", varentry):gsub("doesfunc", doesfunc) 476 | compiler:append(closure) 477 | compiler:call("settarget") 478 | compiler:append("compiler:bindfunc()") 479 | compiler:append("%s.compilebuf = {%q}", varentry, closure) 480 | end 481 | 482 | --! ( name -- entry ) 483 | local function dict() 484 | local word = stack:pop() 485 | local entry = compiler:lookup(word) 486 | stack:push(entry) 487 | end 488 | 489 | --! ( -- entry ) 490 | local function last() 491 | stack:push(compiler.last) 492 | end 493 | 494 | --! ( -- b ) TS: word 495 | local function defined() 496 | local name = stack:pop() 497 | compiler:push(dictionary[name] ~= nil) 498 | end 499 | 500 | --! @@ ( t k -- t x ) 501 | local function fetchfield() 502 | local idx = stack:pop() 503 | local tab = stack:top() 504 | stack:push(tab[idx]) 505 | end 506 | 507 | --! !! ( x t k -- t ) 508 | local function storefield() 509 | local idx = stack:pop() 510 | local tab = stack:pop() 511 | local val = stack:pop() 512 | tab[idx] = val 513 | stack:push(tab) 514 | end 515 | 516 | --! ( -- ) 517 | local function immediate() 518 | compiler:immediate() 519 | end 520 | 521 | --! ( -- c ) 522 | local function char() 523 | local str = compiler:nexttoken() 524 | local char = str:sub(1, 1) 525 | if char == "%" or char == "\\" then char = str:sub(1, 2) end 526 | compiler:pushstring(char) 527 | end 528 | 529 | --! ( name -- ) 530 | local function call() 531 | local name = stack:pop() 532 | if compiler.compiling then 533 | compiler:call(name) 534 | else 535 | local entry = compiler:lookup(name) 536 | entry.func() 537 | end 538 | end 539 | 540 | --! ( xt -- i*x ) 541 | local function execute() 542 | local xt = stack:pop() 543 | xt() 544 | end 545 | 546 | -- reflection, debugging, internal compiler state, etc. -- 547 | 548 | local function dumpword() 549 | local word = compiler:nexttoken() 550 | local entry = compiler:lookup(word) 551 | stack:push(table.concat(entry.compilebuf, '\n')) 552 | end 553 | 554 | local function dump() 555 | stack:push(table.concat(compiler.last.compilebuf, '\n')) 556 | end 557 | 558 | local function trace() 559 | compiler.tracing = true 560 | end 561 | 562 | local function notrace() 563 | compiler.tracing = false 564 | end 565 | 566 | local function tracing() 567 | stack:push(compiler.tracing) 568 | end 569 | 570 | local function path() 571 | stack:push(compiler.path) 572 | end 573 | 574 | local function tstart() 575 | stack:push(clock()) 576 | end 577 | 578 | local function tend() 579 | local fin = clock() 580 | local diff = fin - stack:pop() 581 | stringio.printline('(', diff, " seconds)") 582 | end 583 | 584 | -- load up the dictionary -- 585 | 586 | dictionary.dup = { func = dup } 587 | dictionary.over = { func = over } 588 | dictionary.drop = { func = drop } 589 | dictionary.clear = { func = clear } 590 | dictionary.swap = { func = swap } 591 | dictionary.rot = { func = rot } 592 | dictionary['-rot'] = { func = revrot } 593 | dictionary.pick = { func = pick } 594 | dictionary['C>'] = { func = c_from } 595 | dictionary['>C'] = { func = to_c } 596 | dictionary['C@'] = { func = c_fetch } 597 | dictionary['2C>'] = { func = two_c_from } 598 | dictionary['2>C'] = { func = two_to_c } 599 | dictionary['2C@'] = { func = two_c_fetch } 600 | dictionary['>[]'] = { func = to_s } 601 | dictionary['[]>'] = { func = s_from } 602 | dictionary['[]@'] = { func = s_fetch } 603 | dictionary['2>[]'] = { func = two_to_s } 604 | dictionary['[2]>'] = { func = two_s_from } 605 | dictionary['[2]@'] = { func = two_s_fetch } 606 | 607 | dictionary['if'] = { func = ifstmt, immediate = true } 608 | dictionary['else'] = { func = elsestmt, immediate = true } 609 | dictionary['for'] = { func = forstmt, immediate = true } 610 | dictionary['iterator'] = { func = iterator, immediate = true } 611 | dictionary['foreach'] = { func = foreachstmt, immediate = true } 612 | dictionary['while'] = { func = whilestmt, immediate = true } 613 | dictionary['do'] = { func = dostmt, immediate = true } 614 | dictionary['end'] = { func = endstmt, immediate = true } 615 | 616 | dictionary.binop = { func = binop } 617 | dictionary.binopconst = { func = binopconst } 618 | 619 | dictionary['true'] = { func = pushtrue, immediate = true } 620 | dictionary['false'] = { func = pushfalse, immediate = true } 621 | dictionary['not'] = { func = pushnot, immediate = true } 622 | dictionary['2not'] = { func = push2not, immediate = true } 623 | dictionary['nil'] = { func = pushnil, immediate = true } 624 | dictionary['{}'] = { func = pushtable, immediate = true } 625 | dictionary['[]'] = { func = pushstack, immediate = true } 626 | dictionary['type'] = { func = pushtype, immediate = true } 627 | 628 | dictionary['.raw'] = { func = rawprint } 629 | dictionary['.'] = { func = dotprint } 630 | dictionary['.x'] = { func = dotprinthex } 631 | dictionary['.S'] = { func = dotprints } 632 | dictionary['.C'] = { func = dotprintc } 633 | 634 | dictionary.loadfile = { func = loadfile } 635 | dictionary.exit = { func = exit, immediate = true } 636 | dictionary.setcompiling = { func = setcompiling } 637 | dictionary.settarget = { func = settarget } 638 | dictionary.interpret = { func = interpret, immediate = true } 639 | dictionary['compiling?'] = { func = compiling } 640 | dictionary.parse = { func = parse } 641 | dictionary.parsematch = { func = parsematch } 642 | dictionary['>ts'] = { func = ungettoken } 643 | dictionary.push = { func = push } 644 | dictionary.create = { func = create } 645 | dictionary['alias:'] = { func = alias, immediate = true } 646 | dictionary.buildfunc = { func = buildfunc } 647 | dictionary.bindfunc = { func = bindfunc } 648 | dictionary['does>'] = { func = does, immediate = true } 649 | dictionary.dict = { func = dict } 650 | dictionary.last = { func = last } 651 | dictionary["defined?"] = { func = defined, immediate = true } 652 | dictionary['@@'] = { func = fetchfield } 653 | dictionary['!!'] = { func = storefield } 654 | dictionary.immediate = { func = immediate } 655 | dictionary.char = { func = char, immediate = true } 656 | dictionary.call = { func = call } 657 | dictionary.execute = { func = execute } 658 | 659 | dictionary.dump = { func = dump } 660 | dictionary['dumpword:'] = { func = dumpword, immediate = true } 661 | dictionary.trace = { func = trace } 662 | dictionary.notrace = { func = notrace } 663 | dictionary['tracing?'] = { func = tracing } 664 | dictionary.path = { func = path } 665 | -- dictionary['calls:'] = { func = calls, immediate = true } 666 | -- dictionary['calledby:'] = { func = calledby, immediate = true } 667 | dictionary.tstart = { func = tstart } 668 | dictionary.tend = { func = tend } 669 | 670 | if bit then 671 | dictionary["&"] = { func = band } 672 | dictionary["|"] = { func = bor } 673 | dictionary["^"] = { func = bxor } 674 | dictionary["~"] = { func = bnot } 675 | end 676 | 677 | dictionary.NOP = { func = compiler.NOP } 678 | 679 | buildentries(dictionary, funcmap) 680 | end 681 | 682 | 683 | return exports 684 | -------------------------------------------------------------------------------- /firth/compiler.lua: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | --! @file compiler.lua 3 | --! @brief Source compiler for Firth language. 4 | --! @author btoskin - 5 | --! @copyright © 2015 Brigham Toskin 6 | --! 7 | --!

This file is part of the Firth language reference implementation. Usage 8 | --! and redistribution of this software is governed by the terms of a modified 9 | --! MIT-style license. You should have received a copy of the license with the 10 | --! source distribution in the file LICENSE; if not, you may find it online at 11 | --!

12 | --! 13 | --! @see stack.lua 14 | --! @see stringio.lua 15 | -- 16 | -- Formatting: 17 | -- utf-8 ; unix ; 80 cols ; tabwidth 4 18 | -------------------------------------------------------------------------------- 19 | 20 | 21 | --! @cond 22 | local stringio = require "firth.stringio" 23 | local stack = require "firth.stack" 24 | local prims = require "firth.compiler+prims" 25 | 26 | local compiler = { 27 | __FIRTH_INTERNAL__ = "" -- used for stacktraces 28 | } 29 | --! @endcond 30 | 31 | 32 | function compiler:nexttoken(delim) 33 | local token, rest = stringio.nexttoken(self.src, delim) 34 | self.src = rest 35 | return token 36 | end 37 | 38 | function compiler:interpret(src, num) 39 | self.src = src 40 | self.linenum = num or 1 41 | self.running = type(src) == "string" 42 | 43 | -- interpret/compile each token in src 44 | while self.src and #self.src > 0 and self.running do 45 | local tok = self:nexttoken() 46 | -- print("TOKEN "..tostring(tok)) 47 | -- try dictionary lookup 48 | local val = self.dictionary[tok] 49 | if val then 50 | xpcall(self.execword, self.xperrhandler, self, val) 51 | else 52 | -- try to parse it as a number 53 | val = stringio.tonumber(tok) 54 | if val then 55 | self:push(val) 56 | else 57 | -- error; use xpcall, so we get the stacktrace 58 | xpcall(self.lookuperror, self.xperrhandler, self, tok, num) 59 | end 60 | end 61 | end 62 | end 63 | 64 | function compiler:loadfile(path) 65 | -- TODO: default/search paths 66 | local cstack = self.cstack 67 | local num = 1 68 | self.running = true 69 | 70 | cstack:push(stringio.input()) 71 | cstack:push(self.path) 72 | cstack:push(self.linenum) 73 | 74 | self.path = path 75 | stringio.input(path) 76 | local src = stringio.read() 77 | self:interpret(src, num) 78 | 79 | if self.compiling then self:runtimeerror("loadfile", "UNEXPECTED EOF") end 80 | 81 | if self.running and self.cstack.height > 0 then 82 | self.linenum = cstack:pop() 83 | self.path = cstack:pop() 84 | stringio.input(cstack:pop()) 85 | end 86 | end 87 | 88 | function compiler:lookup(name) 89 | local entry = self.dictionary[name] 90 | if not entry then self:lookuperror(name) end 91 | return entry 92 | end 93 | 94 | function compiler:execword(entry) 95 | -- TODO: compile mode-specific vocabulary? 96 | local name = entry.name 97 | if (not self.compiling) or entry.immediate then 98 | self:execfunc(entry) 99 | else 100 | self:call(name) 101 | end 102 | end 103 | 104 | --! @private 105 | local function newcompilebuf() 106 | return { [[ 107 | local __FIRTH_WORD_NAME__ = %q 108 | local compiler = ... 109 | local dictionary, stack, cstack = compiler.dictionary, compiler.stack, compiler.cstack 110 | %s 111 | %s 112 | return function() ]] } 113 | end 114 | 115 | function compiler:settarget(newtarget) 116 | local typ = type(newtarget) 117 | assert(typ == "table", "INVALID TARGET TYPE "..typ) 118 | assert(newtarget.compilebuf, "MISSING COMPILEBUF SETTING TARGET "..newtarget.name) 119 | 120 | -- print("COMPILING: "..newtarget.name) 121 | if self.target then self.cstack:push(self.target) end 122 | self.target = newtarget 123 | end 124 | 125 | function compiler:restoretarget() 126 | local cstack = self.cstack 127 | if cstack.height > 0 then 128 | local tos = cstack:top() 129 | if type(tos) == "table" and tos.compilebuf then 130 | self.target = tos 131 | cstack:drop() 132 | return 133 | end 134 | end 135 | self.target = nil 136 | end 137 | 138 | --! @private 139 | local entrymt = { 140 | __tostring = function(t) 141 | return string.format("{%q}", t.name) 142 | end 143 | } 144 | entrymt.__index = entrymt 145 | function compiler:create(name) 146 | local dopush = (name ~= nil) 147 | if not name then 148 | if self.target and self.target.name == "[INTERP_BUF]" then 149 | return 150 | else 151 | name = "[INTERP_BUF]" 152 | end 153 | end 154 | 155 | local entry = { 156 | name = name, compilebuf = newcompilebuf(), 157 | calls = { nextidx = 1 }, calledby = {}, upvals = { nextidx = 1 } 158 | } 159 | setmetatable(entry, entrymt) 160 | if dopush then 161 | self.stack:push(entry) 162 | else 163 | self:settarget(entry) 164 | end 165 | end 166 | 167 | function compiler:call(word) 168 | -- print("CALL TO "..word) 169 | local dictionary = self.dictionary 170 | local target = self.target 171 | local calls = target.calls 172 | local callee = self:lookup(word) 173 | 174 | local mappedname = calls[word] 175 | if not mappedname then 176 | local index = calls.nextidx 177 | calls.nextidx = index + 1 178 | mappedname = "__f"..tostring(index).."__" 179 | calls[word] = mappedname 180 | if self.compiling then 181 | callee.calledby[target.name] = true 182 | end 183 | end 184 | self:append("%s()", mappedname) 185 | end 186 | 187 | --! @private 188 | function compiler.NOP() end 189 | 190 | --! @private 191 | local function cachedcalls(calls) 192 | calls.nextidx = nil 193 | local callbuf = {} 194 | for word,tmp in pairs(calls) do 195 | callbuf[#callbuf + 1] = string.format("local %s = dictionary[%q].func", tmp, word) 196 | end 197 | return table.concat(callbuf, '\n') 198 | end 199 | 200 | local function upvalues(upvals) 201 | upvals.nextidx = nil 202 | local buf = { "local upvals = compiler.target.upvals" } 203 | for tmp,val in pairs(upvals) do 204 | buf[#buf + 1] = string.format("local %s = upvals[%q]", tmp, tmp) 205 | end 206 | local compiledupvals = (#buf > 1) and table.concat(buf, '\n') or "" 207 | return compiledupvals 208 | end 209 | 210 | function compiler:buildfunc() 211 | self.nexttmp = 0 -- TODO: cstack? 212 | 213 | local target = self.target 214 | local name, compilebuf = target.name, target.compilebuf 215 | local buflen = #compilebuf 216 | 217 | -- don't bother compiling empty buffers (i.e. blank lines) 218 | if buflen == 1 then 219 | -- print("DUMPING "..name..".compilebuf") 220 | target.func = self.NOP 221 | target.compilebuf = nil 222 | return 223 | end 224 | 225 | compilebuf[1] = compilebuf[1]:format(name, 226 | cachedcalls(target.calls), 227 | upvalues(target.upvals)) 228 | compilebuf[buflen + 1] = "end" 229 | local luasrc = table.concat(compilebuf, '\n') 230 | local func, err = loadstring(luasrc, "__FIRTH_WORD__ "..name) 231 | if func then 232 | -- exec loaded function to get the actual function we want 233 | local success, res = pcall(func, self) 234 | if success then 235 | target.func = res 236 | return 237 | else 238 | stringio.printline("Compile Error (buildfunc):") 239 | stringio.printline(res) 240 | stringio.printline(luasrc) 241 | end 242 | else 243 | stringio.printline("Compile Error (loadstring):") 244 | stringio.printline(err) 245 | stringio.printline(luasrc) 246 | end 247 | 248 | -- didn't hit the success path; clean up target 249 | target.func, target.calls, target.calledby = nil 250 | end 251 | 252 | function compiler:bindfunc() 253 | local target = self.target 254 | self:restoretarget() 255 | local name, func = target.name, target.func 256 | if func then 257 | -- print("ADDING "..name.." TO DICTIONARY") 258 | self.dictionary[name] = target 259 | self.funcmap[func] = name 260 | self.last = target 261 | else 262 | self:runtimeerror("bindfunc", "NIL FUNCTION REF for "..tostring(name)) 263 | end 264 | self:restoretarget() 265 | end 266 | 267 | function compiler:execfunc(entry) 268 | local usecompiletarget = not entry 269 | if usecompiletarget then 270 | entry = self.target 271 | self:restoretarget() 272 | end 273 | 274 | local name = entry.name 275 | local func = entry.func 276 | if func then 277 | if usecompiletarget then 278 | self:restoretarget() 279 | end 280 | -- print("EXECUTING "..name) 281 | xpcall(func, self.xperrhandler, self) 282 | elseif name ~= "[INTERP_BUF]" then -- func == nil, but we have a name for an expected func 283 | self:runtimeerror("execfunc", "NIL FUNCTION REF for "..tostring(name)) 284 | else 285 | -- print("SKIPPING EMPTY [INTERP_BUF]") 286 | end 287 | end 288 | 289 | local function sortmatches(buckets, tok) 290 | local result = {} 291 | 292 | for i, matches in ipairs(buckets) do 293 | local idx = i + 1 294 | local ch = tok:sub(idx, idx) 295 | table.sort(matches, function(a, b) 296 | return (a:sub(idx, idx) == ch) and (b:sub(idx, idx) ~= ch) 297 | end) 298 | end 299 | 300 | for _, matches in ipairs(buckets) do 301 | for _, match in ipairs(matches) do 302 | table.insert(result, match) 303 | end 304 | end 305 | 306 | if #result == 0 then result[1] = "(NO SUGGESTIONS FOUND)" end 307 | return result 308 | end 309 | 310 | function compiler:clearcompilestate() 311 | self.linenum = nil 312 | self.src = nil 313 | self.compiling = false 314 | self.target = nil 315 | self.path = "stdin" 316 | stringio.input(stringio.stdin()) 317 | self.cstack:clear() 318 | end 319 | 320 | function compiler:lookuperror(tok, num) 321 | local __FIRTH_DUMPTRACE__ = true 322 | 323 | num = num or self.linenum 324 | local prefix = self.path 325 | if num then prefix = prefix..':'..num end 326 | local buckets = {} 327 | for k,v in pairs(self.dictionary) do 328 | for i = 1, #tok do 329 | buckets[i] = buckets[i] or {} 330 | if i > #k then break end 331 | if tok:sub(i,i) == k:sub(i,i) then 332 | table.insert(buckets[i], k..(v.immediate and " (immediate)" or "")) 333 | break 334 | end 335 | end 336 | end 337 | local suffix = "Did You Mean..?\n\t"..table.concat(sortmatches(buckets, tok), "\n\t") 338 | self:runtimeerror(prefix, "UNKNOWN WORD '"..tok.."'\n"..suffix, 3) 339 | end 340 | 341 | function compiler:runtimeerror(name, msg, level) 342 | local __FIRTH_DUMPTRACE__ = true 343 | 344 | self:clearcompilestate() 345 | error("in "..name..': '..msg, level or 2) 346 | end 347 | 348 | -- returns a callstack frame iterator 349 | local function frames(start) 350 | local i = start or 0 351 | return function() 352 | local frame = debug.getinfo(i, "Snf") -- source (file), name, function ref 353 | i = i + 1 354 | return frame 355 | end 356 | end 357 | 358 | -- returns true if local __FIRTH_DUMPTRACE__ == true, at frame frameidx. 359 | -- this is a signal that we've hit an internal error mechanism, and the 360 | -- actual error occurred one frame below that. 361 | local function dumptrace(frameidx) 362 | for i = 1, math.huge do -- "counted forever" 363 | local name, value = debug.getlocal(frameidx, i) 364 | if not name then break end 365 | if (name == "__FIRTH_DUMPTRACE__") and (value == true) then return true end 366 | end 367 | return false 368 | end 369 | 370 | function compiler:traceframe(frame, level) 371 | local name, kind 372 | 373 | -- is it a firth-defined word or the interpret buf? 374 | local source = stringio.split(frame.source, ' ') 375 | if source[1] == "__FIRTH_WORD__" then 376 | name = source[2] 377 | kind = (name == "[INTERP_BUF]") and " " or " " 378 | else 379 | -- try to find a reasonable name 380 | local func = frame.func 381 | name = self.funcmap[func] 382 | if name then 383 | -- in dict/map; this is a prim word 384 | kind = " " 385 | else 386 | -- not defined in dict/map; try to extract from callstack 387 | local fname = frame.name 388 | if fname then 389 | name = fname 390 | else 391 | -- handle special cases; why don't these have a name? 392 | -- TODO: if one more of these crops up, do search in a for loop 393 | if func == self.interpret then 394 | name = "interpret" 395 | elseif func == self.execword then 396 | name = "execword" 397 | else 398 | name = tostring(func) 399 | end 400 | end 401 | 402 | -- is it a firth internal component? 403 | for i = 1, math.huge do -- "counted forever" 404 | local lname, lvalue = debug.getlocal(level, i) 405 | if not lname then break end 406 | if lname == "self" and type(lvalue) == "table" then 407 | kind = lvalue.__FIRTH_INTERNAL__ 408 | break 409 | end 410 | end 411 | end 412 | 413 | -- didn't find a specific type, mark it as "internal" 414 | kind = kind or "" 415 | end 416 | return '\t'..kind..": "..tostring(name) 417 | end 418 | 419 | function compiler:stacktrace() 420 | local __FIRTH_DUMPTRACE__ = true 421 | 422 | local stackframes = {} 423 | local i = 2 424 | for frame in frames(i) do 425 | if dumptrace(i) then 426 | stackframes = {} 427 | elseif frame.what == "Lua" then 428 | stackframes[#stackframes+1] = self:traceframe(frame, i) 429 | end 430 | i = i + 1 431 | end 432 | return "call trace:\n"..table.concat(stackframes, '\n') 433 | end 434 | 435 | function compiler:assert(cond, name, msg) 436 | local __FIRTH_DUMPTRACE__ = true 437 | 438 | if not cond then self:runtimeerror(name, msg) end 439 | end 440 | 441 | function compiler:cassert(tmpcond, msg) 442 | self:append("compiler:assert(%s, __FIRTH_WORD_NAME__, %q)", tmpcond, msg) 443 | end 444 | 445 | function compiler:immediate(word) 446 | local entry 447 | if word then 448 | entry = self:lookup(word) 449 | else 450 | entry = self.last 451 | self:assert(entry, "immediate", "NO ENTRY TO SET IMMEDIATE") 452 | end 453 | -- print("SETTING "..entry.name.." IMMEDIATE") 454 | entry.immediate = true 455 | end 456 | 457 | --! Compiles code to push value. 458 | --! @see pushstring() 459 | function compiler:push(val) 460 | if self.compiling then 461 | local typ = type(val) 462 | if typ == "function" or typ == "table" or typ == "userdata" then 463 | self:pushupval(val) 464 | else 465 | self:append("stack:push(%s)", val) 466 | end 467 | else 468 | self.stack:push(val) 469 | end 470 | end 471 | 472 | --! Compiles code to push correctly quoted string value. 473 | --! @see push() 474 | function compiler:pushstring(str) 475 | if self.compiling then 476 | str = string.format("%q", str):gsub("\\\\", "\\") -- restore backslashes 477 | self:append("stack:push(%s)", str) 478 | else 479 | self.stack:push(str) 480 | end 481 | end 482 | 483 | --! Compiles code to push a non-stringifiable value, which must be closed over. 484 | function compiler:pushupval(val) 485 | local upvals = self.target.upvals 486 | local idx = upvals.nextidx 487 | upvals.nextidx = idx + 1 488 | local name = "__uv"..tostring(idx).."__" 489 | upvals[name] = val 490 | self:append("stack:push(%s)", name) 491 | end 492 | 493 | function compiler:newtmp(initialval) 494 | local nexttmp = self.nexttmp 495 | local var = string.format("__tmp%d__", nexttmp) 496 | self.nexttmp = nexttmp + 1 497 | self:append("local %s = %s", var, tostring(initialval)) 498 | 499 | return var 500 | end 501 | 502 | function compiler:newtmpfmt(str, ...) 503 | return self:newtmp(str:format(...)) 504 | end 505 | 506 | function compiler:newtmpstr(str) 507 | str = string.format("%q", str):gsub("\\\\", "\\") -- restore backslashes 508 | return self:newtmp(str) 509 | end 510 | 511 | function compiler:poptmp() 512 | local var = self:newtmp("stack:pop()") 513 | return var 514 | end 515 | 516 | function compiler:toptmp() 517 | local var = self:newtmp("stack:top()") 518 | return var 519 | end 520 | 521 | function compiler:append(...) 522 | local code = '\t'..string.format(...) 523 | local target = self.target 524 | -- print("APPENDING LINE "..code.." TO "..target.name) 525 | if type(target) ~= "table" or (not target.compilebuf) then 526 | -- print(table.concathash(target)) 527 | self:runtimeerror("append", "INVALID COMPILE TARGET ("..tostring(target)..')') 528 | end 529 | table.insert(target.compilebuf, code) 530 | end 531 | 532 | 533 | --! @cond 534 | local mt = compiler 535 | mt.__index = mt 536 | compiler = {} 537 | --! @endcond 538 | 539 | 540 | --! compiler constructor. 541 | function compiler.new() 542 | -- build compiler object 543 | local self 544 | self = { 545 | dictionary = {}, 546 | funcmap = {}, 547 | stack = stack.new(), 548 | cstack = stack.new(), 549 | compiling = false, 550 | target = nil, 551 | tracing = false, 552 | nexttmp = 0, 553 | running = true, 554 | path = "stdin"; 555 | 556 | xperrhandler = function(msg) 557 | stringio.printline(string.format("ERROR: %s", msg)) 558 | stringio.print 'stack : ' 559 | stringio.printline(tostring(self.stack)) 560 | stringio.print 'cstack: ' 561 | stringio.printline(tostring(self.cstack)) 562 | stringio.printline(self:stacktrace()) 563 | end 564 | } 565 | setmetatable(self, mt) 566 | 567 | -- initialize dictionary with core words 568 | prims.initialize(self) 569 | self:loadfile "firth/core.firth" 570 | 571 | -- cleanup memory 572 | collectgarbage() 573 | collectgarbage() 574 | 575 | return self 576 | end 577 | 578 | return compiler 579 | -------------------------------------------------------------------------------- /firth/core.firth: -------------------------------------------------------------------------------- 1 | char @file_prims.firth drop 2 | char @see_comment_block_below_for_copyright_notice drop 3 | 4 | char : create settarget setcompiling 5 | char %s parse create setcompiling settarget 6 | interpret buildfunc bindfunc immediate 7 | : %s 8 | char %s push 9 | interpret buildfunc bindfunc immediate 10 | : word 11 | %s parse 12 | interpret buildfunc bindfunc 13 | : postpone 14 | word call 15 | interpret buildfunc bindfunc immediate 16 | : ; 17 | postpone interpret buildfunc bindfunc 18 | interpret buildfunc bindfunc immediate 19 | : ;immed postpone ; postpone immediate ; immediate 20 | 21 | : // char \n parse drop ;immed // There, now we can comment our code! 22 | 23 | // ///////////////////////////////////////////////////////////////////////////// 24 | // core.firth 25 | // Core library for low level code and compiler manipulation 26 | // 27 | // Copyright © 2015 Brigham Toskin 28 | // This file is part of the Firth language reference implementation. Usage 29 | // and redistribution of this software is governed by the terms of a modified 30 | // MIT-style license. You should have received a copy of the license with the 31 | // source distribution in the file LICENSE; if not, you may find it online at: 32 | // 33 | // 34 | // Formatting: 35 | // utf-8 ; unix ; 80 cols ; tabwidth 4 36 | // ///////////////////////////////////////////////////////////////////////////// 37 | 38 | : " char " parse push ;immed 39 | : ( char ( >ts " %b()" parsematch drop ;immed // ( (nestable) parentheses) 40 | 41 | // loop and iteration 42 | : loops ( n -- ) 1 push " swap" call postpone for " drop" call ;immed 43 | 44 | // some higher level stack manipulations 45 | : nip ( a b -- b ) swap drop ; 46 | : 2dup ( a b -- a b a b ) over over ; 47 | : 3dup ( a b c -- a b c a b c ) 2 pick 2 pick 2 pick ; 48 | : ndup ( n*x n -- 2n*x ) dup loops dup pick swap end drop ; 49 | : 2drop ( a b -- ) drop drop ; 50 | : 3drop ( a b c -- ) drop drop drop ; 51 | : ndrop ( n*x n -- ) loops drop end ; 52 | 53 | // a few parser/interpreter/compiler control words 54 | : call: word push " call" call ;immed 55 | : xt ( entry -- xt ) " func" @@ nip ; 56 | : ?deferred ( -- ) 57 | last " name" @@ dup 58 | create settarget setcompiling // create and start compiling overriding def 59 | call: compiling? postpone if 60 | call // encode a call to compiling word 61 | postpone else 62 | xt execute // exec compiling word, so it can write semantics to def 63 | postpone end 64 | postpone ;immed ; 65 | : ;defer postpone ; ?deferred ;immed 66 | : ` ( -- entry ) ( TS: name ) word dict push ;immed 67 | : ' ( -- xt ) ( TS: name ) postpone ` xt ;immed 68 | : eval: ( -- i*x ) ( TS: name ) postpone ' execute ;immed 69 | 70 | // miscellaneous util words and stuff 71 | ` NOP alias: NOOP 72 | : \n ( -- "\n" ) char \n push ;immed 73 | : CR ( -- ) \n .raw ; 74 | : calls: ( -- ) postpone ` " calledby" @@ nip foreach drop " \tcalledby". . CR end ; 75 | : calledby: ( -- ) postpone ` " calls" @@ nip foreach drop " \tcalls" . . CR end ; 76 | 77 | // values and variables 78 | : const: ( x -- ) postpone : push call: push postpone ;immed ;immed 79 | " data" const: DATA 80 | : @ ( entry -- x ) DATA @@ nip ; 81 | : ! ( x entry -- ) DATA !! drop ; 82 | : variable ( name -- entry ) create dup does> NOP ; 83 | : var: ( x -- ) word variable ! ;immed 84 | : val: ( x -- ) word variable ! last does> @ ;immed 85 | 86 | 1 const: VERSION 87 | : copyright " :Firth ver. pre-alpha". VERSION . " Copyright © 2015 Brigham Toskin.". ; 88 | ` copyright alias: © 89 | 90 | // some basic arithmetic operators and shortcuts 91 | : + ( a b -- a+b ) char + binop ;defer 92 | ` + alias: add 93 | : - ( a b -- a-b ) char - binop ;defer 94 | ` - alias: sub 95 | : * ( a b -- a*b ) char * binop ;defer 96 | ` * alias: mul 97 | : / ( a b -- a/b ) char / binop ;defer 98 | ` / alias: div 99 | : % ( a b -- a%b ) char % binop ;defer 100 | ` % alias: mod 101 | : ** ( a b -- a^b ) char ^ binop ;defer // ^ is the name for bit-wise xor 102 | ` ** alias: pow 103 | 104 | : 1+ ( n -- n+1 ) 1 char + binopconst ;defer 105 | ` 1+ alias: inc 106 | : 1- ( n -- n-1 ) 1 char - binopconst ;defer 107 | ` 1- alias: dec 108 | : -1* ( n -- -n ) -1 char * binopconst ;defer 109 | ` -1* alias: neg 110 | : -1** ( n -- 1/n ) 1 swap / ; // division is faster, even with swap 111 | ` -1** alias: inverse 112 | 113 | : 2+ ( n -- n+2 ) 2 char + binopconst ;defer 114 | : 2- ( n -- n-2 ) 2 char - binopconst ;defer 115 | : 2* ( n -- 2n ) 2 char * binopconst ;defer 116 | ` 2* alias: double 117 | : 2/ ( n -- n/2 ) 2 char / binopconst ;defer 118 | ` 2/ alias: halve 119 | 120 | : 5+ ( n -- n+5 ) 5 char + binopconst ;defer 121 | : 5- ( n -- n-5 ) 5 char - binopconst ;defer 122 | : 5* ( n -- 5n ) 5 char * binopconst ;defer 123 | : 5/ ( n -- n/5 ) 5 char / binopconst ;defer 124 | 125 | : 10+ ( n -- n+10 ) 10 char + binopconst ;defer 126 | : 10- ( n -- n-10 ) 10 char - binopconst ;defer 127 | : 10* ( n -- 10n ) 10 char * binopconst ;defer 128 | : 10/ ( n -- n/10 ) 10 char / binopconst ;defer 129 | 130 | : 100+ ( n -- n+100 ) 100 char + binopconst ;defer 131 | : 100- ( n -- n-100 ) 100 char - binopconst ;defer 132 | : 100* ( n -- 100n ) 100 char * binopconst ;defer 133 | : 100/ ( n -- n/100 ) 100 char / binopconst ;defer 134 | : percent ( n p -- n*(p/100) ) postpone 100/ postpone * ;immed 135 | 136 | // some basic boolean operators 137 | : > ( a b -- a>b ) char > binop ;defer 138 | ` > alias: greater? 139 | : < ( a b -- a=" binop ;defer 146 | ` >= alias: greatereq? 147 | : ~= ( a b -- a!=b ) " ~=" binop ;defer 148 | ` ~= alias: noteq? 149 | 150 | : and ( a b -- a&&b ) " and" binop ;defer 151 | : or ( a b -- a||b ) " or" binop ;defer 152 | : nand ( a b -- !a&&b ) postpone and postpone not ;immed 153 | : nor ( a b -- !a||b ) postpone or postpone not ;immed 154 | : xor ( a b -- aXORb ) postpone 2not postpone ~= ;immed 155 | 156 | // redefine xt to allow a string name or a backticked word entry. 157 | // (needs = to be defined, which is deferred, which uses xt.) 158 | : xt ( name | entry -- xt ) dup type " string" = if dict end xt ; 159 | -------------------------------------------------------------------------------- /firth/stack.lua: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | --! @file stack.lua 3 | --! @brief Stack class for Firth language. 4 | --! @author btoskin - 5 | --! @copyright © 2015 Brigham Toskin 6 | --! 7 | --!

This file is part of the Firth language reference implementation. Usage 8 | --! and redistribution of this software is governed by the terms of a modified 9 | --! MIT-style license. You should have received a copy of the license with the 10 | --! source distribution in the file LICENSE; if not, you may find it online at 11 | --!

12 | -- 13 | -- Formatting: 14 | -- utf-8 ; unix ; 80 cols ; tabwidth 4 15 | -------------------------------------------------------------------------------- 16 | 17 | 18 | --! @cond 19 | local table = require "table" 20 | 21 | local mt 22 | local stack = { 23 | __FIRTH_INTERNAL__ = " " -- used for stacktraces 24 | } 25 | 26 | -- throw if stack is too short for operation 27 | local function assertsize(st, min, msg) 28 | local __FIRTH_DUMPTRACE__ = true -- used for stacktraces 29 | assert(min <= st.height, msg) 30 | end 31 | --! @endcond 32 | 33 | 34 | --! Pushes an item onto the stack. 35 | --! @param val value to push onto the stack 36 | function stack:push(val) 37 | local top = self.height + 1 38 | rawset(self, top, val) 39 | self.height = top 40 | end 41 | 42 | function stack:pushv(...) 43 | local args = {...} 44 | for _, arg in ipairs(args) do 45 | self:push(arg) 46 | end 47 | end 48 | 49 | --! Pops an item off the stack 50 | --! @return the former top stack item. 51 | function stack:pop() 52 | assertsize(self, 1, "UNDERFLOW") 53 | local val = rawget(self, self.height) 54 | self:drop() 55 | return val 56 | end 57 | 58 | --! Peeks at the top item, non-destructively. 59 | --! @return a copy of the top of the stack, without popping it. 60 | function stack:top() 61 | assertsize(self, 1, "STACK EMPTY") 62 | return rawget(self, self.height) 63 | end 64 | 65 | --! Pushes a copy of the top stack entry. 66 | function stack:dup() 67 | assertsize(self, 1, "STACK EMPTY") 68 | self:push(self:top()) 69 | end 70 | 71 | --! Removes the top stack item. 72 | function stack:drop() 73 | assertsize(self, 1, "UNDERFLOW") 74 | local top = self.height 75 | rawset(self, top, 0) 76 | self.height = top - 1 77 | end 78 | 79 | --! Removes all items from the stack. 80 | function stack:clear() 81 | local top = self.height 82 | for i = top, 1, -1 do rawset(self, i, 0) end 83 | self.height = 0 84 | end 85 | 86 | --! Swaps the location of the two top items. 87 | function stack:swap() 88 | assertsize(self, 2, "INSUFFICIENT HEIGHT") 89 | local top = self.height 90 | local prev = top - 1 91 | self[top], self[prev] = self[prev], self[top] 92 | end 93 | 94 | --! Pushes a copy of the second item from the top. 95 | function stack:over() 96 | assertsize(self, 2, "INSUFFICIENT HEIGHT") 97 | local top = self.height 98 | self:push(rawget(self, top-1)) 99 | end 100 | 101 | --! Rotates the top 3 stack elements downward. 102 | function stack:rot() 103 | assertsize(self, 3, "INSUFFICIENT HEIGHT") 104 | local top = self.height 105 | local prev, second = top - 1, top - 2 106 | self[second], self[prev], self[top] = self[prev], self[top], self[second] 107 | end 108 | 109 | --! Rotates the top 3 stack elements upward. 110 | function stack:revrot() 111 | assertsize(self, 3, "INSUFFICIENT HEIGHT") 112 | local top = self.height 113 | local prev, second = top - 1, top - 2 114 | self[second], self[prev], self[top] = self[top], self[second], self[prev] 115 | end 116 | 117 | --! Removes the second item from the top. 118 | function stack:nip() 119 | assertsize(self, 2, "INSUFFICIENT HEIGHT") 120 | local top = self.height 121 | local prev = top - 1 122 | rawset(self, prev, rawget(self, top)) 123 | self:drop() 124 | end 125 | 126 | --! Swaps the top two items, and pushes a copy of the former top item. 127 | function stack:tuck() 128 | self:swap() 129 | self:over() 130 | end 131 | 132 | --! Pushes a copy of the specified item. 133 | --! @param idx zero-based offset from the top of the stack of item to duplicate. 134 | function stack:pick(idx) 135 | assertsize(self, idx + 1, "INSUFFICIENT HEIGHT") 136 | local top = self.height 137 | self:push(rawget(self, top - idx)) 138 | end 139 | 140 | --! Removes the item at the given index and pushes it back onto the stack. 141 | --! @param idx offset of item to duplicate from the top of the stack. 142 | function stack:roll(idx) 143 | assertsize(self, idx + 1, "INSUFFICIENT HEIGHT") 144 | local top = self.height 145 | local tmp = rawget(self, top - idx) 146 | for i = top-idx, top-1 do 147 | rawset(self, i, rawget(self, i+1)) 148 | end 149 | rawset(self, top, tmp) 150 | end 151 | 152 | function stack:__tostring() 153 | local height = self.height 154 | local buf = {} 155 | for i = 1, height do 156 | buf[i] = tostring(self[i]) 157 | end 158 | 159 | return "["..table.concat(buf, ' ').."]" 160 | end 161 | 162 | function stack:__itr() 163 | return function(height, current) 164 | if current < height then 165 | current = current + 1 166 | return current, self[current] 167 | end 168 | end, self.height, 0 169 | end 170 | 171 | 172 | -- set up metatable and library table -- 173 | --! @cond 174 | mt = stack 175 | mt.__index = mt 176 | stack = {} 177 | --! @endcond 178 | 179 | --! stack constructor. 180 | --!

Example usage:

181 | --!
182 | --! local stack = require 'stack'
183 | --! local st = stack.new()
184 | --! st:push(1)
185 | --! st:push(2)
186 | --! st:push(3)
187 | --! print(st:pop(), st:pop(), st:pop())
188 | --! -- prints: 3	2	1
189 | --! 
190 | --! @return a newly initialized stack object. 191 | function stack.new() 192 | local s = setmetatable({ height = 0 }, mt) 193 | for i = 32,1,-1 do s[i] = 0 end -- poke in some null data to pre-reserve array 194 | return s 195 | end 196 | 197 | return stack 198 | -------------------------------------------------------------------------------- /firth/stringio.lua: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | --! @file stringio.lua 3 | --! @brief Text io and manipulation routines for Firth language. 4 | --! @author btoskin - 5 | --! @copyright © 2015 Brigham Toskin 6 | --! 7 | --!

This file is part of the Firth language reference implementation. Usage 8 | --! and redistribution of this software is governed by the terms of a modified 9 | --! MIT-style license. You should have received a copy of the license with the 10 | --! source distribution in the file LICENSE; if not, you may find it online at 11 | --!

12 | -- 13 | -- Formatting: 14 | -- utf-8 ; unix ; 80 cols ; tabwidth 4 15 | -------------------------------------------------------------------------------- 16 | 17 | 18 | --! @cond 19 | local string = require 'string' 20 | local table = require 'table' 21 | local io = require 'io' 22 | 23 | local stdout = io.stdout 24 | 25 | local ipairs = ipairs 26 | local print = print 27 | 28 | 29 | local function shift(tbl) 30 | return table.remove(tbl, 1) 31 | end 32 | 33 | local function pop(tbl) 34 | return table.remove(tbl) 35 | end 36 | 37 | local stringio = { 38 | __FIRTH_INTERNAL__ = "" -- used for stacktraces 39 | } 40 | --! @endcond 41 | 42 | 43 | --! Creates a Lua-style iterator to loop over tokens in string. 44 | --! Tokens are any consecutive printable chars broken up by white space. 45 | --! @param str the string of tokens to iterate over. 46 | --! @return an iterator function suitable for a for loop. 47 | --! @see #split() 48 | function stringio.tokens(str) 49 | return string.gmatch(str, "([^%s]+)") 50 | end 51 | 52 | --! Splits a string into an array, tokenized on whitespace. 53 | --! Tokens are any consecutive printable chars broken up by white space. 54 | --! @param str the string of tokens to split. 55 | --! @return an array of tokenized substrings. 56 | --! @see #tokens() 57 | function stringio.split(str) 58 | local result = {} 59 | for word in stringio.tokens(str) do 60 | table.insert(result, word) 61 | end 62 | return result 63 | end 64 | 65 | --! Chops off the next token from the front of a string. 66 | --! This function calculates the the next token, delimited as specified, from 67 | --! the start of the string, followed by the substring that follows the 68 | --! delimiter string. 69 | --! @param str the string to tokenize 70 | --! @param delim the delimiter pattern to tokenize with, 71 | --! defaults to '%s' (whitespace). 72 | --! @return the token, the remaining substring 73 | --! @see #matchtoken() 74 | function stringio.nexttoken(str, delim) 75 | delim = delim or "%s" -- here %s is an alias for any whitespace 76 | local pattern = string.format("([%s]*)([^%s]+)([%s]?)", delim, delim, delim) -- here %s is a string formatter as in C 77 | local discard1, token, discard2 = str:match(pattern) 78 | -- print(string.format("'%s', '%s', '%s'", discard1, token, discard2)) 79 | return token, str:sub(#token + #discard1 + #discard2 + 1) 80 | end 81 | 82 | --! Chops off the next matching token from the front of a string. 83 | --! This function matches the first occurrence of specified pattern, extracting 84 | --! and returning the matching substring, followed by the substring that follows 85 | --! the match. This function differs from #nexttoken() in that it finds a 86 | --! positive match, rather than the first thing it finds that doesn't match a 87 | --! delimiter pattern. 88 | --! @param str the string to tokenize 89 | --! @param pattern the pattern to match against 90 | --! @return the token, the remaining substring 91 | --! @see #nexttoken() 92 | function stringio.matchtoken(str, pattern) 93 | local tstart, tend = str:find(pattern) 94 | local token = str:sub(tstart, tend) 95 | return token, str:sub(tend + 1) 96 | end 97 | 98 | --! Tries to convert a string into a number. 99 | --! 100 | --!

This function attempts to parse a string as a numeric value, and convert 101 | --! it to the corresponding number. Surrounding whitespace is ignored, but any 102 | --! other non-contiguous or invalid characters mean this string is not a number. 103 | --!

104 | --! 105 | --!

If the argument is not of type string or number, it will attempt to first 106 | --! convert it to a string, and then convert that to a number. If that fails, 107 | --! the result is \c nil.

108 | --! 109 | --!

Some examples of valid numeric strings:

110 | --!
    111 | --!
  • "1" 112 | --!
  • \c "1." 113 | --!
  • \c ".1" 114 | --!
  • \c "1.0" 115 | --!
  • \c " 3.14159" 116 | --!
  • \c " -1.48532e-12 " 117 | --!
118 | --! 119 | --!

Some examples of invalid numeric strings:

120 | --!
    121 | --!
  • \c "1!" 122 | --!
  • \c "1 2" 123 | --!
  • \c "3.14159 = PI" 124 | --!
  • \c "zero" 125 | --!
126 | --! 127 | --! @param val a token string to convert. 128 | --! @return the parsed numeric value of \c val, or \c nil. 129 | function stringio.tonumber(val) 130 | return tonumber(val) or tonumber(tostring(val)) 131 | end 132 | 133 | -- file i/o stuff -- 134 | 135 | function stringio.stdin() 136 | return io.stidin 137 | end 138 | 139 | function stringio.input(infile) 140 | if type(infile) == "string" then 141 | return io.input(infile) 142 | elseif type(infile) == "userdata" and infile.read then 143 | return io.input(infile) 144 | elseif infile == nil then 145 | return io.input() 146 | else 147 | error("INVALID ARGUMENT: "..tostring(infile)) 148 | end 149 | end 150 | 151 | function stringio.read(file) 152 | file = file or stringio.input() 153 | return file:read("*all") 154 | end 155 | 156 | --! Reads a single line from file. 157 | --! Reads text from file up to the first end-of-line char it finds. 158 | --! The newline, if encountered, is discarded. 159 | --! @param file descriptor object to read from. If omitted or \c nil, 160 | --! uses the current input file. 161 | --! @return a string containing the next line read from \c file, or \c nil 162 | --! if it encounters EOF. 163 | function stringio.readline(file) 164 | file = file or stringio.input() 165 | return file:read("*line") 166 | end 167 | 168 | --! Prints its arguments to its output. 169 | --! The behavior of this function can be modified depending on the first 170 | --! argument that is passed in. 171 | --! @param ... a list of arguments to print. If the first argument is:
    172 | --!
  • a function, it is called, and the returned value is used to 173 | --! concatenate the items of the list; 174 | --!
  • a file descriptor or file-like object, it is treated as the 175 | --! output file, and printing is carried out by calling its 176 | --! \c write() method; 177 | --!
  • anything else, it is printed as a normal value, along with 178 | --! all the other arguments. 179 | --!
180 | --! @see #printline() 181 | function stringio.print(...) 182 | local args = {...} 183 | if #args == 0 then 184 | return 185 | end 186 | 187 | local arg1 = shift(args) 188 | local out 189 | if type(arg1) == 'function' then 190 | out = stdout 191 | out:write(table.concat(args, arg1())) 192 | else 193 | local mt = getmetatable(arg1) 194 | if mt and type(mt.write) == 'function' then 195 | out = arg1 196 | out:write(unpack(args)) 197 | else 198 | out = stdout 199 | out:write(arg1, unpack(args)) 200 | end 201 | end 202 | out:flush() 203 | end 204 | 205 | --! Prints its arguments to its output, with a newline appended. 206 | --! The behavior of this function is identical to #print(), except that a 207 | --! newline char/sequence appropriate for the host platform is appended to 208 | --! the output stream. 209 | --! @param ... a list of arguments to print. Please see the documentation 210 | --! for the paramaters of #print(), for details. 211 | --! @see #print() 212 | function stringio.printline(...) 213 | local args = {...} 214 | if #args > 0 then 215 | stringio.print(...) 216 | end 217 | stringio.print('\n') 218 | end 219 | 220 | --! @private 221 | local function getquotespec(val) 222 | return (type(val) == "string") and "%q" or "%s" 223 | end 224 | 225 | function table.concathash(t, sep) 226 | sep = sep or ' ' 227 | local kvs = {} 228 | for k,v in pairs(t) do 229 | local fmt = getquotespec(k)..' = '..getquotespec(v) 230 | kvs[#kvs+1] = string.format(fmt, tostring(k), tostring(v)) 231 | end 232 | return table.concat(kvs, sep) 233 | end 234 | 235 | return stringio 236 | -------------------------------------------------------------------------------- /firth/table.save.lua: -------------------------------------------------------------------------------- 1 | -- -*- coding: utf-8 -*- 2 | 3 | --[[ 4 | table.save.lua 5 | Save Table to File 6 | Load Table from File 7 | v 1.0 8 | 9 | Lua 5.2 compatible 10 | 11 | Only Saves Tables, Numbers and Strings 12 | Insides Table References are saved 13 | Does not save Userdata, Metatables, Functions and indices of these 14 | ---------------------------------------------------- 15 | table.save( table , filename ) 16 | 17 | on failure: returns an error msg 18 | 19 | ---------------------------------------------------- 20 | table.load( filename or stringtable ) 21 | 22 | Loads a table that has been saved via the table.save function 23 | 24 | on success: returns a previously saved table 25 | on failure: returns as second argument an error msg 26 | ---------------------------------------------------- 27 | 28 | http://lua-users.org/wiki/SaveTableToFile 29 | Copyright © 2012-2014 contributors 30 | Use and distribution governed by the MIT license; 31 | see for details. 32 | ]]-- 33 | 34 | 35 | do 36 | -- declare local variables 37 | --// exportstring( string ) 38 | --// returns a "Lua" portable version of the string 39 | local function exportstring( s ) 40 | return string.format("%q", s) 41 | end 42 | 43 | --// The Save Function 44 | function table.save( tbl,filename ) 45 | local charS,charE = " ","\n" 46 | local file,err = io.open( filename, "wb" ) 47 | if err then return err end 48 | 49 | file:write("--\n-- Machine-generated file; DO NOT HAND-EDIT\n--\n\n") 50 | 51 | -- initiate variables for save procedure 52 | local tables,lookup = { tbl },{ [tbl] = 1 } 53 | file:write( "return {"..charE ) 54 | 55 | for idx,t in ipairs( tables ) do 56 | file:write( "-- Table: {"..idx.."}"..charE ) 57 | file:write( "{"..charE ) 58 | local thandled = {} 59 | 60 | for i,v in ipairs( t ) do 61 | thandled[i] = true 62 | local stype = type( v ) 63 | -- only handle value 64 | if stype == "table" then 65 | if not lookup[v] then 66 | table.insert( tables, v ) 67 | lookup[v] = #tables 68 | end 69 | file:write( charS.."{"..lookup[v].."},"..charE ) 70 | elseif stype == "string" then 71 | file:write( charS..exportstring( v )..","..charE ) 72 | elseif stype == "number" then 73 | file:write( charS..tostring( v )..","..charE ) 74 | end 75 | end 76 | 77 | for i,v in pairs( t ) do 78 | -- escape handled values 79 | if (not thandled[i]) then 80 | 81 | local str = "" 82 | local stype = type( i ) 83 | -- handle index 84 | if stype == "table" then 85 | if not lookup[i] then 86 | table.insert( tables,i ) 87 | lookup[i] = #tables 88 | end 89 | str = charS.."[{"..lookup[i].."}]=" 90 | elseif stype == "string" then 91 | str = charS.."["..exportstring( i ).."]=" 92 | elseif stype == "number" then 93 | str = charS.."["..tostring( i ).."]=" 94 | end 95 | 96 | if str ~= "" then 97 | stype = type( v ) 98 | -- handle value 99 | if stype == "table" then 100 | if not lookup[v] then 101 | table.insert( tables,v ) 102 | lookup[v] = #tables 103 | end 104 | file:write( str.."{"..lookup[v].."},"..charE ) 105 | elseif stype == "string" then 106 | file:write( str..exportstring( v )..","..charE ) 107 | elseif stype == "number" then 108 | file:write( str..tostring( v )..","..charE ) 109 | end 110 | end 111 | end 112 | end 113 | file:write( "},"..charE ) 114 | end 115 | file:write( "}" ) 116 | file:close() 117 | end 118 | 119 | --// The Load Function 120 | function table.load( sfile ) 121 | local ftables,err = loadfile( sfile ) 122 | if err then return _,err end 123 | local tables = ftables() 124 | for idx = 1,#tables do 125 | local tolinki = {} 126 | for i,v in pairs( tables[idx] ) do 127 | if type( v ) == "table" then 128 | tables[idx][i] = tables[v[1]] 129 | end 130 | if type( i ) == "table" and tables[i[1]] then 131 | table.insert( tolinki,{ i,tables[i[1]] } ) 132 | end 133 | end 134 | -- link indices 135 | for _,v in ipairs( tolinki ) do 136 | tables[idx][v[2]],tables[idx][v[1]] = tables[idx][v[1]],nil 137 | end 138 | end 139 | return tables[1] 140 | end 141 | 142 | -- close do 143 | end 144 | -------------------------------------------------------------------------------- /lua2dox: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | use Doxygen::Lua; 6 | 7 | my $input = $ARGV[0]; 8 | my $p = Doxygen::Lua->new; 9 | print $p->parse($input); -------------------------------------------------------------------------------- /notes.txt: -------------------------------------------------------------------------------- 1 | Ideas / TODOs 2 | ------------- 3 | experimental optimization branch 4 | checks and cleanup tasks 5 | new compiler:trace(str) method, only prints if self.tracing == true 6 | Make sure [ and ] work, since compile looks for target on stack 7 | back up compiling? on cstack in eval, and restore at end 8 | inline functions 9 | compiles to an immediate that compiles the firth code in the body inline 10 | implies immediate 11 | explicit? 12 | : ;inline postpone interpret compilebuf inlinefunc bindfunc immediate ;immed 13 | automatic? 14 | fewer than x words in def? 15 | fewer than y lines of generate lua in compilebuf? 16 | pre-cache used lua library functions as locals and close over them 17 | string 18 | io 19 | stringio 20 | table 21 | stack cache? 22 | use closed-over locals for quick-access to top stack elements? 23 | what kind of bookkeeping and calculations are needed to make this work? 24 | does the overhead dominate the possible savings? 25 | where do these values live? the compiler? the prims file? 26 | compile time optimization module 27 | likely to be complicated 28 | load as a separate, optional module 29 | patch itself into the compiler machinery 30 | calculate stack deltas 31 | : foo bar baz ; 32 | foo.delta = bar.delta + baz.delta 33 | capture stack usage even for immediates that compile executable code 34 | +.consumes = 2 35 | +.writes = 1 36 | build a simple tree to represent input 37 | use knowledge of ops and stack usage to group expressions 38 | transform and optimize tree is possible 39 | generate lua code from optimized tree 40 | stack elision 41 | use locals wherever possible 42 | commit stack changes lazily 43 | pre-calculate results when literals are arguments? 44 | refactor input stream tokenization 45 | newlines(str) 46 | returns an array table of offsets into str where newlines live 47 | used in interpret() to set self.linenum in the processing loop 48 | e.g. { 0, 35, 71, 123, 151, 200, ... } 49 | nexttoken() 50 | rather than resizing input string, use token begin/end pointers 51 | makes extracting substrings faster and more memory efficient 52 | makes >ts faster and more memory efficient 53 | so we get: stringio.nexttoken(string, delim, searchstart) -> token, tokstart, tokend 54 | if only delims in string (or empty) return "" and start == end 55 | FIXES: will error if an input line has more than one trailing whitespace. 56 | stack data type 57 | 3>[], 4>[], n>[] ( n*x s n -- s ) 58 | [3]>, [4], [n]> ( s n -- s n*x ) 59 | [3]@, [4]@, [n]@ ( s n -- s n*x ) 60 | : [ ( -- s ) // TS: i*x ']' 61 | immediate 62 | allocates new stack 63 | executes each following word 64 | pulls tokens in a loop 65 | executes or num converts, pushes onto stack object 66 | ] breaks the loop and pushes the resulting stack object onto sys stack 67 | e.g. 68 | [] val: st 69 | 1 2 3 st 3>[] [3]> .S clear // prints [1 2 3] 70 | loops 71 | for: i/j/k 72 | implement with for 73 | pop TOS to local i 74 | e.g. 75 | users @ for: user genpassword user setpass end 76 | ranges 77 | return an iterator closure 78 | 1 10 range // -> 1 2 3 4 5 6 7 8 9 10 79 | 1 10 range: 2 // -> 1 3 5 7 9 80 | each, map, reduce, etc. 81 | similar to foreach style loop, but applies an xt to each item 82 | reference forth loops 83 | end start DO xxx LOOP // for i=start,end-1 ... 84 | end start DO xxx step +LOOP // for i=start,end-1,step ... 85 | BEGIN xxx cond UNTIL // do ... while(!cond) 86 | BEGIN xxx cond WHILE yyy REPEAT // while(cond) yyy OR do xxx while(cond) 87 | LEAVE // break 88 | conversions 89 | >string 90 | >number 91 | change parse routines to allow for doubled delimiters 92 | // e.g. alternative string syntax, for quoting options 93 | : '' ( -- s ) " ''" nextword parse ;immed 94 | execution, lambdas, functional programming 95 | compiler:pushfunc() 96 | in contrast to execfunc() 97 | pushes self.target and restores 98 | throws, if not target or target.name ~= "ANON" or some such? 99 | lambdas 100 | has no name, or name is ANON or something 101 | leaves an xt on tos, using pushfunc() 102 | when implemented, closes over locals? 103 | possible names/syntax 104 | lambda: 5 > ; // rewrite ; to push based on target.name? 105 | :( 5 > ); // ); will always do pushfunc() 106 | :NONAME 5 > ; 107 | e.g. 108 | :( 1- 2* ); data each 109 | compiletime macros / constexprs? 110 | precalculate the value as it's compiled 111 | most useful for showing the calculation in a func, but speeding it up at runtime 112 | lispy macros 113 | (+ 1 12 foo ) // compiles to push(13+compiletimevalueof(foo)) 114 | forthy macros 115 | [ 1 12 foo +] // compiles to the same 116 | better error debug messagges (xpcall and debug lib) 117 | if "debug mode", compile in a local var that counts firth source line 118 | recurse 119 | start all defs with -> " local function word()" 120 | compile as append " word()" 121 | end all defs with -> " end \n return word" 122 | local variables 123 | given name mapped to new temp variable 124 | in nested situations, should we be pushing the locals map? 125 | local ( name -- ) 126 | new primitive word, analogue to variable 127 | must only be used from an immediate because it compiles a new local decl 128 | local function newlocal() 129 | local name = stack:pop() 130 | compiler:newlocal(name, nil) 131 | end 132 | function compiler:newlocal(name, initialval) 133 | self.target.locals[name] = compiler:newtmp(initialval) 134 | end 135 | local? ( name -- b ) 136 | new immediate prim word 137 | compiler:push(target.locals[name] ~= nil) 138 | setl ( x name -- ) 139 | new prim, analogue to ! 140 | must only be used from an immediate because it alters compile state 141 | local function setlocal() 142 | local name = stack:pop() 143 | local val = stack:pop() 144 | compiler:setlocal(name, val) 145 | end 146 | function compiler:setlocal(tmpname, value) 147 | self:append(tmpname..'='..tostring(value)) 148 | end 149 | local: ( x -- ) // TS: name 150 | new primitive word, analogue to var: 151 | must be immediate because it alters compile state, reads input token 152 | : local: ( ) nextword dup local setl ;immed 153 | locals: ( -- ) // TS: names... 154 | allocates a list of local variables 155 | probably most useful at the top of a word def 156 | every space-delimited token following on the line is a new var name? 157 | : locals: ( ) char \n parse char %s split each local end ;immed 158 | setl: ( x -- ) // TS: name 159 | new prim, no analogue with global variables 160 | unlike !, this must be immediate to read tokens 161 | updates (but does not define) a local variable 162 | : setl: ( ) nextword setl ;immed 163 | =: (x -- ) // TS: name 164 | defines or updates a local, like go? 165 | immediate; checks at compile time which action to compile? 166 | : =: ( ) 167 | nextword dup local? if 168 | setl 169 | else 170 | dup local setl 171 | end 172 | ;immed 173 | e.g... 174 | // uses a local to keep the stack cleaner 175 | 0 =: width 176 | 100 loops 177 | nextfield dup printcontent widefield? if 178 | 50 setl: width // will print a wide break 179 | else 180 | 25 setl: width // will print a narrower break 181 | end 182 | // ...doing who knows what with the stack... 183 | width loops char . .raw end CR 184 | end 185 | process for compiling a token becomes 186 | 1. check target.locals[token] 187 | 2. if not found, check dictionary 188 | 3. if not found, attempt numeric conversion 189 | 4. if fails, lookup error 190 | print user's locals in stack traces? 191 | at top level, or first after dumptrace 192 | reverse lookup on func 193 | pull locals map from entry 194 | access lua tables form firth land 195 | compiler 196 | new prim word 197 | local function pushcompiler() stack:push(compiler) end 198 | dictionary 199 | : dictionary ( -- dict ) compiler " dictionary" @@ ; 200 | or, for efficiency... 201 | local function pushdict() stack:push(dictionary) end 202 | refactor scratch/last scheme 203 | should set us up for nested functions/lambdas 204 | multiline comments and strings 205 | pending parse callback if parsing unsatisfied at end of line? 206 | char ) parse => if didn't actually find it, run parse again on next line 207 | in a loop, read and throw away lines from input device until close comment? 208 | need to rework compiler input scheme 209 | lines(file) returns an iterator function; useful? 210 | version of parse that returns whether the delimiter was found 211 | we can keep looping if not found 212 | regular parse uses this and drops the result? 213 | param pattern matching 214 | core compiler/language feature? 215 | implemented through metaprogramming? 216 | // assumes something like : concat ( s1 s2 -- s1s2 ) ... ; 217 | : strreps ( s n -- n*s ) 218 | (( _ 1 )) drop 219 | (( _ _ )) over swap loops over concat end swap drop 220 | ; 221 | runtime documentation 222 | call lists 223 | if foo is deleted or redefined, remove from bar.calledby 224 | attach stack diagram comments to words as a docstring 225 | attach preceding comments to words, in a specific form? 226 | append string to EOL, to internal buffer 227 | : checks this buffer, and attaches to dict entry, if it exists 228 | interactive emacs mode 229 | repl in minibuffer 230 | use standard M-: key to jump to repl 231 | stay in minibuffer, in repl mode, between executions 232 | after minibuffer used for anything else (help, search) revert to repl 233 | output to secondary buffer 234 | code in main buffer 235 | executes code on save 236 | i/o redirected to minibuffer/secondary buffer 237 | syntax highlighting 238 | as words are defined, they are automatically added to font lock pattern list 239 | tokens can set self format and next-parsed format 240 | parse finds begin and end span for currently-set formatting 241 | found word can alway override its own formatting when "executed" 242 | variables to set 243 | wordstyle // default for any word 244 | numberstyle // default for numbers 245 | selfstyle // how to style this word, overrides whatever the default would have been 246 | nextstyle // how to style following word, overrides default and any self-styling 247 | wordstyle @ gray var: compilerstyle // wordstyle but gray; useful for styling compiler words 248 | code translation protocol 249 | main mechanism for running firth code from emacs 250 | translation layer for elisp <-> firth code 251 | collects firth code from buffers, sends via pipe to firth 252 | firth runs code, and sends back elisp to run 253 | could be as simple as display result 254 | could add font-lock rules for newly-defined words 255 | create a vocabulary for writing executable grammars for parsing DSLs 256 | whittle the lua codebase down to 257 | generic io and string handling 258 | access table fields 259 | compile buffers to lua functions 260 | call lua functions 261 | FirthOS 262 | tiny os that will boot directly into interactive emacs firth mode 263 | stripped-down linux kernel 264 | terminal drivers and maybe framebuffer 265 | audio of any sort? 266 | simplest/easiest FS support 267 | single-user mode 268 | needs virtual memory and multitasking 269 | minimal set of support utils and libs 270 | probably things like ls, rm, mkdir, and such 271 | libc 272 | ncurses 273 | readline? 274 | stripped-down emacs 275 | remove modes, routines, and elisp libraries that aren't applicable 276 | luajit 277 | firth 278 | -------------------------------------------------------------------------------- /test/test-stack.lua: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | --! @file test-stack.lua 3 | --! @brief Stack class test module. 4 | --! @author btoskin - 5 | --! @copyright © 2015 Brigham Toskin 6 | --! 7 | --!

This file is part of the Firth language reference implementation. Usage 8 | --! and redistribution of this software is governed by the terms of a modified 9 | --! MIT-style license. You should have received a copy of the license with the 10 | --! source distribution in the file LICENSE; if not, you may find it online at 11 | --!

12 | -- 13 | -- Formatting: 14 | -- utf-8 ; unix ; 80 cols ; tabwidth 4 15 | -------------------------------------------------------------------------------- 16 | 17 | 18 | local stack = require 'firth/stack' 19 | 20 | local st 21 | local initialcount = 5 22 | 23 | -- prints stack contents with error message 24 | local function assert(bool, msg, level) 25 | level = level or 2 26 | if not bool then 27 | error(msg.."\nstack: "..table.concat(st, ', '), level) 28 | end 29 | end 30 | 31 | local function assertstack(name, ...) 32 | local items = {...} 33 | assert(#items == st.height, 34 | string.format("%s(): stack height %d (expected %d)", 35 | name, st.height, #items), 36 | 3) 37 | for i = 1, st.height do 38 | assert(st[i] == items[i], 39 | string.format("%s(): stack[%d] == %s (expected %s)", 40 | name, i, tostring(st[i]), tostring(items[i])), 41 | 3) 42 | end 43 | end 44 | 45 | return { 46 | 47 | function() 48 | st = stack.new() -- () 49 | assert(st, "Error creating new stack") 50 | assert(st.height == 0, "Error creating new stack") 51 | end, 52 | 53 | function() 54 | for i = 1, initialcount do 55 | st:push(i) 56 | assert(st[st.height] == i, "Error pushing "..i.." onto stack") 57 | end 58 | assertstack("push", 1, 2, 3, 4, 5) 59 | end, 60 | 61 | function() 62 | local top = st.height 63 | local topitem = st:pop() 64 | assertstack("pop", 1, 2, 3, 4) 65 | assert(topitem == top, "pop() returned incorrect TOS val: "..topitem) 66 | end, 67 | 68 | function() 69 | local top = st.height 70 | st:drop() 71 | assertstack("drop", 1, 2, 3) 72 | end, 73 | 74 | function() 75 | local top = st.height 76 | local topitem = st:top() 77 | assertstack("top", 1, 2, 3) 78 | assert(topitem == top, "top() returned incorrect TOS val: "..top) 79 | end, 80 | 81 | function() 82 | local top = st.height 83 | st:dup() 84 | assertstack("dup", 1, 2, 3, 3) 85 | local topitem = st[st.height] 86 | local previtem = st[st.height - 1] 87 | st[st.height] = 4 -- sets up for testing swap() 88 | end, 89 | 90 | function() 91 | local top = st.height 92 | st:swap() 93 | assertstack("swap", 1, 2, 4, 3) 94 | end, 95 | 96 | function() 97 | local top = st.height 98 | st:over() 99 | assertstack("over", 1, 2, 4, 3, 4) 100 | end, 101 | 102 | function() 103 | local top = st.height 104 | st:rot() 105 | assertstack("rot", 1, 2, 3, 4, 4) 106 | st[top] = 5 -- replaces dup'ed val for testing nip() 107 | end, 108 | 109 | function() 110 | local top = st.height 111 | st:nip() 112 | assertstack("nip", 1, 2, 3, 5) 113 | end, 114 | 115 | function() 116 | local top = st.height 117 | st:tuck() 118 | assertstack("tuck", 1, 2, 5, 3, 5) 119 | end, 120 | 121 | function() 122 | local top = st.height 123 | st:pick(2) 124 | assertstack("pick", 1, 2, 5, 3, 5, 5) 125 | end, 126 | 127 | function() 128 | local top = st.height 129 | st:roll(2) 130 | assertstack("roll", 1, 2, 5, 5, 5, 3) 131 | end, 132 | 133 | function() 134 | local top = st.height 135 | local a, b, c = st[top], st[top-1], st[top-2] 136 | st:revrot() 137 | assertstack("-rot", 1, 2, 5, 3, 5, 5) 138 | end, 139 | 140 | function() 141 | local top = st.height 142 | st:push(nil) 143 | assert(st.height == top + 1, "push(nil) did not increment size") 144 | assert(st:pop() == nil, "pop() didn't properly return nil") 145 | assert(st.height == top, "pop() didn't properly return nil") 146 | end, 147 | 148 | function() 149 | st:clear() -- () 150 | assert(st.height == 0, "clear() did not empty stack") 151 | end, 152 | 153 | function() 154 | st:pushv(1, 2, 3, 4) 155 | assertstack("pushv", 1, 2, 3, 4) 156 | st:clear() 157 | end, 158 | 159 | function() 160 | st:push(nil) 161 | st:push(123.456) 162 | st:push(nil) 163 | assert(st.height == 3, "didn't push nils") 164 | 165 | -- we can't use assertstack() because it will count nils wrong 166 | assert(st:pop() == nil, "didn't push nil correctly") 167 | assert(st:pop() == 123.456, "didn't push nil correctly") 168 | assert(st:pop() == nil, "didn't push nil correctly") 169 | end, 170 | } -------------------------------------------------------------------------------- /test/test-stringio.lua: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | --! @file test-stringio.lua 3 | --! @brief Text io and manipulation routines test module. 4 | --! @author btoskin - 5 | --! @copyright © 2015 Brigham Toskin 6 | --! 7 | --!

This file is part of the Firth language reference implementation. Usage 8 | --! and redistribution of this software is governed by the terms of a modified 9 | --! MIT-style license. You should have received a copy of the license with the 10 | --! source distribution in the file LICENSE; if not, you may find it online at 11 | --!

12 | -- 13 | -- Formatting: 14 | -- utf-8 ; unix ; 80 cols ; tabwidth 4 15 | -------------------------------------------------------------------------------- 16 | 17 | 18 | local stringio = require 'firth/stringio' 19 | 20 | local table = require 'table' 21 | 22 | 23 | return { 24 | 25 | function() 26 | local toks = {"Hello,", "World!"} 27 | local string = table.concat(toks, ' ') 28 | local i = 1 29 | for tok in stringio.tokens(string) do 30 | assert(tok == toks[i], "Token mismatch: "..tok..", "..toks[i]) 31 | i = i + 1 32 | end 33 | end, 34 | 35 | function() 36 | local toks = stringio.split "this.should produce\tan array of-@#$_SIX \t \nstrings" 37 | assert(#toks == 6, "Incorrect number of tokens returned") 38 | end, 39 | 40 | function() 41 | local str = "\t1 2 \t 3:4::::" 42 | local tok 43 | for i = 1, 2 do 44 | tok, str = stringio.nexttoken(str) 45 | assert(i == stringio.tonumber(tok), 46 | string.format("Incorrect token value parsed: '%s'", tok)) 47 | end 48 | for i = 3, 4 do 49 | tok, str = stringio.nexttoken(str, ':') 50 | assert(i == stringio.tonumber(tok), 51 | string.format("Incorrect token value parsed: '%s'", tok)) 52 | end 53 | assert(str == ":::", "Expected ':::' after last token; got '"..str.."'") 54 | end, 55 | 56 | function() 57 | local nested = "(I'd like to talk to you about a thing (you know... the thing))" 58 | local str = " \t"..nested.."123" 59 | local tok 60 | tok, str = stringio.matchtoken(str, "%b()") 61 | assert(tok == nested, 62 | string.format("Incorrect token value parsed: '%s'", tok)) 63 | assert(str == "123", "Expected '123' after last token; got '"..str.."'") 64 | tok, str = stringio.matchtoken(str, '2') 65 | assert(tok == '2', 66 | string.format("Incorrect token value parsed: '%s'", tok)) 67 | assert(str == "3", "Expected '3' after last token; got '"..str.."'") 68 | end, 69 | 70 | function() 71 | assert(stringio.tonumber('0') == 0, "Conversion to number failed") 72 | assert(stringio.tonumber('1.') == 1.0, "Conversion to number failed") 73 | assert(stringio.tonumber('.1') == 0.1, "Conversion to number failed") 74 | assert(stringio.tonumber(' 3.14159 ') == 3.14159, "Conversion to number failed") 75 | assert(stringio.tonumber('-1.23456E-5') == -1.23456E-5, "Conversion to number failed") 76 | 77 | local t = {s = "2.718281828459045"} 78 | local mt = {} 79 | function mt:__tostring() return self.s end 80 | mt.__index = mt 81 | setmetatable(t, mt) 82 | assert(stringio.tonumber(t) == 2.718281828459045, "Conversion to number failed") 83 | 84 | assert(stringio.tonumber('zero') == nil, "Conversion to number failed to fail") 85 | assert(stringio.tonumber('1.0.') == nil, "Conversion to number failed to fail") 86 | assert(stringio.tonumber('PI=3.14159') == nil, "Conversion to number failed to fail") 87 | assert(stringio.tonumber('123 hello') == nil, "Conversion to number failed to fail") 88 | assert(stringio.tonumber('') == nil, "Conversion to number failed to fail") 89 | end, 90 | 91 | } -------------------------------------------------------------------------------- /test/test.lua: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | --! @file test.lua 3 | --! @brief Simple testing framework for Firth language components. 4 | --! @author btoskin - 5 | --! @copyright © 2015 Brigham Toskin 6 | --! 7 | --!

This file is part of the Firth language reference implementation. Usage 8 | --! and redistribution of this software is governed by the terms of a modified 9 | --! MIT-style license. You should have received a copy of the license with the 10 | --! source distribution in the file LICENSE; if not, you may find it online at 11 | --!

12 | -- 13 | -- Formatting: 14 | -- utf-8 ; unix ; 80 cols ; tabwidth 4 15 | -------------------------------------------------------------------------------- 16 | 17 | 18 | local io = require 'io' 19 | local output = io.output() 20 | local table = require 'table' 21 | 22 | 23 | local failures = 0 24 | local messages = {} 25 | local mt = {} 26 | mt.insert = table.insert 27 | mt.__index = mt 28 | setmetatable(messages, mt) 29 | 30 | local function try(bool, msg) 31 | if not bool then 32 | failures = failures + 1 33 | if msg then messages:insert(msg) end 34 | output:write 'E' 35 | else 36 | output:write '.' 37 | end 38 | return bool, msg 39 | end 40 | 41 | local function failed(msg) 42 | output:write('E\n') 43 | messages:insert(msg) 44 | failures = failures + 1 45 | end 46 | 47 | local function dotests(path) 48 | local success, tests = pcall(loadfile, path) 49 | if success then 50 | success, tests = pcall(tests) 51 | if success then 52 | for _, test in ipairs(tests) do 53 | try(pcall(test)) 54 | end 55 | else 56 | failed(tests) 57 | end 58 | else 59 | failed(tests) 60 | end 61 | output:write('\n') 62 | end 63 | 64 | -- TODO: parameterize this or automate it 65 | dotests 'test/test-stack.lua' 66 | dotests 'test/test-stringio.lua' 67 | 68 | if failures > 0 then 69 | output:write(string.format("%d failed tests.\n", failures)) 70 | for _, msg in ipairs(messages) do 71 | output:write(string.format("%s\n", msg)) 72 | end 73 | end --------------------------------------------------------------------------------