//: A Mu program is a book of 'recipes' (functions) :(before "End Globals") //: Each recipe is stored at a specific page number, or ordinal. map Recipe; //: You can also refer to each recipe by its name. map Recipe_ordinal; recipe_ordinal Next_recipe_ordinal = 1; //: Ordinals are like numbers, except you can't do arithmetic on them. Ordinal //: 1 is not less than 2, it's just different. Phone numbers are ordinals; //: adding two phone numbers is meaningless. Here each recipe does something //: incommensurable with any other recipe. :(after "Types") typedef int recipe_ordinal; :(before "End Types") // Recipes are lists of instructions. To perform or 'run' a recipe, the // computer runs its instructions. struct recipe { recipe_ordinal ordinal; string name; vector steps; // End recipe Fields recipe(); }; :(before "struct recipe") // Each instruction is either of the form: // product1, product2, product3, ... <- operation ingredient1, ingredient2, ingredient3, ... // or just a single 'label' starting with a non-alphanumeric character // +label // Labels don't do anything, they're just named locations in a recipe. struct instruction { bool is_label; string label; // only if is_label string name; // only if !is_label string original_string; // for error messages recipe_ordinal operation; // get(Recipe_ordinal, name) vector ingredients; // only if !is_label vector products; // only if !is_label // End instruction Fields instruction(); void clear(); bool is_empty(); }; :(before "struct instruction") // Ingredients and products are a single species -- a reagent. Reagents refer // either to numbers or to locations in memory along with 'type' tags telling // us how to interpret them. They also can contain arbitrary other lists of // properties besides types, but we're getting ahead of ourselves. struct reagent { string original_string; string name; type_tree* type; vector > properties; // can't be a map because the string_tree sometimes needs to be NULL, which can be confusing double value; bool initialized; // End reagent Fields reagent(const string& s); reagent() :type(NULL), value(0), initialized(false) {} reagent(type_tree* t) :type(t), value(0), initialized(false) {} ~reagent(); void clear(); reagent(const reagent& original); reagent& operator=(const reagent& original); void set_value(double v) { value = v; initialized = true; } }; :(before "struct reagent") // Types can range from a simple type ordinal, to arbitrarily complex trees of // type parameters, like (map (address array character) (list number)) struct type_tree { bool atom; string name; // only if atom type_ordinal value; // only if atom type_tree* left; // only if !atom type_tree* right; // only if !atom ~type_tree(); type_tree(const type_tree& original); // atomic type ordinal explicit type_tree(string name); type_tree(string name, type_ordinal v) :atom(true), name(name), value(v), left(NULL), right(NULL) {} // tree of type ordinals type_tree(type_tree* l, type_tree* r) :atom(false), value(0), left(l), right(r) {} type_tree& operator=(const type_tree& original); bool operator==(const type_tree& other) const; bool operator!=(const type_tree& other) const { return !operator==(other); } bool operator<(const type_tree& other) const; bool operator>(const type_tree& other) const { return other.operator<(*this); } }; struct string_tree { bool atom; string value; // only if atom string_tree* left; // only if !atom string_tree* right; // only if !atom ~string_tree(); string_tree(const string_tree& original); // atomic string explicit string_tree(string v) :atom(true), value(v), left(NULL), right(NULL) {} // tree of strings string_tree(string_tree* l, string_tree* r) :atom(false), left(l), right(r) {} }; // End type_tree Definition :(code) type_tree::type_tree(string name) :atom(true), name(name), value(get(Type_ordinal, name)), left(NULL), right(NULL) {} :(before "End Globals") // Locations refer to a common 'memory'. Each location can store a number. map Memory; :(before "End Reset") Memory.clear(); :(after "Types") // Mu types encode how the numbers stored in different parts of memory are // interpreted. A location tagged as a 'character' type will interpret the // value 97 as the letter 'a', while a different location of type 'number' // would not. // // Unlike most computers today, Mu stores types in a single big table, shared // by all the Mu programs on the computer. This is useful in providing a // seamless experience to help understand arbitrary Mu programs. typedef int type_ordinal; :(before "End Globals") map Type_ordinal; map Type; type_ordinal Next_type_ordinal = 1; type_ordinal Number_type_ordinal = 0; type_ordinal Boolean_type_ordinal = 0; type_ordinal Character_type_ordinal = 0; type_ordinal Address_type_ordinal = 0; type_ordinal Array_type_ordinal = 0; :(code) void setup_types() { Type.clear(); Type_ordinal.clear(); put(Type_ordinal, "literal", 0); Next_type_ordinal = 1; // Mu Types Initialization Number_type_ordinal = put(Type_ordinal, "number", Next_type_ordinal++); get_or_insert(Type, Number_type_ordinal).name = "number"; put(Type_ordinal, "location", Number_type_ordinal); // synonym of number for addresses we'll never look up Address_type_ordinal = put(Type_ordinal, "address", Next_type_ordinal++); get_or_insert(Type, Address_type_ordinal).name = "address"; Boolean_type_ordinal = put(Type_ordinal, "boolean", Next_type_ordinal++); get_or_insert(Type, Boolean_type_ordinal).name = "boolean"; Character_type_ordinal = put(Type_ordinal, "character", Next_type_ordinal++); get_or_insert(Type, Character_type_ordinal).name = "character"; // Array types are a special modifier to any other type. For example, // array:number or array:address:boolean. Array_type_ordinal = put(Type_ordinal, "array", Next_type_ordinal++); get_or_insert(Type, Array_type_ordinal).name = "array"; // End Mu Types Initialization } void teardown_types() { for (map::iterator p = Type.begin(); p != Type.end(); ++p) { for (int i = 0; i < SIZE(p->second.elements); ++i) p->second.elements.clear(); } Type_ordinal.clear(); } :(before "End One-time Setup") setup_types(); atexit(teardown_types); :(before "End Types") // You can construct arbitrary new types. New types are either 'containers' // with multiple 'elements' of other types, or 'exclusive containers' containing // one of multiple 'variants'. (These are similar to C structs and unions, // respectively, though exclusive containers implicitly include a tag element // recording which variant they should be interpreted as.) // // For example, storing bank balance and name for an account might require a // container, but if bank accounts may be either for individuals or groups, // with different properties for each, that may require an exclusive container // whose variants are individual-account and joint-account containers. enum kind_of_type { PRIMITIVE, CONTAINER, EXCLUSIVE_CONTAINER }; struct type_info { string name; kind_of_type kind; vector elements; // End type_info Fields type_info() :kind(PRIMITIVE) { // End type_info Constructor } }; enum primitive_recipes { IDLE = 0, COPY, // End Primitive Recipe Declarations MAX_PRIMITIVE_RECIPES, }; :(code) //: It's all very well to construct recipes out of other recipes, but we need //: to know how to do *something* out of the box. For the following //: recipes there are only codes, no entries in the book, because Mu just knows //: what to do for them. void setup_recipes() { Recipe.clear(); Recipe_ordinal.clear(); put(Recipe_ordinal, "idle", IDLE); // Primitive Recipe Numbers put(Recipe_ordinal, "copy", COPY); // End Primitive Recipe Numbers } //: We could just reset the recipe table after every test, but that gets slow //: all too quickly. Instead, initialize the common stuff just once at //: startup. Later layers will carefully undo each test's additions after //: itself. :(before "End One-time Setup") setup_recipes(); assert(MAX_PRIMITIVE_RECIPES < 200); // level 0 is primitives; until 199 Next_recipe_ordinal = 200; put(Recipe_ordinal, "main", Next_recipe_ordinal++); // Load Mu Prelude // End Mu Prelude :(before "End Commandline Parsing") assert(Next_recipe_ordinal < 1000); // recipes being tested didn't overflow into test space :(before "End Reset") Next_recipe_ordinal = 1000; // consistent new numbers for each test //: One final detail: tests can modify our global tables of recipes and types, //: so we need some way to clean up after each test is done so it doesn't //: influence later ones. :(before "End Globals") map Recipe_ordinal_snapshot; map Recipe_snapshot; map Type_ordinal_snapshot; map Type_snapshot; :(before "End One-time Setup") save_snapshots(); :(before "End Reset") restore_snapshots(); :(code) void save_snapshots() { Recipe_ordinal_snapshot = Recipe_ordinal; Recipe_snapshot = Recipe; Type_ordinal_snapshot = Type_ordinal; Type_snapshot = Type; // End save_snapshots } void restore_snapshots() { Recipe = Recipe_snapshot; Recipe_ordinal = Recipe_ordinal_snapshot; restore_non_recipe_snapshots(); } // when running sandboxes in the edit/ app we'll want to restore everything except recipes defined in the app void restore_non_recipe_snapshots() { Type_ordinal = Type_ordinal_snapshot; Type = Type_snapshot; // End restore_snapshots } //:: Helpers :(code) recipe::recipe() { ordinal = -1; // End recipe Constructor } instruction::instruction() :is_label(false), operation(IDLE) { // End instruction Constructor } void instruction::clear() { is_label=false; label.clear(); name.clear(); operation=IDLE; ingredients.clear(); products.clear(); original_string.clear(); // End instruction Clear } bool instruction::is_empty() { return !is_label && name.empty(); } // Reagents have the form :::...///... reagent::reagent(const string& s) :original_string(s), type(NULL), value(0), initialized(false) { // Parsing reagent(string s) istringstream in(s); in >> std::noskipws; // name and type istringstream first_row(slurp_until(in, '/')); first_row >> std::noskipws; name = slurp_until(first_row, ':'); string_tree* type_names = parse_property_list(first_row); // End Parsing Reagent Type Property(type_names) type = new_type_tree(type_names); delete type_names; // special cases if (is_integer(name) && type == NULL) type = new type_tree("literal"); if (name == "_" && type == NULL) type = new type_tree("literal"); // other properties slurp_properties(in, properties); // End Parsing reagent } void slurp_properties(istream& in, vector >& out) { while (has_data(in)) { istringstream row(slurp_until(in, '/')); row >> std::noskipws; string key = slurp_until(row, ':'); string_tree* value = parse_property_list(row); out.push_back(pair(key, value)); } } string_tree* parse_property_list(istream& in) { skip_whitespace_but_not_newline(in); if (!has_data(in)) return NULL; string_tree* first = new string_tree(slurp_until(in, ':')); if (!has_data(in)) return first; string_tree* rest = parse_property_list(in); if (!has_data(in) && rest->atom) return new string_tree(first, new string_tree(rest, NULL)); return new string_tree(first, rest); } :(before "End Unit Tests") void test_parse_property_list_atom() { istringstream in("a"); string_tree* x = parse_property_list(in); CHECK(x->atom); delete x; } void test_parse_property_list_list() { istringstream in("a:b"); string_tree* x = parse_property_list(in); CHECK(!x->atom); CHECK(x->left->atom); CHECK_EQ(x->left->value, "a"); CHECK(!x->right->atom); CHECK(x->right->left->atom); CHECK_EQ(x->right->left->value, "b"); CHECK(x->right->right == NULL); delete x; } :(code) type_tree* new_type_tree(const string_tree* properties) { if (!properties) return NULL; if (properties->atom) { const string& type_name = properties->value; int value = 0; if (contains_key(Type_ordinal, type_name)) value = get(Type_ordinal, type_name); else if (is_integer(type_name)) // sometimes types will contain literal integers, like for the size of an array value = 0; else if (properties->value == "->") // used in recipe types value = 0; else value = -1; // should never happen; will trigger errors later return new type_tree(type_name, value); } r
/*---------------------------------------------------------------------------/
/  FatFs - Configuration file
/---------------------------------------------------------------------------*/

#define FFCONF_DEF 89352	/* Revision ID */

/*---------------------------------------------------------------------------/
/ Function Configurations
/---------------------------------------------------------------------------*/

#define FF_FS_READONLY	0
/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
/  Read-only configuration removes writing API functions, f_write(), f_sync(),
/  f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
/  and optional writing functions as well. */


#define FF_FS_MINIMIZE	0
/* This option defines minimization level to remove some basic API functions.
/
/   0: Basic functions are fully enabled.
/   1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename()
/      are removed.
/   2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
/   3: f_lseek() function is removed in addition to 2. */


#define FF_USE_STRFUNC	0
/* This option switches string functions, f_gets(), f_putc(), f_puts() and f_printf().
/
/  0: Disable string functions.
/  1: Enable without LF-CRLF conversion.
/  2: Enable with LF-CRLF conversion. */


#define FF_USE_FIND		0
/* This option switches filtered directory read functions, f_findfirst() and
/  f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */


#define FF_USE_MKFS		1
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */


#define FF_USE_FASTSEEK	0
/* This option switches fast seek function. (0:Disable or 1:Enable) */


#define FF_USE_EXPAND	0
/* This option switches f_expand function. (0:Disable or 1:Enable) */


#define FF_USE_CHMOD	0
/* This option switches attribute manipulation functions, f_chmod() and f_utime().
/  (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */


#define FF_USE_LABEL	0
/* This option switches volume label functions, f_getlabel() and f_setlabel().
/  (0:Disable or 1:Enable) */


#define FF_USE_FORWARD	0
/* This option switches f_forward() function. (0:Disable or 1:Enable) */


/*---------------------------------------------------------------------------/
/ Locale and Namespace Configurations
/---------------------------------------------------------------------------*/

#define FF_CODE_PAGE	932
/* This option specifies the OEM code page to be used on the target system.
/  Incorrect code page setting can cause a file open failure.
/
/   437 - U.S.
/   720 - Arabic
/   737 - Greek
/   771 - KBL
/   775 - Baltic
/   850 - Latin 1
/   852 - Latin 2
/   855 - Cyrillic
/   857 - Turkish
/   860 - Portuguese
/   861 - Icelandic
/   862 - Hebrew
/   863 - Canadian French
/   864 - Arabic
/   865 - Nordic
/   866 - Russian
/   869 - Greek 2
/   932 - Japanese (DBCS)
/   936 - Simplified Chinese (DBCS)
/   949 - Korean (DBCS)
/   950 - Traditional Chinese (DBCS)
/     0 - Include all code pages above and configured by f_setcp()
*/


#define FF_USE_LFN		2
#define FF_MAX_LFN		255
/* The FF_USE_LFN switches the support for LFN (long file name).
/
/   0: Disable LFN. FF_MAX_LFN has no effect.
/   1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
/   2: Enable LFN with dynamic working buffer on the STACK.
/   3: Enable LFN with dynamic working buffer on the HEAP.
/
/  To enable the LFN, ffunicode.c needs to be added to the project. The LFN function
/  requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and
/  additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled.
/  The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can
/  be in range of 12 to 255. It is recommended to be set 255 to fully support LFN
/  specification.
/  When use stack for the working buffer, take care on stack overflow. When use heap
/  memory for the working buffer, memory management functions, ff_memalloc() and
/  ff_memfree() in ffsystem.c, need to be added to the project. */


#define FF_LFN_UNICODE	0
/* This option switches the character encoding on the API when LFN is enabled.
/
/   0: ANSI/OEM in current CP (TCHAR = char)
/   1: Unicode in UTF-16 (TCHAR = WCHAR)
/   2: Unicode in UTF-8 (TCHAR = char)
/
/  Also behavior of string I/O functions will be affected by this option.
/  When LFN is not enabled, this option has no effect. */


#define FF_LFN_BUF		255
#define FF_SFN_BUF		12
/* This set of options defines size of file name members in the FILINFO structure
/  which is used to read out directory items. These values should be suffcient for
/  the file names to read. The maximum possible length of the read file name depends
/  on character encoding. When LFN is not enabled, these options have no effect. */


#define FF_STRF_ENCODE	3
/* When FF_LFN_UNICODE >= 1 with LFN enabled, string I/O functions, f_gets(),
/  f_putc(), f_puts and f_printf() convert the character encoding in it.
/  This option selects assumption of character encoding ON THE FILE to be
/  read/written via those functions.
/
/   0: ANSI/OEM in current CP
/   1: Unicode in UTF-16LE
/   2: Unicode in UTF-16BE
/   3: Unicode in UTF-8
*/


#define FF_FS_RPATH		0
/* This option configures support for relative path.
/
/   0: Disable relative path and remove related functions.
/   1: Enable relative path. f_chdir() and f_chdrive() are available.
/   2: f_getcwd() function is available in addition to 1.
*/


/*---------------------------------------------------------------------------/
/ Drive/Volume Configurations
/---------------------------------------------------------------------------*/

#define FF_VOLUMES		10
/* Number of volumes (logical drives) to be used. (1-10) */


#define FF_STR_VOLUME_ID	0
#define FF_VOLUME_STRS		"RAM","NAND","CF","SD","SD2","USB","USB2","USB3"
/* FF_STR_VOLUME_ID switches string support for volume ID.
/  When FF_STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive
/  number in the path name. FF_VOLUME_STRS defines the drive ID strings for each
/  logical drives. Number of items must b