{"id":18495789,"url":"https://github.com/sunjay/lion","last_synced_at":"2025-04-08T22:31:57.388Z","repository":{"id":62442305,"uuid":"97862212","full_name":"sunjay/lion","owner":"sunjay","description":"A programming language and interactive REPL for performing calculations involving units","archived":false,"fork":false,"pushed_at":"2019-04-30T04:25:41.000Z","size":739,"stargazers_count":8,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-23T19:12:11.718Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sunjay.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-07-20T17:38:11.000Z","updated_at":"2022-08-20T05:46:09.000Z","dependencies_parsed_at":"2022-11-01T22:02:24.125Z","dependency_job_id":null,"html_url":"https://github.com/sunjay/lion","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sunjay%2Flion","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sunjay%2Flion/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sunjay%2Flion/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sunjay%2Flion/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sunjay","download_url":"https://codeload.github.com/sunjay/lion/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247940571,"owners_count":21022007,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-06T13:26:17.299Z","updated_at":"2025-04-08T22:31:53.525Z","avatar_url":"https://github.com/sunjay.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# lion [![Build Status](https://travis-ci.org/sunjay/lion.svg?branch=master)](https://travis-ci.org/sunjay/lion)\n\nA programming language and interactive REPL for performing calculations\ninvolving units.\n\n```rust\n% // Lines starting with `%` are input, `//` lines are comments\n% 2 + 2\n4\n% // Values starting with ' are units\n% // 'in = inches\n% 2 'in + 2 'in\n4 'in\n% // Conversions are automatically performed\n% 2 'cm + 4 'in\n12.16 'cm\n% 4 'in + 2 'cm\n4.78740157 'in\n% // Units can be operated on to create \"compound units\"\n% 9.81 'm / 's^2 // acceleration due to gravity\n9.81 'm / 's ^ 2\n% 9.81 'm * 's^-2 // Equivalent to 9.81 'm / 's ^ 2, but printed as written\n9.81 'm 's ^ -2\n% // Just the unit is the same as multiplying by the unit with value 1\n% (9.81'm) * (1's^-2) // Same as 9.81 'm * 's ^ -2\n9.81 'm 's ^ -2\n% // Adjacent units are automatically grouped together as one compound unit\n% 9.81 'm 's^-2\n9.81 'm 's ^ -2\n% // Explicit conversions are possible with `as`\n% 1 'm as 'cm\n100 'cm\n% // Commutative multiplication is recognized and unified in units\n% 10 'm * 's + 20 's * 'm\n30 'm * 's\n% // Division is not commutative, so the following will not do the same thing\n% 10 'm / 's + 20 's / 'm\nError: Cannot convert from 'm / 's to 's / 'm\n```\n\n## Features\n\n* Units in variables and functions are inferred when necessary and checked to\n  make sure each operation is mathematically sound\n  ```rust\n  // Here we are annotating the unit of the parameter radius and the unit\n  // of the return type\n  fn circle_area(radius 'm) -\u003e 'm^2 {\n      // If this unit was not provided, it would be inferred from the simplified\n      // unit of the expression on the right hand side. Since it is provided,\n      // lion will check to make sure the right hand side results in something\n      // that is convertable to the declared unit.\n      let square_radius 'm ^ 2 = radius ^ 2;\n      pi * square_radius\n  }\n  ```\n* Conversions between units are performed automatically and implicitly\n  ```rust\n  % fn foo(x 'km) {}\n  % // This is automatically converted into 'km so it can be passed to the function\n  % foo(10 'm);\n  % // This function only accepts values without a unit\n  % fn bar(x) {}\n  % bar(10); // OK\n  % bar(10 'm);\n  Error: Cannot convert from 'm to '_\n  % // This also works in expressions: 2 'km is converted to 'm\n  % 10 'm * 2 'km\n  20000 'm ^ 2\n  ```\n* Units are automatically simplified, or coerced into the unit you provide if\n  you provide one explicitly\n  ```rust\n  % 10 'km / 'm\n  10000\n  % 10 as 'm / 'm\n  10 'm / 'm\n  % let x 'm / 'm = 10;\n  % x\n  10 'm / 'm\n  ```\n* Unit conversions are mathematically valid\n  ```rust\n  % // This is invalid because you cannot convert between unit dimensions\n  % 10 as 'm / 'm ^ 2\n  Error: Cannot convert from '_ to 'm / 'm ^ 2\n  % // This is still okay because we aren't converting anything\n  % // Notice how the unit gets simplified since we haven't explicitly specified\n  % // the target unit\n  % 10 'm / 'm^2\n  10 'm ^ -1\n  ```\n\n## Declaration Files\n\nLion declaration files allow you to declare units, conversions, functions and\nuseful constants. For example, here is part of the default set of declarations\nloaded each time you start the interpreter:\n\n```rust\n//Filename: units.lion\n\n// Import this file using a use statement:\n//     use units;\n// This will import the file `units.lion` from the current directory\n//\n// Specify the full path using:\n//     use \"./units.lion\";\n//\n// Specify explicit unit overrides with:\n//     use units override ('mm, 'cm);\n// This would suppress any warnings about duplicate declarations for 'mm and 'cm\n\n// Declaring a public (usable outside of this module) constant value called `pi`\n// that has the unit '_\n//\n// The unit '_ is special in that it represents a quantity that is unitless\npub const pi '_ = 3.1415926535897932384626433832;\n// We could have also written this as follows since '_ is optional\n//pub const pi = 3.1415926535897932384626433832;\n\n// Units are defined using the `unit` keyword\nunit 'deg;\nunit 'rad;\n\n// Units do not need to appear in the file before they are used, but in order to\n// use a unit, it must be declared *somewhere*. Units don't even need to be\n// declared in the same file they are used in. As long as the units appear in\n// some imported file or in some imported package, you are good to go.\n\n// All units belong to the same global scope. Multiple declarations of the same\n// unit are NOT allowed because someone else defining the same unit may make\n// different assumptions about which conversions are defined than someone else.\n// If someone defines the same unit as someone else, you *cannot* use both of\n// their packages (or files if using path imports).\n\n// Unit conversions are defined by explicitly declaring the conversion factor\n// between two units. This representation permits lion to do dimensional\n// analysis in order to determine how to convert between units.\n//\n// Note that this defines the conversion from degrees to radians AND the\n// conversion from radians to degrees\nconversion 180 'deg == pi 'rad;\n\n// Duplicate conversions that result in multiple potential conversion factors\n// between two units are not allowed.\n//\n// conversion 1000 'm == 1 'km; // (1)\n// conversion 1000 'm == 1 'km; // Not allowed, duplicate of (1)\n// conversion 1001 'm == 1 'km; // Also not allowed, conflicts with (1)\n//\n// Rules about conversions:\n// * The units on both sides must be distinct\n// * At least one of the units in a defined conversion factor must be\n//   locally defined in the current package (or file if using path imports)\n//   * You cannot define conversions for units you do not define\n// * Within the same package (or file if using path imports), a conversion is\n//   only allowed to be defined once for a given unordered pair of units\n// * If more than one package defines a conversion between an unordered pair\n//   of units, the defined conversion factors must be equivalent\n//   Example:\n//       conversion 1000 'm == 1 'km; // In package A that defines 'm\n//       conversion 1 'km == 1000 'm; // In package B that defines 'km\n//       // Order does not matter\n//       conversion 1000 'm == 1 'km; // Package B could also do this\n// * The conversion cannot be between two different dimensions of the same unit\n//   * This is not a rigorous check. `'m == 'm^2` will be rejected, but it is\n//     up to you to make sure that `'L == 'm^2` is not accidentally defined\n// * The unit of the expression on either side cannot be '_ since a conversion\n//   between dimensions of units is invalid and '_ has dimension zero\n// * The expression on either side of the conversion cannot evaluate to zero\n//   since this can result in divide by zero errors\n// * Furthermore, if more than one conversion path exists between two units,\n//   then the cumulative conversion factor must be the same across all possible\n//   paths between those two units.\n//\n//   Example:\n//       conversion 1 'a == 2 'b;\n//       conversion 1 'b == 3 'c;\n//       conversion 1 'a == 6 'c; // OK because 2 * 3 = 6\n\n// Units can take advantage of predefined prefix systems. For example, rather\n// than defining m, cm, mm, nm, etc., you can just use the `#[prefix(SI)]`\n// annotation. This automatically defines conversion factors for 'm to 'cm,\n// 'm to 'mm, etc. Since conversion factors allow for converting in both\n// directions, you can just use 'm to define conversions to other units and all\n// of those conversions will automatically work for 'cm, 'mm, 'km, etc.\n//\n// An error will be produced if one of the generated units conflicts with an\n// already declared unit.\n\n// This will define 'm, 'mm, 'cm, etc.\n#[prefix(SI)]\nunit 'm;\n\n// Instead of 'mL, this will define 'ml, 'kl, etc.\n// transform takes a list of filters to apply\n#[prefix(SI, transform=[lowercase])]\nunit 'L;\n\n// Instead of mmeter and kmeter, this will produce millimeter and kilometer\n#[prefix(SI, transform=[longprefix])]\nunit 'meter;\n\n// All units must be a SINGLE case-sensitive identifier. You cannot for example\n// define a compound unit:\n//\n// unit 'rad / 'sec; // ERROR: Cannot define a compound unit. Use conversions instead.\n\n// You can define conversions for compound units using the individual units that\n// you have defined. Note that the rules about conversions above must be\n// followed and are enforced when conversions are defined, not just when\n// conversions are used.\nconversion 1 'm^3 == 1000 'L;\n\n// ALIASING\n\n// This is syntactic sugar for both defining a unit and a 1:1 conversion between\n// that unit and another (possibly compound) unit\n//\n// Any attributes like #[prefix(...)] will be passed on to the unit declaration\nunit 'kph alias 'km / 'hour;\n\n// This will generate the following code:\n//\n// unit 'kph;\n// conversion 1 'kph == 1 'km / 'hour;\n```\n\nDefining custom prefix systems will **eventually** be supported. Here's an idea\nof what that might look like:\n\n```rust\nprefix_system! {\n    name: SI,\n    conversions: {\n        // If the prefix system was applied to 'm, this would generate:\n        //\n        // conversion 10^24 'm == 1 'Ym\n        // conversion 10^21 'm == 1 'Zm\n        // ...etc...\n\n        // Format: prefix =\u003e conversion factor\n        Y =\u003e 10^24,\n        Z =\u003e 10^21,\n        E =\u003e 10^18,\n        P =\u003e 10^15,\n        T =\u003e 10^12,\n        G =\u003e 10^9,\n        M =\u003e 10^6,\n        k =\u003e 10^3,\n        h =\u003e 10^2,\n        da =\u003e 10^1,\n        d =\u003e 10^-1,\n        c =\u003e 10^-2,\n        m =\u003e 10^-3,\n        u =\u003e 10^-6,\n        n =\u003e 10^-9,\n        p =\u003e 10^-12,\n        f =\u003e 10^-15,\n        a =\u003e 10^-18,\n        z =\u003e 10^-21,\n        y =\u003e 10^-24,\n    },\n    // This is equivalent to using `#[prefix(SI)]` on each of these units when\n    // they were defined. Having this is important because we want to be able\n    // to use custom prefix systems with existing units that may have been\n    // defined elsewhere.\n    // This will fail to compile if these units already define a unit that\n    // has the same name as a unit generated by this prefix system\n    units: ['m, 'L],\n    //TODO: Figure out how transforms will work\n    transform: {\n        // Defines the long prefixes, the actual transform is done by some\n        // internal code\n        longprefix: {\n            // Format: prefix =\u003e long prefix\n            Y =\u003e yotta,\n            Z =\u003e zetta,\n            E =\u003e exa,\n            P =\u003e peta,\n            T =\u003e tera,\n            G =\u003e giga,\n            M =\u003e mega,\n            k =\u003e kilo,\n            h =\u003e hecto,\n            da =\u003e deca,\n            d =\u003e deci,\n            c =\u003e centi,\n            m =\u003e milli,\n            u =\u003e micro,\n            n =\u003e nano,\n            p =\u003e pico,\n            f =\u003e femto,\n            a =\u003e atto,\n            z =\u003e zepto,\n            y =\u003e yocto,\n        },\n    },\n}\n```\n\nTODO:\n* Port [remaining units](https://github.com/sunjay/lion/blob/d90dd3089f16e091a8c8ef4735a1bd2ff5e330cd/src/prelude/units.lion)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsunjay%2Flion","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsunjay%2Flion","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsunjay%2Flion/lists"}