diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ca35be0 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +_site diff --git a/Alex.jpeg b/Alex.jpeg new file mode 100644 index 0000000..3a0bf61 Binary files /dev/null and b/Alex.jpeg differ diff --git a/Blog.md b/Blog.md new file mode 100644 index 0000000..d89982d --- /dev/null +++ b/Blog.md @@ -0,0 +1,18 @@ +--- +permalink: /blog/ +title: MANOOL Blog +--- + +
+

{{page.title|smartify}}

+
+ +News, Random Topics: Examples, Rationale, Hacking... + +{%for post in site.posts%}{%unless post.unlisted%}
+

{{post.title|smartify}}

+

…{{post.excerpt}}…

+

Complete article

+
Published:
+
Updated:
+
{%endunless%}{%endfor%} diff --git a/CC-BY-4.0.svg b/CC-BY-4.0.svg new file mode 100644 index 0000000..addf050 --- /dev/null +++ b/CC-BY-4.0.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CNAME b/CNAME new file mode 100644 index 0000000..7bd2795 --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +manool.org \ No newline at end of file diff --git a/Home.md b/Home.md new file mode 100644 index 0000000..2e51135 --- /dev/null +++ b/Home.md @@ -0,0 +1,153 @@ +--- +# Home.md +permalink: / +title: The Programming Language MANOOL +updated: 2021-08-31 +--- + + + +{%include page_header.md%}{%raw%} + + +*MANOOL is a fairly readable homoiconic language with primarily value (non-referential) semantics that balances the programmer's productivity with +scalability...* + +#### What is the purpose of MANOOL? + +MANOOL is a general-purpose language suitable for diverse programming tasks from several problem domains. However, it has substantial bias toward scripting and +hopefully represents an evolutionary step over existing scripting languages. Thus, MANOOL should be compared to and can serve the same purpose as Python, Ruby, +PHP, Scheme, JavaScript, or Lua, which implies properties typical for such languages, e.g.: short edit-compile-test development cycle (at least for code bases +under 1 MLOCs) and run-time type checking. + +#### What real-world problem does your project solve? + +MANOOL is intentionally designed in such way that it represents a specific point on a continuum; on one side of this continuum are high-level languages designed +with the programmer's convenience and productivity in mind, and on the other side are languages designed with execution speed and/or solution scalability in +mind (whereas, as a matter of fact, software quality and reliability may correlate with either of those extremes, depending on the situation). Based on my past +programming experience, I argue that no existing mainstream language addresses both goals at once in a balanced way. As a result, programs are either more +expensive in development than they should be or do not scale with workload as needed. + +Think, for instance, about the number of registered users and their contributions on an in-house social-network Web-site. Working as a server infrastructure +administrator, on multiple occasions I saw serious scalability issues popping up suddenly after a year of production use due to flaws in backend software +architecture, including the choice of programming language and/or its implementation. + +As a more elaborate illustration, using a rigid static type system and gratuitous sharing mutable state in the program (widespread in traditional OOP) is far +from how many people think about problem solving in terms of basic (immutable) mathematical objects, and thus this places undue cognitive load on developers for +simple backend scripting. On the other hand, implementing backend algorithms in an average exploratory-programming language (especially in a straightforward +and/or idiomatic way) often leads to poor run-time performance scalability w.r.t. Web-site workload growth. + +#### OK, but what warrants a whole new language in case of MANOOL? + +Starting off with some relatively unpopular language makes little sense for me as a language designer, since I might miss in this case some improvement +opportunities (whatever it means) while getting almost nothing in return. But why not just extend an existing mainstream language to suite the above stated +goals instead of creating one from scratch? + +Achieving competing (and even incompatible) goals is hard and may lead to overly complex and difficult to adopt language designs. MANOOL leverages two +principles in order to deal with this problem, which are currently not observed among mainstream languages: + * open (homoiconic) approach to language architecture (in the same sense as in Lisp but using a notation alternative to S-expressions and with macro + definitions applying to their own limited scope), and + * primarily value (non-referential) semantics with copy-on-write policy under the hood and move operations (and this works even for user-defined abstract data + types, due to availability of special syntactic sugar). + +Both of those principles require things to work slightly differently on the very basic level, which suggests that introducing a whole new language is more +appropriate than trying to extend an existing one. + +#### Why should I learn MANOOL? + +It depends on who is asking. One possible reason is that playing around with MANOOL means joy and fun that existing mainstream languages can hardly offer to +you. E.g., in brief: + +* Assuming `A == (array of 1 2 3)`, after `B = A; A[1] = 0`, `B[1] == 2`. Likewise, after `B = A; B[1] = 0`, `A[1] == 2` -- value semantics. + +* On the other hand, `A[1] = 0` (as well as `S = S! + "Hi"`) may have (amortized) O(1) run-time complexity -- thanks to move operations. + +* `A[1] = 0` is actually a shorthand for `A = A!.Repl[1; 0]`, and in other contexts `A[1]` is equivalent to `A.Apply[1]` -- unifying syntactic sugar. + +* Incidentally, `A.P[...]` just stands for `P[A; ...]` (which could also be written as `(P A; ...)` in a Lisp-ish form) -- more syntactic sugar. + +* Polymorphic operations are indistinguishable from regular (first-class) procedures (and at the same time they are just symbols): + + (var (Plus = (+)) in ... 1.Plus[1] ... "Hi".Plus["World"] ... Out.Write[Plus] ...) + +* You can construct and index into a key-value mapping with sets as keys. After + + M = (map of (set of 1 2 3) = 1; (set of 4 5 6) = 2) + + `M[(set of 4 5 6)] == 2` -- no arbitrary restrictions on keys or their type, which is partly a consequence of value semantics. + +* First-class value bindings involve compile-time evaluation, and similarly you can use handful syntactic sugar to specify constant values, e.g.: `F64["1.1"]$`, + `D128["1.10"]$`, `Sqrt[F64[2]]$`. + +* You can write the whole program unit in some domain-specific language instead of standard MANOOL; just replace `(extern "...")` at program-unit level (see + complete examples below) with the reference to a different module. + +* On the other hand, macro bindings have limited scope (like any other kind of bindings): + + (let (unless = (macro: proc (F) as ...)) in ... (unless ...) ...) + +* A module can be introduced at program-unit level by the construct `(let (...) in: export ...)` or, equally, be bound to a name and thus become a local module + (à la Modula-2): + + (let (mUtilities = (let (...) in: export ...)) in ... (mUtilities in ...) ...) + +* Programs can recover from out-of-memory conditions gracefully and reliably: + + ReserveHeap[...]; (on HeapExhausted do ... after ...) + +#### What does it offer to potential project maintainers and contributors? + +MANOOL is a personal, solo-developer project with severely limited resources. Thus, to be viable, it almost inevitably has to use a straightforward, +streamlined, and modular implementation, which is based on simple algorithms and data structures (from the compiler theory standpoint). Let's take, for +instance, the implementation size -- the MANOOL translator is written in under 10 KLOCs, whereas the most widespread Python interpreter builds upon at least 100 +KLOCs. + +This does not necessarily mean that the MANOOL implementation is cheap or otherwise low-grade but rather that extra development effort can be committed to +ensuring high implementation quality and reliability. This also implies lower project entry requirements, encouraging more people to participate in the +development. Besides, such compact code bases are more suitable for educational purposes (than larger ones, which are often full of legacy stuff). + +#### Give me a complete example of what code in MANOOL may look like + +A "Hello World" program might look like + + ((extern "manool.org.18/std/1.0/all") in Out.WriteLine["Hello, world!"]) + +(using the 2nd version of the syntax, see below), and in the following sample program a recursive factorial function is defined and invoked: + + ( (extern "manool.org.18/std/1.0/all") in + : let rec (Fact = (proc (N) as + : if N == 0 then 1 else N * Fact[N - 1])) + in + Out.WriteLine["Factorial of 10 = " Fact[10]]) + +#### What's next? Do you have a roadmap for MANOOL? + +Sure, here it is (as of September 2021): + +1. Complete a JIT compiler for MANOOL to achieve run-time performance only marginally slower than that of the most sophisticated dynamic-language engines on the + market (such as V8 and LuaJIT) but only at a fraction of their complexity -- this is doable as per my experiments. + +2. Replace `{`/`}` in the syntax by `(`/`)` in the 2nd version of the language (as shown in this writeup). The idea is to appeal more to at least one + established language community (Lisp/Scheme), albeit at the cost of extra complexity (including a more complicated LL(2) parser). + +3. Complete and polish the MANOOL language [Specification](specification/) and the [Tutorial](tutorial/lesson-1). + +4. And ultimately, build a MANOOL ecosystem (libraries, tools, success stories) and a user community -- any help is welcome! + + +{%endraw%}{%include page_footer.md%} diff --git a/Icon.png b/Icon.png new file mode 100644 index 0000000..ee28b97 Binary files /dev/null and b/Icon.png differ diff --git a/Intro.md b/Intro.md new file mode 100644 index 0000000..11571e7 --- /dev/null +++ b/Intro.md @@ -0,0 +1,322 @@ +--- +# Intro.md +permalink: /specification/introduction-to-manool +title: Introduction to MANOOL +updated: 2020-01-09 +--- + +{%include page_header.md%}{%raw%} + + +MANOOL is a [homoiconic][], [dynamic][], and [multi-paradigm][] [general-purpose][] [computer][] [programming][] [language][] with a [functional core][]. The +author's [implementation][] of MANOOL is made for [native-code][] [run-time][] [environments][], is written in *idiomatic* [C++11][] (with [GCC-specific][] +extensions), and currently runs under several [Unix-like][] [operating systems][] (OSes) on a number of [CPU][] [instruction-set architectures][] (ISAs). The +implementation is a [free and open-source][] (FOSS) [software][] [development][] [tool][] published under the version 3 of the [GNU][] [General Public +License][] (GPLv3). + +[homoiconic]: //en.wikipedia.org/wiki/Homoiconic "Wikipedia: Homoiconic" +[dynamic]: //en.wikipedia.org/wiki/Dynamic_programming_language "Wikipedia: Dynamic programming language" +[multi-paradigm]: //en.wikipedia.org/wiki/Programming_paradigm "Wikipedia: Programming paradigm" +[general-purpose]: //en.wikipedia.org/wiki/General-purpose_programming_language "Wikipedia: General-purpose programming language" +[computer]: //en.wikipedia.org/wiki/Computer "Wikipedia: Computer" +[programming]: //en.wikipedia.org/wiki/Computer_programming "Wikipedia: Computer programming" +[language]: //en.wikipedia.org/wiki/Programming_language "Wikipedia: Programming language" +[functional core]: //en.wikipedia.org/wiki/Functional_programming "Wikipedia: Functional programming" +[implementation]: //en.wikipedia.org/wiki/Programming_language_implementation "Wikipedia: Programming language implementation" +[native-code]: //en.wikipedia.org/wiki/Native_code "Wikipedia: Native code" +[run-time]: //en.wikipedia.org/wiki/Run_time_(program_lifecycle_phase) "Wikipedia: Run time" +[environments]: //en.wikipedia.org/wiki/Runtime_environments "Wikipedia: Runtime environments" +[GCC-specific]: //en.wikipedia.org/wiki/GNU_Compiler_Collection "Wikipedia: GNU Compiler Collection" +[Unix-like]: //en.wikipedia.org/wiki/Unix-like "Wikipedia: Unix-like" +[operating systems]: //en.wikipedia.org/wiki/Operating_systems "Wikipedia: Operating systems" +[CPU]: //en.wikipedia.org/wiki/Central_processing_unit "Wikipedia: Central processing unit" +[instruction-set architectures]: //en.wikipedia.org/wiki/Instruction_set_architectures "Wikipedia: Instruction set architectures" +[free and open-source]: //en.wikipedia.org/wiki/Free_and_open-source "Wikipedia: Free and open-source" +[software]: //en.wikipedia.org/wiki/Software "Wikipedia: Software" +[development]: //en.wikipedia.org/wiki/Software_development "Wikipedia: Software development" +[tool]: //en.wikipedia.org/wiki/Development_tool "Wikipedia: Development tool" +[GNU]: //en.wikipedia.org/wiki/GNU_Project "Wikipedia: GNU Project" +[General Public License]: //en.wikipedia.org/wiki/General_Public_License "Wikipedia: General Public License" + + +Purpose +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +MANOOL is characterized as a _general-purpose_ language because it is *not* specific to a particular [problem domain][], and it is instead broadly applicable +across several problem domains. However, it is meant to compare and compete directly with such programming languages as [Python], [PHP], [Ruby], [Perl], and +[Tcl][] (i.e., so-called [scripting languages][]), or even [Scheme] and [Common Lisp], which somehow dooms its purpose. + +In overall, MANOOL is a practical language: it is conceived as a tool useful in the professional field of programming rather than as a [proof of concept][] for +any new programming techniques or mechanisms, although incidentally its [syntax][] and [semantics][] do have a combination of unusual features. + +[problem domain]: //en.wikipedia.org/wiki/Problem_domain "Wikipedia: Problem domain" +[scripting languages]: //en.wikipedia.org/wiki/Scripting_languages "Wikipedia: Scripting languages" +[proof of concept]: //en.wikipedia.org/wiki/Proof_of_concept "Wikipedia: Proof of concept" +[syntax]: //en.wikipedia.org/wiki/Syntax_(programming_languages) "Wikipedia: Syntax" +[semantics]: //en.wikipedia.org/wiki/Semantics_(computer_science) "Wikipedia: Semantics" + + +Motivation +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +The project MANOOL emerged out of mere programming language enthusiasm and frustration with existing languages but is based on ideas and experience its author +acquired throughout more than 25 years. Its design and implementation is the result of about 3.5 years of almost full-time work. + + +Examples +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +The following ["Hello, world!" program][] provides a basic idea of what [programs][] in MANOOL may look like: + + {{extern "manool.org.18/std/0.6/all"} in WriteLine[Out; "Hello, world!"]} + +And the following MANOOL [expression][] evaluates to a ([recursive][]) MANOOL [procedure][] (a function in more conventional terminology) used to calculate the +[factorial][] of an [integral][] [argument][]: + + { let rec + { Fact = + { proc { N } as + : if N == 0 then 1 else + N * Fact[N - 1] + } + } + in + Fact + } + +It is worth to observe that while [source code][] in MANOOL may look at first sight unfamiliar or even awkward, there are really good reasons behind it since +the syntax of MANOOL is a result of many design trade-offs. However, as in the case of [Smalltalk], the syntax of MANOOL is actually so simple that it could be +"described on a postcard". + +["Hello, world!" program]: //en.wikipedia.org/wiki/%22Hello,_World!%22_program "Wikipedia: “Hello, World!” program" +[programs]: //en.wikipedia.org/wiki/Computer_programs "Wikipedia: Computer programs" +[expression]: //en.wikipedia.org/wiki/Expression_(computer_science) "Wikipedia: Expression" +[recursive]: //en.wikipedia.org/wiki/Recursive_(computer_science) "Wikipedia: Recursive" +[procedure]: //en.wikipedia.org/wiki/Procedure_(computer_science) "Wikipedia: Procedure" +[factorial]: //en.wikipedia.org/wiki/Factorial "Wikipedia: Factorial" +[integral]: //en.wikipedia.org/wiki/Integer "Wikipedia: Integer" +[argument]: //en.wikipedia.org/wiki/Argument_(computer_programming) "Wikipedia: Argument" +[source code]: //en.wikipedia.org/wiki/Source_code "Wikipedia: Source code" + + +Goals +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +MANOOL has *explicitly* stated design goals (ordered according to priority), which are + 1. [implementation simplicity][] (which is the sole most important consideration in the design); + 2. [expressive power][] (in practical sense), [usability][], and general utility (value for consumers); attention to syntax and semantics details; + 3. [correctness][], [security][], and overall [quality][] of implementation; [run-time reliability][]; + 4. [run-time performance][] and [scalability][]; and + 5. consistency, completeness, [orthogonality][] of features and language [elegance][]; [conceptual economy][] and purity. + +The project MANOOL strives to satisfy *all* of the above goals (in that order) and do it *better* than existing alternatives would do! + +Note that quality of [diagnostics][] and [coding defect][] prevention have been deliberately left, however, among low-priority goals. + +[implementation simplicity]: //en.wikipedia.org/wiki/Worse_is_better "Wikipedia: Worse is better" +[expressive power]: //en.wikipedia.org/wiki/Expressive_power_(computer_science) "Wikipedia: Expressive power" +[usability]: //en.wikipedia.org/wiki/Usability "Wikipedia: Usability" +[correctness]: //en.wikipedia.org/wiki/Correctness_(computer_science) "Wikipedia: Correctness" +[security]: //en.wikipedia.org/wiki/Secure_coding "Wikipedia: Secure coding" +[quality]: //en.wikipedia.org/wiki/Software_quality "Wikipedia: Software quality" +[run-time reliability]: //en.wikipedia.org/wiki/Reliability_engineering "Wikipedia: Reliability engineering" +[run-time performance]: //en.wikipedia.org/wiki/Benchmark_(computing) "Wikipedia: Benchmark" +[scalability]: //en.wikipedia.org/wiki/Scalability "Wikipedia: Scalability" +[orthogonality]: //en.wikipedia.org/wiki/Orthogonality_(programming) "Wikipedia: Orthogonality" +[elegance]: //en.wikipedia.org/wiki/Elegance "Wikipedia: Elegance" +[conceptual economy]: //en.wikipedia.org/wiki/Occam%27s_razor "Wikipedia: Occam's razor" +[diagnostics]: //en.wikipedia.org/wiki/Diagnostics "Wikipedia: Diagnostics" +[coding defect]: //en.wikipedia.org/wiki/Software_defect "Wikipedia: Software defect" + + +Results +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +### Implementation simplicity +The MANOOL [translator][] fits in less than 10K [lines of code][] (KLOC) in C++11 plus less than 500 LOC in MANOOL, which covers the core language as well as +the [standard][] [library][]. + +[translator]: //en.wikipedia.org/wiki/Translator_(computing) "Wikipedia: Translator" +[lines of code]: //en.wikipedia.org/wiki/Lines_of_code "Wikipedia: Lines of code" +[standard]: //en.wikipedia.org/wiki/Standard_library "Wikipedia: Standard library" +[library]: //en.wikipedia.org/wiki/Library_(computing) "Wikipedia: Library" + +### Expressive power +MANOOL is directly suitable for accurately expressing [algorithms][] (*up to* [asymptotic computational complexity][]) as complex as those that one might find +in [computer-science][] (CS) papers where mostly some form of [high-level][] [pseudocode][] is encountered. + +[algorithms]: //en.wikipedia.org/wiki/Algorithms "Wikipedia: Algorithms" +[asymptotic computational complexity]: //en.wikipedia.org/wiki/Asymptotic_computational_complexity "Wikipedia: Asymptotic computational complexity" +[computer-science]: //en.wikipedia.org/wiki/Computer_science "Wikipedia: Computer science" +[high-level]: //en.wikipedia.org/wiki/High-level_programming_language "Wikipedia: High-level programming language" +[pseudocode]: //en.wikipedia.org/wiki/Pseudocode "Wikipedia: Pseudocode" + +### Run-time performance +According to simple synthetic [benchmarks][], the MANOOL implementation executes programs notably faster than some competitors and slightly faster or on a par +with other competitors. + +[benchmarks]: //en.wikipedia.org/wiki/Benchmark_(computing) "Wikipedia: Benchmark" + +### Run-time reliability +When needed, MANOOL programs can be instructed to recover even from [dynamic memory][] (heap and stack) exhaustion. + +[dynamic memory]: //en.wikipedia.org/wiki/Dynamic_memory "Wikipedia: Dynamic memory" + +### Other features ++ Very compact (or even [minimalist][]) core language (up to a point where a [meta-circular specification][] might be appropriate) ++ Convenient standard library (but completely *optional* to use) ++ Computational primitives based on [Church's][] [λ-calculus][] (in the spirit of [Landin's][] [ISWIM] prototype language/[ML]-like languages) + - [Name bindings][] with [static (lexical) scope][] + - *Explicit* [variable capture][] and classification of name bindings into [compile][compile-time]- and run-time + - Mainly [eager][] (strict) [evaluation strategy][] (from the perspective of λ-calculus) with possible [side effects][] ++ [Compile-time evaluation][] ++ [Metaprogramming][]: + - [Lisp]-like [syntactic macros][] with optional [macro hygiene][] + - (optionally) [self-modifying code][] ++ [Block-structured][] and [expression-oriented][] (from the perspective of [procedural][] [imperative programming][]) ++ [Dynamic][dynamic typing] (latent) but [strong][strong typing] [data typing][] [discipline][typing discipline] ++ [Ad-hoc][ad-hoc polymorphism] [polymorphism][]: + - run-time [function][function overload]/[operator][operator overload] overload resolution (via [dynamic single-dispatch][]) ++ *Observably* (modulo timings) [non-referential][] (by-value) data model encouraging (but not requiring) to use (observably) [immutable][] [objects][] using + [automatic reference counting][] (ARC) and [transparent copy-on-write][] (COW) implementation techniques ++ [Move semantics][] and [syntactic sugar][] that *emulates* [in-place][in-place updates] partial [updates][] ++ [Very high-level][] [composite][composite types] [abstract data types][] (ADTs): + - [set-theoretic operations][], [comprehensions][], and [logic quantifications][] inspired by the [math notation][] and [SETL] + - values of *any* types (as long as the former are [totally ordered][]) can be used as set elements and map keys + - [iterators][] with elements of [lazy evaluation strategy][] ++ [Modular programming][]: + - [namespaces][] + - name binding visibility control + - multiple [source files][] (plus support for [Ada]-like private program units) ++ User-defined abstract data types: + - [data encapsulation][] (with visibility control) ++ [Exception handling][] (with stack unwinding) ++ [Decimal][] [floating-point arithmetic][] ([out-of-the-box][]) ++ [Multithreading-aware][] implementation, free from [global interpreter lock][] (GIL) ++ Simple [plug-in][] [application programming interface][] (API) + +[minimalist]: //en.wikipedia.org/wiki/Minimalism_(computing) "Wikipedia: Minimalism" +[meta-circular specification]: //en.wikipedia.org/wiki/Meta-circular_evaluator "Wikipedia: Meta-circular evaluator" +[Church's]: //en.wikipedia.org/wiki/Alonzo_Church "Wikipedia: Alonzo Church" +[λ-calculus]: //en.wikipedia.org/wiki/λ-calculus "Wikipedia: λ-calculus" +[Landin's]: //en.wikipedia.org/wiki/Peter_Landin "Wikipedia: Peter Landin" +[Name bindings]: //en.wikipedia.org/wiki/Name_binding "Wikipedia: Name binding" +[static (lexical) scope]: //en.wikipedia.org/wiki/Static_scope "Wikipedia: Static scope" +[variable capture]: //en.wikipedia.org/wiki/Closure_(computer_programming) "Wikipedia: Closure" +[compile-time]: //en.wikipedia.org/wiki/Compile-time "Wikipedia: Compile-time" +[eager]: //en.wikipedia.org/wiki/Eager_evaluation "Wikipedia: Eager evaluation" +[evaluation strategy]: //en.wikipedia.org/wiki/Evaluation_strategy "Wikipedia: Evaluation strategy" +[side effects]: //en.wikipedia.org/wiki/Side_effect_(computer_science) "Wikipedia: Side effect" +[Compile-time evaluation]: //en.wikipedia.org/wiki/General_constant_expressions "Wikipedia: General constant expressions" +[Metaprogramming]: //en.wikipedia.org/wiki/Metaprogramming "Wikipedia: Metaprogramming" +[syntactic macros]: //en.wikipedia.org/wiki/Macro_(computer_science) "Wikipedia: Macro" +[macro hygiene]: //en.wikipedia.org/wiki/Hygienic_macro "Wikipedia: Hygienic macro" +[self-modifying code]: //en.wikipedia.org/wiki/Self-modifying_code "Wikipedia: Self-modifying code" +[Block-structured]: //en.wikipedia.org/wiki/Block_(programming) "Wikipedia: Block" +[expression-oriented]: //en.wikipedia.org/wiki/Expression-oriented_programming_language "Wikipedia: Expression-oriented programming language" +[procedural]: //en.wikipedia.org/wiki/Procedural_programming "Wikipedia: Procedural programming" +[imperative programming]: //en.wikipedia.org/wiki/Imperative_programming "Wikipedia: Imperative programming" +[dynamic typing]: //en.wikipedia.org/wiki/Latent_typing "Wikipedia: Latent typing" +[strong typing]: //en.wikipedia.org/wiki/Strong_typing "Wikipedia: Strong typing" +[data typing]: //en.wikipedia.org/wiki/Data_typing "Wikipedia: Data typing" +[typing discipline]: //en.wikipedia.org/wiki/Type_system "Wikipedia: Type system" +[ad-hoc polymorphism]: //en.wikipedia.org/wiki/Ad-hoc_polymorphism "Wikipedia: Ad-hoc polymorphism" +[polymorphism]: //en.wikipedia.org/wiki/Polymorphism_(computer_science) "Wikipedia: Polymorphism" +[function overload]: //en.wikipedia.org/wiki/Function_overloading "Wikipedia: Function overloading" +[operator overload]: //en.wikipedia.org/wiki/Operator_overloading "Wikipedia: Operator overloading" +[dynamic single-dispatch]: //en.wikipedia.org/wiki/Dynamic_dispatch "Wikipedia: Dynamic dispatch" +[non-referential]: //en.wikipedia.org/wiki/Reference_(computer_science) "Wikipedia: Reference" +[immutable]: //en.wikipedia.org/wiki/Immutable_object "Wikipedia: Immutable object" +[objects]: //en.wikipedia.org/wiki/Object_(computer_science) "Wikipedia: Object" +[automatic reference counting]: //en.wikipedia.org/wiki/Reference_counting "Wikipedia: Reference counting" +[transparent copy-on-write]: //en.wikipedia.org/wiki/Copy-on-write "Wikipedia: Copy-on-write" +[Move semantics]: //en.wikipedia.org/wiki/C%2B%2B11 "Wikipedia: C++11" +[syntactic sugar]: //en.wikipedia.org/wiki/Syntactic_sugar "Wikipedia: Syntactic sugar" +[in-place updates]: //en.wikipedia.org/wiki/Persistent_data_structure "Wikipedia: Persistent data structure" +[updates]: //en.wikipedia.org/wiki/Assignment_(computer_science) "Wikipedia: Assignment" +[Very high-level]: //en.wikipedia.org/wiki/Very_high-level_programming_language "Wikipedia: Very high-level programming language" +[composite types]: //en.wikipedia.org/wiki/Composite_data_type "Wikipedia: Composite data type" +[abstract data types]: //en.wikipedia.org/wiki/Abstract_data_types "Wikipedia: Abstract data types" +[set-theoretic operations]: //en.wikipedia.org/wiki/Set_theoretic_programming "Wikipedia: Set theoretic programming" +[comprehensions]: //en.wikipedia.org/wiki/List_comprehensions "Wikipedia: List comprehensions" +[logic quantifications]: //en.wikipedia.org/wiki/Quantification_(logic) "Wikipedia: Quantification" +[math notation]: //en.wikipedia.org/wiki/Set-builder_notation "Wikipedia: Set-builder notation" +[totally ordered]: //en.wikipedia.org/wiki/Totally_ordered "Wikipedia: Totally ordered" +[iterators]: //en.wikipedia.org/wiki/Iterators "Wikipedia: Iterators" +[lazy evaluation strategy]: //en.wikipedia.org/wiki/Lazy_evaluation "Wikipedia: Lazy evaluation" +[Modular programming]: //en.wikipedia.org/wiki/Modular_programming "Wikipedia: Modular programming" +[namespaces]: //en.wikipedia.org/wiki/Namespaces "Wikipedia: Namespaces" +[source files]: //en.wikipedia.org/wiki/Source_files "Wikipedia: Source files" +[data encapsulation]: //en.wikipedia.org/wiki/Encapsulation_(computer_programming) "Wikipedia: Encapsulation" +[Exception handling]: //en.wikipedia.org/wiki/Exception_handling "Wikipedia: Exception handling" +[Decimal]: //en.wikipedia.org/wiki/Decimal_floating-point "Wikipedia: Decimal floating-point" +[floating-point arithmetic]: //en.wikipedia.org/wiki/Floating-point_arithmetic "Wikipedia: Floating-point arithmetic" +[out-of-the-box]: //en.wikipedia.org/wiki/Out_of_the_box_(feature) "Wikipedia: Out of the box" +[Multithreading-aware]: //en.wikipedia.org/wiki/Thread_(computing) "Wikipedia: Thread" +[global interpreter lock]: //en.wikipedia.org/wiki/Global_interpreter_lock "Wikipedia: Global interpreter lock" +[plug-in]: //en.wikipedia.org/wiki/Plug-in_(computing) "Wikipedia: Plug-in" +[application programming interface]: //en.wikipedia.org/wiki/Application_programming_interface "Wikipedia: Application programming interface" + + +Influences +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +The following programming languages have driven major inspiration for MANOOL (in alphabetical order): + * [Ada], [APL] + * [C++], [Clojure], [CLU], [Common Lisp] + * [Forth] + * [Haskell] + * [ISWIM] + * [Kernel][] () + * [Modula-2] + * [Oberon] + * [Pascal], [Perl], [Python] + * [Scheme], [Self], [SETL2], [Smalltalk], [Standard ML] + * [Tcl] + + And the following programming languages are noteworthy anti-influences: + * [C#], [Java], [JavaScript], [PHP] + +[Ada]: //en.wikipedia.org/wiki/Ada_(programming_language) "Wikipedia: Ada" +[APL]: //en.wikipedia.org/wiki/APL_(programming_language) "Wikipedia: APL" +[C++]: //en.wikipedia.org/wiki/C%2B%2B "Wikipedia: C++" +[C++11]: //en.wikipedia.org/wiki/C%2B%2B11 "Wikipedia: C++11" +[Clojure]: //en.wikipedia.org/wiki/Clojure "Wikipedia: Clojure" +[CLU]: //en.wikipedia.org/wiki/CLU_(programming_language) "Wikipedia: CLU" +[Common Lisp]: //en.wikipedia.org/wiki/Common_Lisp "Wikipedia: Common Lisp" +[Forth]: //en.wikipedia.org/wiki/Forth_(programming_language) "Wikipedia: Forth" +[Haskell]: //en.wikipedia.org/wiki/Haskell_(programming_language) "Wikipedia: Haskell" +[ISWIM]: //en.wikipedia.org/wiki/ISWIM "Wikipedia: ISWIM" +[Kernel]: //en.wikipedia.org/wiki/Kernel_(programming_language) "Wikipedia: Kernel" +[Lisp]: //en.wikipedia.org/wiki/Lisp_(programming_language) "Wikipedia: Lisp" +[ML]: //en.wikipedia.org/wiki/ML_(programming_language) "Wikipedia: ML" +[Modula-2]: //en.wikipedia.org/wiki/Modula-2 "Wikipedia: Modula-2" +[Oberon]: //en.wikipedia.org/wiki/Oberon_(programming_language) "Wikipedia: Oberon" +[Pascal]: //en.wikipedia.org/wiki/Pascal_(programming_language) "Wikipedia: Pascal" +[Perl]: //en.wikipedia.org/wiki/Perl "Wikipedia: Perl" +[Python]: //en.wikipedia.org/wiki/Python_(programming_language) "Wikipedia: Python" +[Ruby]: //en.wikipedia.org/wiki/Ruby_(programming_language) "Wikipedia: Ruby" +[Scheme]: //en.wikipedia.org/wiki/Scheme_(programming_language) "Wikipedia: Scheme" +[Self]: //en.wikipedia.org/wiki/Self_(programming_language) "Wikipedia: Self" +[SETL]: //en.wikipedia.org/wiki/SETL "Wikipedia: SETL" +[SETL2]: //en.wikipedia.org/wiki/SETL "Wikipedia: SETL" +[Smalltalk]: //en.wikipedia.org/wiki/Smalltalk "Wikipedia: Smalltalk" +[Standard ML]: //en.wikipedia.org/wiki/Standard_ML "Wikipedia: Standard ML" +[Tcl]: //en.wikipedia.org/wiki/Tcl "Wikipedia: Tcl" +[C#]: //en.wikipedia.org/wiki/C_Sharp_(programming_language) "Wikipedia: C#" +[Java]: //en.wikipedia.org/wiki/Java_(programming_language) "Wikipedia: Java" +[JavaScript]: //en.wikipedia.org/wiki/JavaScript "Wikipedia: JavaScript" +[PHP]: //en.wikipedia.org/wiki/PHP "Wikipedia: PHP" + + +{%endraw%}{%include page_footer.md%} + + diff --git a/KSB.jpeg b/KSB.jpeg new file mode 100644 index 0000000..69bb866 Binary files /dev/null and b/KSB.jpeg differ diff --git a/MANOOL-Logo.png b/MANOOL-Logo.png new file mode 100644 index 0000000..04bb9fc Binary files /dev/null and b/MANOOL-Logo.png differ diff --git a/Manul-Cat.jpeg b/Manul-Cat.jpeg new file mode 100644 index 0000000..adfb78d Binary files /dev/null and b/Manul-Cat.jpeg differ diff --git a/Medellin.jpeg b/Medellin.jpeg new file mode 100644 index 0000000..d2fd295 Binary files /dev/null and b/Medellin.jpeg differ diff --git a/README.md b/README.md new file mode 100644 index 0000000..6ca5aae --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +This is the publishing source. + +How to test locally, e.g, on Ubuntu Linux 18.04: + + apt install ruby-full zlib1g-dev && gem install github-pages + jekyll serve --drafts -l + +and then navigate to . diff --git a/Spec.md b/Spec.md new file mode 100644 index 0000000..4fd1a3a --- /dev/null +++ b/Spec.md @@ -0,0 +1,35 @@ +--- +# Spec.md +permalink: /specification/ +title: MANOOL Specification +updated: 2019-12-21 +--- + + +{%include page_header.md%}{%raw%} + + +Contents: + +* [Introduction to MANOOL](introduction-to-manool) +* [General](general) +* Core Language + * [Syntax](core-language/syntax) + * [Semantic Concepts](core-language/semantic-concepts) + * [Compiler Dispatcher](core-language/compiler-dispatcher) + * [Program Units](core-language/program-units) +* [Standard Library](standard-library/) + * [Basic Data Types](standard-library/basic-data-types) + * [Composite Data Types](standard-library/composite-data-types) + * [Assignments, Explicit Sequencing](standard-library/assignments-explicit-sequencing) + * [Control Flow, Bindings](standard-library/control-flow-bindings) + * [Custom Data Types, Data Encapsulation](standard-library/custom-data-types-data-encapsulation) + * [Custom Notation (Syntactic Macros), Metaprogramming](standard-library/custom-notation-syntactic-macros-metaprogramming) + * [Basic Input/Output](standard-library/basic-input-output) + * [Dynamic Variables (Pointers)](standard-library/dynamic-variables-pointers) + * [Out-of-Memory Handling (Memory Accounting)](standard-library/out-of-memory-handling-memory-accounting) + * [Concurrency (Thread-Level Parallelism)](standard-library/concurrency-thread-level-parallelism) + * [Miscellaneous Features](standard-library/miscellaneous-features) + + +{%endraw%}{%include page_footer.md%} diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..75ef92f --- /dev/null +++ b/_config.yml @@ -0,0 +1,25 @@ +defaults: + - scope: {path: ""} + values: + layout: "default" + - scope: {path: "_posts"} + values: + permalink: "/blog/:categories/:year-:month-:day/:title" + - scope: {path: "_drafts"} + values: + permalink: "/blog-drafts/:categories/:title" + +kramdown: + syntax_highlighter_opts: {disable: true} + smart_quotes: ["apos", "apos", "ldquo", "rdquo"] + typographic_symbols: {ndash: "\u2014", laquo: "<<", raquo: ">>", laquo_space: "<< ", raquo_space: ">> "} + auto_id_prefix: "h:" + footnote_backlink: "" + +plugins: + - jekyll-feed + +exclude: + - CNAME + - README.md + - bin diff --git a/_drafts/article.md b/_drafts/article.md new file mode 100644 index 0000000..c2fe1e0 --- /dev/null +++ b/_drafts/article.md @@ -0,0 +1,178 @@ +--- +layout: default +title: Test' *a* < © "b" +excerpt: Blog article test. +--- + +{%include post_header.md%}{%raw%} + +## foo " bar'& "jioj" + +Blog article test. + +'"<>"& + +"[aaa -- "bbb](#— "aaa — & <> bbb'"") + +[foo](# """) + +* aaa + + bbb + + * ccc + * ddd + +----- + +Blog article test. + +> This is a quote +> # Foo Bar +> +> > Double Quote +> +> * I1 +> * I2 + +> +> * kpko k kop kop kpo kop k opk opk opk op kop k opk opk opk po kop kop kop kop kopk opk op kop kop kop kopk opk opk op kop k opk opk opk op kop ko pk +> ^ +> code +> code +> +> > * kpko k kop kop kpo kop k opk opk opk op kop k opk opk opk po kop kop kop kop kopk opk op kop kop kop kopk opk opk op kop k opk opk opk op kop ko pk +> > ^ +> > code +> > code +> +> > > * kpko k kop kop kpo kop k opk opk opk op kop k opk opk opk po kop kop kop kop kopk opk op kop kop kop kopk opk opk op kop k opk opk opk op kop ko pk +> > > ^ +> > > code +> > > code +> +> > > > * kpko k kop kop kpo kop k opk opk opk op kop k opk opk opk po kop kop kop kop kopk opk op kop kop kop kopk opk opk op kop k opk opk opk op kop ko pk +> > > > ^ +> > > > code +> > > > code +> +> end + + + +foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo +foo foo foo foo foo foo + +* `scheme` + + foo bar + +* `scheme` + + foo bar + + + + +--- + +foo|b|c +-|:-|- +1|bar|*3* +4|`i`i|6 + + +foo bar +baz +: definition + + another def + +Polmorphic operations: + +* + code foo bar + + + --- returns... + +* + code foo bar + + --- returns... + +
Image with scrolling
+ +
+

+    
+      
+      
+    
+    
+      
+      
+      
+    
+  
Foo kokkopk opkopkBarFaz
FooBar kok okpk kopkk opkopkFaz
+
Table with scrolling
+
+ + + +> foo bar +> +> pre pre +> pre pre +> +> baz baz +> baz baz + + + + +joijjiojifijtgjit5jgiojt5ijgiot5jigojio5tgijti5jgio5tgjjio5tgijtjigiotjg5tgjioj5tigjiotjgijt5ijgioj5iogjiotojo5joijjiojifijtgjit5jgiojt5ijgiot5jigojio5tgijti5jgio5tgjjio5tgijtjigiotjg5tgjioj5tigjiotjgijt5ijgioj5iogjiotojo5 + + +{%endraw%}{%include post_footer.md%} diff --git a/_drafts/cfp1.md b/_drafts/cfp1.md new file mode 100644 index 0000000..3dfd440 --- /dev/null +++ b/_drafts/cfp1.md @@ -0,0 +1,147 @@ +--- +title: Emerging Programming Language with a Translator Under 10K Lines of Code +excerpt: CFP 1 +--- + +{%include page_header.md%}{%raw%} + + +Abstract +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Some well known families of programming languages make particularly *simple* implementations possible: Forth, Lisp, Tcl, Smalltalk, etc., which is good. +I argue that almost none of them have the syntax and/or semantics close enough to those of more conventional languages, such as Python or Java on one hand and C +or Ada on the other hand, which is not so good (since RPN and S-expressions are unconventional, array manipulation in Tcl is tricky, and so on). +The programming language MANOOL is my attempt to address this issue, and the language differs in a number of details from some other less known and *simple* +languages that solve the same problem. + +Being a one-person project, MANOOL is designed around one central idea: to maximize the _expressive power_ / _implementation complexity_ ratio. +This implied the goal of providing expressive power of certain (present and past) popular languages, some of which are mentioned above, but I also managed to +develop a translator of MANOOL in less than 10 KLOC in C++11 (yet it exhibits competitive run-time performance compared to, say, the CPython implementation of +Python). + +MANOOL is meant to be a *practical* tool rather than a research project (of course prototype language implementations in just a few LOC are broadly known). + +You will learn what the design process for such a project looks like, what is the scope of the language in terms of its features, and what are the simple and +straightforward implementation techniques that contribute to its relatively high run-time performance. + +About the presenter +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +**Alexey Protasov** + +Alex is an enthusiastic independent developer with Russian origins (from Saint Petersburg) living in Medellin, Colombia. +He constantly dreams with "better" programming languages and has over 30 years of experience with designing and implementing languages and development tools --- +he had in the past a shareware visual programming tool, worked in the area of compilers at Intel and Sun Microsystems, and taught compiler construction and the +theory of formal languages at a university. + +Alex speaks Spanish, English, and Russian. +When he is not working on MANOOL, he likes swimming, traveling, and dancing (Salsa, Porro, Merengue, Cumbia, etc.). + +What will the attendee learn? --- Comments for reviewers +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +### Overview ########################################################################################################### + +MANOOL is a *homoiconic*, *dynamic*, and *multi-paradigm* *general-purpose* programming language with a *functional* core. +Its translator (published under GPLv3) is made for native-code run-time environments, is written in idiomatic C++11 (with a few industry-acknowledged +GCC-specific extensions), and currently runs under several Unix-like operating systems on a number of instruction-set architectures. + +MANOOL happens to be a close relative of Lisp-family languages but not by how programs in MANOOL look (its surface syntax is distinct from the syntax of +S-expressions). +Also, unlike Lisp(s) it has what I call a _non-referential data model_ (see below). + +### Design goals ####################################################################################################### + +MANOOL has explicitly stated design goals (ordered according to priority), which are + 1. implementation simplicity (which is the sole most important consideration in the design); + 2. expressive power (in practical sense), usability, and general utility (value for consumers); attention to syntax and semantics details; + 3. correctness, security, and overall quality of implementation; run-time reliability; + 4. run-time performance and scalability; and + 5. consistency, completeness, orthogonality of features and language elegance; conceptual economy and purity. + +Note that implementation simplicity is not only important to make the project a viable option for just a single developer but also indirectly contributes to the +*quality* of implementation. + +### Syntax ############################################################################################################# + +Starting right from the syntax, MANOOL is the result of many design trade-offs, and its syntax as such is not ideal (after all, it may still seem to be a bit +surprising to some newcomers). +So, in this talk I'll **explain** why it's important (to achieve the implementation simplicity) for the syntax of MANOOL to remain the way it is (especially +considering some basic constraints such as a limited source character set). + +As a crude illustration, a "Hello, world!" program in MANOOL might look like + + {{extern "manool.org.18/std/0.6/all"} in WriteLine[Out; "Hello, world!"]} + + and an expression that evaluates to a recursive factorial function might look like + + { let rec + { Fact = + { proc { N } as -- precond: N.IsI48[] & (N >= 0) + : if N == 0 then 1 else + N * Fact[N - 1] + } + } + in + Fact + } + +### Non-referential data model ######################################################################################### + +The following code fragment is valid in a large number of programming languages (ignoring minor syntactic differences): + + A[1] = 1; B = A; B[1] = 2 + + However, there are two possible outcomes after executing it (and hence, two classes of languages): + * `A[1] == 1` --- non-referential data model used by such languages as C (for `struct`s) and Ada (and MANOOL) and + * `A[1] == 2` --- referential data model used by such languages as Python and Java. + +We'll **talk** about flaws of the purely referential data model, good reasons because of which it has been there in a historical perspective (since Lisp and +later, CLU), alternatives to it adopted in different families of languages, and performance limitations of the simple (and semantically consistent) solution +adopted in MANOOL. + +### Features ########################################################################################################### + +I'll also at least **touch** all other language feature (apart of those marked with *), which are (according to the official language introduction) + * Very compact (or even minimalist) core language (up to a point where a meta-circular specification might be appropriate) + * Convenient standard library (but completely optional to use) + * Computational primitives based on Church's lambda-calculus (in the spirit of Landin's ISWIM prototype language/ML-like languages) + - Name bindings with static (lexical) scope + - Explicit variable capture and classification of name bindings into compile- and run-time + - Mainly eager (strict) evaluation strategy (from the perspective of lambda-calculus) with possible side effects + * Compile-time evaluation + * Metaprogramming: + - Lisp-like syntactic macros with optional macro hygiene + - (optionally) self-modifying code + * Block-structured and expression-oriented (from the perspective of procedural imperative programming) + * Dynamic (latent) but strong data typing discipline + * Ad-hoc polymorphism: + - run-time function/operator overload resolution (via dynamic single-dispatch) + * Observably (modulo timings) non-referential (by-value) data model encouraging (but not requiring) to use (observably) immutable objects using automatic + reference counting (ARC) and transparent copy-on-write (COW) implementation techniques (*) + * Move semantics and syntactic sugar that emulates in-place partial updates (*) + * Very high-level composite abstract data types (ADTs): + - set-theoretic operations, comprehensions, and logic quantifications inspired by the math notation and SETL + - values of any types (as long as the former are totally ordered) can be used as set elements and map keys + - iterators with elements of lazy evaluation strategy + * Modular programming: + - namespaces + - name binding visibility control + - multiple source files (plus support for Ada-like private program units) + * User-defined abstract data types: + - data encapsulation (with visibility control) + * Exception handling (with stack unwinding) + * MANOOL programs can be instructed to recover even from dynamic memory (heap and stack) exhaustion + * Decimal floating-point arithmetic (out-of-the-box) + * Multithreading-aware implementation, free from global interpreter lock (GIL) + * Simple plug-in application programming interface (API) + +### Run-time performance ############################################################################################### + +And finally we'll **talk** about the simple and relatively straightforward techniques that allowed me to achieve competitive run-time performance (such as VM +operation fusion, which reduces the number of dynamic dispatches, i.e. branches, and careful design of internal VM interfaces and data structures) and +**review** some numbers to compare the implementation performance-wise with other engines. + + +{%endraw%}{%include page_footer.md%} diff --git a/_drafts/cfp2.md b/_drafts/cfp2.md new file mode 100644 index 0000000..2a3b209 --- /dev/null +++ b/_drafts/cfp2.md @@ -0,0 +1,82 @@ +--- +title: Native Run-time Performance for Dynamic Programming Languages +excerpt: Presentation talk for the [StrangeLoop](https://www.thestrangeloop.com) conference Sep-Oct'21 (CFP submission) +hidden: false +--- + +{%include page_header.md%} + + +*{{page.excerpt}}* + +Abstract +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Much has been told about advantages and disadvantages of static vs dynamic type checking, without reaching a definitive conclusion. Being a low-budget project, +the programming language MANOOL has to seek major conceptual economy, leading to a compact implementation. For instance, MANOOL is a homoiconic language (like +Lisp or Tcl). On the other hand, its design and implementation simplicity is the most compelling reason for the dynamic typing choice. + +Static type checking is often associated with languages that admit high-performance implementations (e.g., C or Java), but this does not always has to be the +case. For instance, sophisticated dynamic specialization (e.g., in case of JavaScript/V8 or LuaJIT) bridges the performance gap between static and dynamic +typing languages. However, such techniques may be prohibitively expensive for a small project like MANOOL, while being still insufficient to reach the highest +possible run-time performance (achievable for C/C++). + +Despite of dynamic typing, MANOOL is specifically designed to admit effective static optimizations. While not a new idea (e.g., Julia has the necessary +properties for that), it seems that it has not been duly explored in mainstream programming yet (and hopefully, MANOOL has a more general-purpose design than +Julia). + +You will learn about the overall architecture of MANOOL and its compiler, the intermediate representations, the sources of inefficiencies in high-level dynamic +languages (including the dynamic typing), and how they can be overcome. No prior knowledge of modern compiler technology is required. + +About the presenter +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +**Alexey Protasov** + +Alex is an enthusiastic independent developer with Russian origins living in Medellin, Colombia. He constantly dreams with "better" programming languages and +has over 30 years of experience with designing and implementing languages and development tools -- he had in the past a shareware visual programming tool, +worked in the area of compilers at Intel and Sun Microsystems, and taught compiler construction and the theory of formal languages at a university. + +Alex speaks Spanish, English, and Russian. When he is not working on MANOOL, he likes swimming, traveling, and dancing (Salsa, Porro, Merengue, Cumbia, etc.). + +What will the attendee learn? --- Comments for reviewers +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Presentation outline: + +* Introduction + * About the author, the current status of MANOOL, references and contact details + * When language implementation run-time performance is relevant and when it is not + * Own middle-end/back-end rationale (instead of LLVM) + * SSA (static single assignment form), to enable advanced data and control flow analysis +* Language and compiler architecture + * Scanner and parser + * The compiler core dispatcher + * Optimization and code generation pipeline + * Object module linker/loader (for separate compilation) +* Intermediate representations + * Abstract syntax tree + * Compiled tree representation + * Register machine + * Object modules (persistent cache) +* Sources of inefficiencies in high-level dynamic languages + * Dynamic dispatch by type (dynamic overloading) and dynamic type checking + * Dynamic specification of polymorphic operations and composite member names (in case of MANOOL) + * Value (un)boxing (pointer dereferencing), reference counting, and dynamic memory allocation (from heap) +* Middle-end (analysis and transformations) + * To-SSA transformation + * Type inference via inter-procedural conditional constant propagation (IPSCCP, a bit more complete than in LLVM), inlining, constant folding + * Copy propagation, dead code elimination, including unused and unreachable code + * Jump optimizations (basic block merging and jump threading) + * Aliasing analysis, elimination of redundant load/store, reference counting, dynamic memory allocations + * Other analysis/transformations (GVN, CSE, PRE, strength reduction, loop unrolling, invariant code motion, autovectorization, etc.) +* Back-end (code generation) + * Out-of-SSA transformation and register allocation + * Instruction selection, peephole optimizations, etc. +* Test case (Game of Life) + * Code + * Optimizations +* Conclusions + Questions & Answers + + +{%include page_footer.md%} diff --git a/_drafts/new_intro.md b/_drafts/new_intro.md new file mode 100644 index 0000000..ab985a2 --- /dev/null +++ b/_drafts/new_intro.md @@ -0,0 +1,148 @@ +--- +title: The Programming Language MANOOL +excerpt: The Programming Language MANOOL +--- + +{%include page_header.md%} + + +#### What is the purpose of MANOOL? + +MANOOL is a general-purpose language suitable for diverse programming tasks from several problem domains. However, it has substantial bias toward scripting and +hopefully represents an evolutionary step over existing scripting languages. Thus, MANOOL should be compared to and can serve the same purpose as Python, Ruby, +PHP, Scheme, JavaScript, or Lua, which implies properties typical for such languages, e.g.: short edit-compile-test development cycle (at least for code bases +under 1 MLOCs) and run-time type checking. + +#### What real-world problem does your project solve? + +MANOOL is intentionally designed in such way that it represents a specific point on a continuum; on one side of this continuum are high-level languages designed +with the programmer's convenience and productivity in mind, and on the other side are languages designed with execution speed and/or solution scalability in +mind (whereas, as a matter of fact, software quality and reliability may correlate with either of those extremes, depending on the situation). Based on my past +programming experience, I argue that no existing mainstream language addresses both goals at once in a balanced way. As a result, programs are either more +expensive in development than they should be or do not scale with workload as needed. + +Think, for instance, about the number of registered users and their contributions on an in-house social-network Web-site. Working as a server infrastructure +administrator, on multiple occasions I saw serious scalability issues to pop up suddenly after a year of production use due to flaws in backend software +architecture, including the choice of programming language and/or its implementation. + +As a more elaborate illustration, using a rigid static type system and gratuitous sharing mutable state in the program (widespread in traditional OOP) is far +from how many people think about problem solving in terms of basic (immutable) mathematical objects, and thus this places undue cognitive load on developers for +simple backend scripting. On the other hand, implementing backend algorithms in an average exploratory-programming language (especially in a straightforward +and/or idiomatic way) often leads to poor run-time performance scalability w.r.t. Web-site workload growth. + +#### OK, but what warrants a whole new language in case of MANOOL? + +Starting off with some relatively unpopular language makes little sense for me as a language designer, since I might miss in this case some improvement +opportunities (whatever it means) while getting almost nothing in return. But why not just extend an existing mainstream language to suite the above stated +goals instead of creating one from scratch? + +Achieving competing (and even incompatible) goals is hard and may lead to overly complex and difficult to adopt language designs. MANOOL leverages two +principles in order to deal with this problem, which are currently not observed among mainstream languages: + +* open (homoiconic) approach to language architecture (in the same sense as in Lisp but using a notation alternative to S-expressions and with macro definitions + applying to their own limited scope), and +* primarily value (non-referential) semantics with copy-on-write policy under the hood and move operations (and this works even for user-defined abstract data + types, due to availability of special syntactic sugar). + +This requires things to work slightly differently on the very basic level, which suggests that introducing a whole new language is more appropriate than trying +to extend an existing one. + +#### Why should I learn MANOOL? + +It depends on who is asking. One possible reason is that playing around with MANOOL means joy and fun that existing mainstream languages can hardly offer to +you. E.g., in brief: + +* Assuming `A == (array of 1 2 3)`, after `B = A; A[1] = 0`, `B[1] == 2`. Likewise, after `B = A; B[1] = 0`, `A[1] == 2` (value semantics). + +* On the other hand, `A[1] = 0` and `S = S! + "Hi"` may have (amortized) O(1) run-time complexity (thanks to move operations). + +* `A[1] = 0` is actually equivalent to `A = A!.Repl[1; 0]`, and in other contexts `A[1]` is equivalent to `A.Apply[1]` (unifying syntactic sugar). + +* Incidentally, `A.P[...]` is equivalent to `P[A; ...]`, which could also be written simply as `(P A; ...)` (more syntactic sugar). + +* You can construct and index into a key-value mapping with sets as keys. After + + M = (map of (set of 1 2 3) = 1; (set of 4 5 6) = 2) + + `M[(set of 4 5 6)] == 2` (no arbitrary restrictions on keys or their type, which is in a sense a consequence of value semantics). + +* You can write the whole program unit in some domain-specific language instead of standard MANOOL. Just replace `(extern "...")` (see below) with the reference + to a module. + +* On the other hand, macro bindings have limited scope (like any other kind of bindings): + + (let (unless = (macro: proc (F) as ...)) in ... (unless ...) ...) + +* First-class value bindings involve compile-time evaluation, and similarly you can use handful syntactic sugar to specify literal values, e.g.: `F64["1.1"]$`, + `D128["1.10"]$`. + +* A module can be introduced on a program unit level by the construct `(let (...) in: export ...)` or, equally, be bound to a name and thus become a local + module (à la Modula-2): + + (let (M = (let (...) in: export ...)) in ... (M in ...) ...) + +* Polymorphic operations are indistinguishable from normal (first-class) procedures (and at the same time they are just symbols): + + (var (Plus = (+)) in ... 1.Plus[1] ... "Hi".Plus["World"] ... Out.Write[Plus] ...) + +* Programs can recover from out-of-memory conditions gracefully and reliably: + + ReserveHeap[...]; (on HeapExhausted do ... after ...) + +#### What does it offer to potential project maintainers and contributors? + +MANOOL is a personal, solo-developer project with severely limited resources. Thus, to be viable, it almost inevitably has to use a straightforward, +streamlined, and modular implementation, which is based on simple algorithms and data structures (from the compiler theory standpoint). Let's take, for +instance, the implementation size -- the MANOOL translator is written in under 10 KLOCs, whereas the most widespread Python interpreter builds upon at least 100 +KLOCs. + +This does not necessarily mean that the MANOOL implementation is cheap or otherwise low-grade but rather that extra development effort can be committed to +ensuring high implementation quality and reliability. This also implies lower project entry requirements, encouraging more people to participate in the +development. Besides, such compact code bases are more suitable for educational purposes (than larger ones, which are often full of legacy stuff). + +#### Give me a complete example of what code in MANOOL may look like + +A "Hello World" program might look like + + ((extern "manool.org.18/std/1.0/all") in Out.WriteLine["Hello, world!"]) + +(using the second version of the syntax, see below). And in the following sample program a recursive factorial function is defined and invoked: + + ( (extern "manool.org.18/std/1.0/all") in + : let rec (Fact = (proc (N) as + : if N == 0 then 1 else N * Fact[N - 1])) + in + Out.WriteLine["Factorial of 10 = " Fact[10]]) + +#### What's next? Do you have a roadmap for MANOOL? + +Sure, here it is (*as of September 2021*): + +1. Complete a JIT compiler for MANOOL to achieve run-time performance only marginally slower than that of the most sophisticated dynamic-language engines in the + market (such as V8 and LuaJIT), but only at a fraction of their complexity. + +2. Replace `{` and `}` in the syntax by `(` and `)` in the second version of the language. The idea is to to appeal more to at least one established language + community (Lisp/Scheme) (although at the cost of extra complexity, including a more complicated LL(2) parser). + +3. Complete the language specification and the tutorial. + +4. Build a MANOOL ecosystem (libraries) and a user community. + +--- +--- +--- + +#### What's next? Are there any plans for future development? + +*[As of September 2021]* + +Although MANOOL has a high-level dynamic semantics, which normally means quite low run-time performance, the goal for the upcoming version of the language and +its translator is to achieve the one-fold boost of performance, which is going to be only marginally below that of the most sophisticated, high-end +dynamic-language engines in the market (such as V8 and LuaJIT), but only at a fraction of their complexity. + +Another notable change involves using normal parentheses in place of curly braces, mostly in order to appeal more to at least one existing language community +(Lisp/Scheme), although at the cost to using a more complicated LL(2) surface grammar (after usual transformation by left-recursion elimination) and more +complicated (but still reasonable) rules for the programmer. This change is actually reflected in code samples in this wrap-up. + + +{%include page_footer.md%} diff --git a/_includes/page_footer.md b/_includes/page_footer.md new file mode 100644 index 0000000..8af2c47 --- /dev/null +++ b/_includes/page_footer.md @@ -0,0 +1,12 @@ +{%comment%} _include/page_footer.md {%endcomment%} + + +
+ +
Updated:
+ + * footnotes + {:footnotes} + +
+ diff --git a/_includes/page_header.md b/_includes/page_header.md new file mode 100644 index 0000000..f9e62a6 --- /dev/null +++ b/_includes/page_header.md @@ -0,0 +1,8 @@ +{%comment%} _include/page_header.md {%endcomment%} + + +
+

{{page.title|smartify}}

+
Updated:
+
+ diff --git a/_includes/post_footer.md b/_includes/post_footer.md new file mode 100644 index 0000000..b6e27cc --- /dev/null +++ b/_includes/post_footer.md @@ -0,0 +1,18 @@ +{%comment%} _include/post_footer.md {%endcomment%} + + + +* * * * * +**The end** --- Questions? --- Email me: + +
+ +
Published:
+
Updated:
+ + * footnotes + {:footnotes} + +
+ + diff --git a/_includes/post_header.md b/_includes/post_header.md new file mode 100644 index 0000000..e5d841e --- /dev/null +++ b/_includes/post_header.md @@ -0,0 +1,28 @@ +{%comment%} _include/post_header.md {%endcomment%} + + + + + +
+

{{page.title|smartify}}

+
Published:
+
Updated:
+
+ + diff --git a/_includes/spec_footer.md b/_includes/spec_footer.md new file mode 100644 index 0000000..5598f36 --- /dev/null +++ b/_includes/spec_footer.md @@ -0,0 +1,12 @@ +{%comment%} _include/spec_footer.md {%endcomment%} + + +
+ +
Updated:
+ + * footnotes + {:footnotes} + +
+ diff --git a/_includes/spec_header.md b/_includes/spec_header.md new file mode 100644 index 0000000..8953fce --- /dev/null +++ b/_includes/spec_header.md @@ -0,0 +1,14 @@ +{%comment%} _include/spec_header.md {%endcomment%} + + +
+ +

{{page.title|smartify}}

+ + * TOC + {:toc} + +
Updated:
+ +
+ diff --git a/_layouts/default.html b/_layouts/default.html new file mode 100644 index 0000000..cfd96c1 --- /dev/null +++ b/_layouts/default.html @@ -0,0 +1,97 @@ +{%comment%} _layouts/default.html +{%endcomment%} + + + + {{page.title|append:" -- MANOOL (manool.org)"|smartify}} + + + + + + + + + + + + +{%feed_meta%} + + + +
+
+
+ +
MANOOL
+
+
+ {{"“"|smartify}}MANOOL is Not an Object-Oriented Language!{{"”"|smartify}} +
+
+ +
+
+
+ +{{content}} + +
+
+ + + diff --git a/_posts/2020-01-07-manool-practical-language-with-universal-syntax-and-only-library-level-features.md b/_posts/2020-01-07-manool-practical-language-with-universal-syntax-and-only-library-level-features.md new file mode 100644 index 0000000..02bf932 --- /dev/null +++ b/_posts/2020-01-07-manool-practical-language-with-universal-syntax-and-only-library-level-features.md @@ -0,0 +1,800 @@ +--- +title: MANOOL --- Practical Language with Universal Syntax and Only Library-Level Features (Except One) +updated: 2020-01-14 +excerpt: >- + Have you ever said to yourself?: "This new version of _my favorite programming language_ is great, but why on earth did they introduce the feature _X_? I + don't need it. The language has become now more bloated than ever!" +--- +{%include post_header.md%}{%raw%} + + +First, I have to make two disclaimers: + +In this article I do *not* intend to propose "the next greatest" minimalist Turing-complete programming language; we already have at least two of them: [Iota +and Jot] (based on combinatory logic)[^a1]. These are extreme, "esoteric" examples that show that language minimalism itself may be a useless, even absurd +design goal from the practical standpoint. So, this article focuses on something more practical --- I rather try to show that (a kind of) minimalism may help to +achieve *other* attractive goals. + +[^a1]: With the same success, we could just view *any* existing Turing-complete programming language _L_ as having only two operators --- `0` and `1` --- `0` + appends the digit "0" to a buffer, whereas `1` appends "1", and every eight digits the abstract machine feeds the corresponding octet to a translator of + _L_, et voilà. + +This article talks about *simple*, cost-effective solutions in the spirit of MANOOL design, easy to implement and easy to master, although they may be a bit +incomplete (which is aligned with the _Pareto principle_, also known as the _80/20 rule_). Thus, I do not pretend to make scientifically rigorous claims here +since ultimately, simplicity is difficult to formalize. + +In other words, although I strove to align the article and the entire MANOOL design with the established theoretic computer science formalisms and terminology, +this article is *not* about computer science but rather software engineering arts. However, it *does* talk (hopefully, in an entertaining form) about how some +phenomena and connections between them predetermine some key aspects of the architecture of the programming language MANOOL (thus, this article is hopefully +neither a goal-oriented technical manual nor a meaningless advertisement). + +Also, to avoid any misunderstandings, let's agree on some important definitions that hold throughout this article: + +> A _syntax_ of a formal language (such as a programming language) is a set of rules that determine how to discover a _syntactic structure_ of any phrase in +> that language, where a syntactic structure of a phrase is a tree-like (i.e., hierarchical) structure induced on that phrase (in the form of a character +> string) that enables formulation of the _meaning_ of the phrase (or a constituent phrase thereof) by means of a straightforward recursive definition.[^a2] +> Sometimes different syntaxes may adequately describe the same programming language, and the syntax is normally specified by a context-free grammar plus +> lexical rules. +> +> [^a2]: For instance, according to these definitions, the syntax of [Lisp]-family programming languages ([Common Lisp], [Scheme], [Clojure], [Kernel], etc.) is +> considered to be *exclusively* the syntax of S-expressions (sometimes called the surface or concrete syntax), regardless of any further (structural) +> requirements placed on the corresponding Lisp data (i.e., the abstract syntax trees), since in practice, the later provide sufficient guidance to +> deduce the meaning of a phrase in Lisp by *straightforward recursion* (anyway). +> +> A _semantics_ of a formal language is a set of rules that determine how to discover the meaning of any phrase in that language. + +(See [Definitions](/specification/general#h:definitions "MANOOL Specification: Definitions")) + +Note that these definitions may be at odds with your intuition and may slightly differ from the corresponding definitions as they appear in some other contexts +(such as specifications of other programming languages, theoretic and applied linguistics, etc.). + +And now, let's start ... + + +General +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +### The problem --- feature bloat ###################################################################################### + +Have you ever said to yourself?: +> This new version of _my favorite programming language_ is great, but why on earth did they introduce the feature _X_? I don't need it. The language has become +> now more bloated than ever! + +or +> This new version of _my favorite programming language_ is great, but it would be even better if they introduced the feature _Y_. + +See the pattern? --- In one case you're complaining about the introduction of a new feature _X_, whereas in another case you're doing (exactly) the *opposite*: +requesting a new feature _Y_ (which might happen to be the feature _X_ from another user's standpoint). + +And since we all have different problems at hand and therefore different needs, in the course of programming language evolution, satisfying the needs of some +language users eventually implies disappointing other users (those who request new features as well). So, can we break this vicious circle? + +### Summary --- solution in MANOOL ##################################################################################### + +The answer of MANOOL to the above question is "Yes, we can!". For instance, in MANOOL there are + * *no* conditionals (`if ... then`, ...) built into the language core, + * *no* λ-expressions (`proc ... as`, ...), + * *no* binding constructs (`let ... in`, ...), + * *no* floating-point literals (`F64[...]$`, ...), etc.; + + instead, all of the above are just _library features_ (like, say, standard mathematical functions). You normally *import* them *explicitly* from a standard +library _module_ into the current _binding environment_ in order to be able to use them, but this is completely optional --- with the same success, you can +write and import from your own similar modules instead, and as the language evolves, different standard library _versions_ can be easily provided (based on the +same code base). In the same way, several language _dialects_ and/or _sublanguages_ (including _domain-specific_ ones) can be provided in the form of library +modules and can even be *mixed* in the same region of the program unit (provided all naming conflicts, if any, are resolved somehow) or used separately from +within different regions. + +Incidentally, the *only* built-in, predefined construct in MANOOL is `extern`, which is used to reference some external _entity_ (i.e., either a _first-class_ +value or a special, _non-value_ entity, such as a standard library module, or a special, non-value entity that the keyword `if` denotes). Actually, there are +also some _core features_, like + * the ability to express at least some values literally, + * the ability to use infix operators and to explicitly group subexpressions syntactically, + * the ability to call procedures with arguments, etc.; + + however, they + * are very, very basic and much more abstract, + * are not even required to form a full basis for a Turing-complete programming language, and + * are expected to evolve *very, very slowly*, if at all. + +Of course, in practice, any such system inevitably has its *limitations* and *scope of applicability*, since every programming language is a result of many +design trade-offs.[^b1] Also, this is not a completely new approach --- there are some related studies in theoretic computer science, and there were a few +intents to design *other* programming languages with library-only functionality (sometimes, but not always, qualified as _extensible_).[^b2] Unfortunately, *no* +such language can be considered mainstream nowadays (and I *do* aim MANOOL to be a practical tool and maybe to become kind of mainstream some day); thus, I +invite you to discover *yourself* whether the approach and especially the limitations of MANOOL make sense for you. + +[^b1]: Strictly speaking, no Turing-complete language _L_ has such limitations since we could always implement in the language _L_ another Turing-complete + language _L'_ that would lack limitations from any chosen set of limitations, but I am talking here about cost-effective use cases only (i.e., in + practical sense). + +[^b2]: For instance, at least one dialect of the programming language Scheme provides almost empty top-level binding environment by default. But MANOOL is not + Scheme --- although it has a similar architecture, it has at least a distinct syntax and a distinct data model, among other things. + +The MANOOL approach involves three aspects: + * a [universal syntax] --- an analog of the syntax of S-expressions (in Lisp-family programming languages); + * a [module system] --- a set of mutually orthogonal features provided (in turn) mainly by the standard library; and + * a (very) simple and compact semantic analysis and code generation [core dispatcher]. + +Further in this article, I explain what a universal syntax *is* and provide a few examples of programs in MANOOL for illustration purposes, although +unfortunately, the article has *no* room for a detailed description of the MANOOL syntax as such, its advantages, drawbacks and/or the rationale (apart from a +simple reproduction of the grammar). Also, I provide some examples that support the above claims about feature _composability_ and _modularity_ of the language +but do *not* provide a detailed description of the MANOOL module system. Both topics are two whole new stories that deserve separate articles, which I'll +publish in the future, so if you are curious, stay tuned ... + +I also touch in this article the closely [related subjects][homoiconicity] of homoiconicity, metaprogramming, and syntactic macros (for the case of MANOOL). + +### Further motivation ################################################################################################# + +Let's see what may happen if we just put up with language bloat ... + +Let's assume that C++98 is an *extension* of C. Remember the famous issue with the C++ grammar? --- What does the following declaration in C++ mean?: + +> _t_1 _a_`(`_t_2`()``)``;` + + Is _a_ declared as an object of type _t_1 and initialized with the value of a value-initialized object of type _t_2, or is it a function +returning a result of type _t_1 and taking as an argument a pointer to a function without parameters returning a result of type _t_2? + +The above code exposes a parsing conflict. Of course, the C++ specification still tells us that _a_ *is a function*, but this is because the specification has +additional conflict-resolution provisions; in fact, no (Chomsky-style)[^b3] grammar can unambiguously capture the distinction between the two situations (_a_ is +an object vs. _a_ is a function). And even though we can always resolve parsing conflicts in some way, I argue that the resulting language may become then +needlessly *irregular*, *inconsistent*, *complex*, and *unintuitive* due to additional (ad hoc) conflict-resolution rules. + +[^b3]: Some grammar formalisms distinct from Chomsky's grammars intrinsically support conflict-resolution policies, but I argue that this does *not* solve the + problems we are talking about here. + +But *how often* the above situation arises in practice? --- Well, the C++ standard committee made this mistake in C++98 and suggested a fix in C++11 via the +uniform initialization notation. Also, while I was playing around with LALR(1)-grammars for MANOOL, I realized that it was too easy to accidentally introduce +grammar ambiguities (especially if we are short of terminal symbols to play with). It is even more easy to come to a grammar that is unambiguous but does not +fall into the (cost-effective) LALR(1) class (which is undesirable). + +The above illustrates that we should *not* ignore language bloat (which is inherently unavoidable) since it may cause real problems and may be harmful if we +do not mitigate the related issues somehow. Besides, the situation would be even worse if instead of language evolution and extensibility, we were talking about +modularity and composability (out of sublanguages). + +### State of the art ################################################################################################### + +There are usually two kinds of features in a programming language: + * those that correspond to some grammar production (such as conditionals and name bindings) and + * those that have a name in the standard library (such as standard mathematical functions). + + As shown above, _syntactic conflicts_ are tricky to avoid and tricky to manage. On the other hand, _naming conflicts_ can be more plausibly resolved via name +spacing and/or aliasing. So, what if we could *unify* both kinds of features and provide *all of them* just using names (or as they say, bind them to names)? + +There is at least one language that excels at such unification, [Kernel],[^b4] but the problem with Kernel is that its computation model seems to be almost +inevitably inefficient because in Kernel actions that are otherwise performed *once*, during what would be called a compilation phase,[^b5] are performed +*repeatedly*, every time an expression is evaluated (in the course of program execution). + +[^b4]: Other possible examples are [Forth], [Tcl], and other dialects of [Lisp], but Kernel may be the most sophisticated of them. Besides, Forth is too + low-level and Tcl leads to inevitably inefficient implementations. + +[^b5]: ... such as checking of presence and placement of certain syntactic structures, (almost unavoidable) linear-time lookup of identifiers, etc. ... + +A simple solution to that problem consists in designing a language in such a way that such activities are explicitly factored out into a separate compilation +phase. And here we come to a translation/execution scheme that resembles in many ways the usual evaluation model of any dialect of Lisp.[^b6] Compared to +Kernel, a (small) price is that in some cases names are now bound to _second-class_ entities (which cannot be freely operated with at run-time), but we still +enjoy a unified approach (at 80%). + +[^b6]: They say that whoever does not understand Lisp is doomed to reinvent it ... Still, MANOOL does differ from all existing Lisp-family programming languages + in a number of important aspects. + +Note that there also exist quite a few studies about general context-free grammar composability, although MANOOL is based on a simpler approach to the problem +of feature composition (see also [Background theory]). + +### Example ############################################################################################################ + +Almost every working example from the MANOOL [tutorial](/tutorial/ "MANOOL Tutorial") contains near its beginning an instance of the `extern` construct (i.e., +`{extern "..."}`), e.g.:[^b7] + + {{extern "manool.org.18/std/0.6/all"} in Out.WriteLine["Hello, world!"]} + + where the standard library module is identified by a special external entity _path_ specified as a compile-time expression that evaluates to a string (i.e., +`"manool.org.18/..."`). As you may note, a global name hierarchy is proposed based on the global Internet domain name system, which is a convention for some +other programming languages (here, `18` means the year of manool.org domain registration --- 2018). + +[^b7]: All examples in MANOOL presented in this article are complete, working examples that you can submit to the + [online evaluator](/eval "MANOOL Online Evaluator") to test. + +The importing part is `... in ...`, which is used here to "apply" the referenced external module (a standard library module in this case) to the body expression +after `in`, making that expression to be compiled in the binding environment augmented with all name bindings the module provides. When evaluated, the body +expression will transmit the `"Hello, world!"` string to the standard output stream of the current process. + +In fact, the *only* standard library member used in the previous example is `Out`, which represents the standard output. `WriteLine` here is *not* a +library-supplied feature at all since it denotes a so-called _polymorphic operation_ (invoked in this case on `Out`), which in MANOOL is deliberately the same +thing as a value of type Symbol (specified literally here), which can be applied to arguments as a procedure or any other procedure-like value. + + +Mechanisms and Techniques +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +### Background theory ################################################################################################## +[background theory]: #h:background-theory "Below: Background theory" + +Together with the problem of programming language extensibility, let's consider also a broader problem of language modularity and composability (out of +sublanguages). First, here are some quick well-known facts from the programming language theory: + +1. In order to be able to describe (in *finite* terms) the meaning of an *infinite* number of phrases (programs), the meaning of a phrase _φ_ is + determined as some composition _M_ of meanings of its constituent phrases _φ__i_ + with respect to some given set of phrase-formation operations: + \[\[_φ_\]\] = _M_(\[\[_φ_0\]\], \[\[_φ_1\]\], ..., \[\[_φ__n_-1\]\])[^c1] + --- divide, and you'll master the infinity! + +2. Thus, a phrase is either an elementary phrase (_p__i_) or a composition of its constituent phrases _φ__i_ with respect to the + phrase-formation operations mentioned above. + +3. In case a phrase is a character string, the elementary phrases (_a__i_) consist of individual characters, and the phrase-formation operation is + just the string concatenation: + _α_ = _α_0_α_1..._α__n_-1. + +4. In case a phrase is given in the form of a term (a tree) instead, the elementary phrases are nullary terms (_t__i_), and the phrase-formation + operations are term constructors: + _τ_ = _f_(_τ_0, _τ_1, ..., _τ__n_-1) for each non-nullary functor _f_. + +5. The fundamental difference between the two phrase spaces (string and term) is that in contrast to term decomposition, string decomposition is *ambiguous* + with respect to its phrase-formation operation alone (i.e., the concatenation operation applied in the process of phrase derivation). + +6. For that reason, (syntactic) (de)composition of source code is often described by an (attributed) *unambiguous* context-free grammar, and the meaning of + programs is ultimately described as the meaning of terms (i.e., _concrete or abstract syntax trees_) instead.[^c2] + +7. The space of Lisp data (i.e., Lisp-style pairs plus atoms) constitutes a term algebra and is homomorphic to any other term algebra; thus, any claims about + term algebras are automatically applicable to Lisp data and vice versa. The homomorphism is due to such mappings as + + > _f_(_τ_0, _τ_1, ..., _τ__n_-1) --- + > (_f_, _τ_0, _τ_1, ..., _τ__n_-1) --- + > `(`_f_ _τ_0 _τ_1 ... _τ__n_-1`)` = + > `(`_f_ `.` `(`_τ_0 _τ_1 ... _τ__n_-1`)``)` --- + > (_f_, `(`_τ_0 _τ_1 ... _τ__n_-1`)`) --- + > pair(_f_, `(`_τ_0 _τ_1 ... _τ__n_-1`)`) + + Using Lisp data can be considered as *just* the most basic storage strategy for efficient term manipulation, and alternative data structures may be used as + well. + +8. Although some aspects of _AST_ (abstract syntax tree) validity and interpretation can be described themselves by (regular) tree grammars (which is a norm in + MANOOL specification), such grammars are not really essential for the principle from the fact (1) to work (that is, for recursive definitions of meaning to + do the job, term structure alone is already sufficient). + +[^c1]: In practice, _M_, in turn, is a function of some context, such as a binding environment. + +[^c2]: The theory of syntactic analysis for formal languages has been developed quite well long ago, and for simplicity, such grammars may be limited to only + one, synthesized attribute and only term constructors within attribute equations. + +Now, whereas some authors argue that arbitrary context-free grammars are inherently *anti-modular* and *non-composable* (as the language evolves it becomes +increasingly easy to introduce grammar ambiguities --- think C++) , others have studied grammar composability and proposed methods to adjust a union of two or +more grammars for the final grammar to make sense and be usable to describe syntax. However, according to its design philosophy, MANOOL takes an arguably +*simpler* route: for any two languages _L_ and _L'_ to be composable, they both should be defined by using *the same* context-free grammar (thus eliminating the +problem of grammar composability altogether). + +### Universal syntax ################################################################################################### +[universal syntax]: #h:universal-syntax "Below: Universal syntax" + +Ideally, the _universal syntax_ (as it *is* at any moment) suits any *future* needs in a programming language and therefore should change *very, very seldom*, +if at all. S-expressions (in any Lisp-family programming language) are described by such a syntax, and whereas MANOOL uses a slightly different notation, the +MANOOL syntax has the same dynamics as in Lisp(s).[^c3] + +[^c3]: I had to modify the MANOOL syntax only once in four years since its inception (due to the introduction of the `\}...\{` notation for string literals). + +A universal syntax is specified by a universal context-free grammar plus lexical rules (universal as well). Different attributed context-free grammars may +adequately do the job of syntactic decomposition for the same programming language, but for the idea of a universal grammar to work as well as possible, the +grammar should be as *general* as possible among those grammars. For instance, + * instead of encoding _a_ `+` _b_ in the AST as add(_a_, _b_), it might be encoded as plus(_a_, _b_) or even `(+)`(_a_, _b_), and + * instead of having a separate production for `if` ... `then` ... `else` ..., a universal production might be used to cover any construct in the form + `{`_e_0 _e_1 ... _e__n_-1`}`. + +The grammar of MANOOL was formulated empirically (by trial and error) and is intended to cover approximately 80% of practical needs,[^c4] although given the +above design principles and the desire to restrict the basic character set to good-old ASCII, the design space was in fact narrow. As a result, for an untrained +eye the MANOOL notation (and my default rules of code indentation) may look unfamiliar or even weird; personally, I had a lot or resistance at first but got +used surprisingly quickly.[^c5] One of the sources of inspiration for me to use an unusual notation (if I had to at all) was [Smalltalk]. + +[^c4]: Yes, *only* 80%, or otherwise the syntax would become too complicated and overwhelmed with seldom used features (recall the *Pareto* principle!). + +[^c5]: I also noticed that due to relatively high regularity, complex constructs in MANOOL do *not* look disproportionally uglier than simple ones, as it + happens with some programming language notations originally designed for "readability" and "elegance". + +Funnily enough, a universal syntax is not even mentioned among the MANOOL language design goals and has never been, but it has quickly become a part of the +implementation strategy; designing a new practical programming language requires a lot of experimentation, and thus it would be unproductive to have to deal +with context-free grammar changes every time a new feature is added, removed, or altered. + +Compared to the "Hello, world!" example presented above, the following example uses + * more library-supplied features (namely, `let rec ... in`, `proc ... as`, `if ... then ... else`, and `Out`), + * more polymorphic operations (namely, `(==)`, `(*)`, `(-)`, and `WriteLine`), and + * more _syntactic sugar_ (i.e., _syntactic features_): +^ + { {extern "manool.org.18/std/0.6/all"} in + : let rec + { Fact = + { proc { N } as + : if N == 0 then 1 else + N * Fact[N - 1] + } + } + in + Out.WriteLine["Factorial of 10 is "; Fact[10]] + } + +This is a version of a classic "Hello, world!" analog for functional languages. I modified the original version to turn it into a complete, runnable program, +which writes to the standard output the factorial of 10 in decimal form by using a recursive definition of the factorial function. + +Note that in its particular context above, `(=)` does *not* denote a polymorphic operation; it is just an "abstract operator" that serves to formulate +name-meaning associations syntactically (i.e., _a_ `=` _b_). + +When translating the above code into an internal run-time representation, the MANOOL _syntactic analyzer_ (also known as the _parser_) first transforms the code +into an intermediate representation (that is, an AST), which incidentally, corresponds to the following equivalent code in MANOOL: + + { {extern "manool.org.18/std/0.6/all"} in + { let rec + { { (=) Fact + { proc { N } as + { if {(==) N 0} then 1 else + {(*) N {Fact {(-) N 1}}} + } + } + } + } + in + {WriteLine Out "Factorial of 10 is " {Fact 10}} + } + } + + where any construct `{`_e_0 _e_1 ... _e__n_-1`}` represents (in textual form) an internal AST node with _n_ successors +_e_0, _e_1, ..., _e__n_-1, and almost everything else represents leaf nodes: `extern`, `"manool.org.18/..."`, `Fact`, `(=)`, +`(-)`, `1`, etc. + +Note that instead of talking about tree nodes, we might talk about Lisp data (and consequently, about Lisp-style lists, pairs, and atoms) as well, but due to +some deep technical and philosophical reasons (which are beyond the scope of this article), we conform with just (unlabeled) internal nodes with _n_ successors +(or equivalently, _n_-element tuples or arrays) plus leaf nodes, which in practice gives us the same advantages. For the same reasons, the MANOOL core does +not support improper lists on the AST level and thus does not have any analog of (S-expression) dotted pair notation.[^c6] + +[^c6]: There is at least one such precedent in the Lisp family of languages --- Clojure. + +In somewhat general terms, the following identities hold in MANOOL on the AST level: + + * _a_`[`_b_0`;` _b_1`;` ...`;` _b__n_-1`]` is equivalent to + `{`_a_ _b_0 _b_1 ... _b__n_-1`}` --- functional application notation (postfix-alike); + * _a_`!` is equivalent to + `(!)``[`_a_`]` --- postfix notation; + * `~`_a_ is equivalent to + `(~)``[`_a_`]` --- prefix notation; + * _a_ `+` _b_ is equivalent to + `(+)``[`_a_`;` _b_`]` --- left-associative infix notation (_a_ `+` _b_ `+` _c_ is equivalent to `(`_a_ `+` _b_`)` `+` _c_); + * _a_`.`_b_`[`_c_0`;` _c_1`;` ...`;` _c__n_-1`]` is equivalent to + _b_`[`_a_`;` _c_0`;` _c_1`;` ...`;` _c__n_-1`]` + --- OOPish notation (postfix-alike and left-associative infix-alike too); + * _a_ `==` _b_ is equivalent to + `(==)``[`_a_`;` _b_`]` --- non-associative infix notation; + * `{`_a_0 _a_1 ... _a__n_-1`:` _b_0 _b_1 ... _b__m_-1`}` is equivalent to + `{`_a_0 _a_1 ... _a__n_-1 `{`_b_0 _b_1 ... _b__m_-1`}``}` + --- right-associative cascading (MANOOLish) notation; etc. + +Note how applicants (i.e., _a_ in the first identity) and operators (e.g., `!`, `+`) correspond to leading (_head_) successors of internal AST nodes. This is +important because the head successor determines in a *uniform* way how the whole node is to be translated into an internal run-time representation (see +[below][core dispatcher]). + +As you may note, the MANOOL syntax has syntactic sugar that acts *abstractly* and *independently* of what _a_`[`_b_0`;` _b_1`;` ...`;` +_b__n_-1`]` and _a_ `+` _b_ actually mean. Some possible interpretations of MANOOL syntactic constructs are + * an expression to be evaluated by using an applicative-order evaluation strategy, + * the same but using a normal-order evaluation strategy, + * an expression-like pattern to match tree-like (hierarchical) data, + * a regular expression (in explicit tree-form) to match character strings, + * a context-free grammar (or a production or a part thereof), + * a [Prolog]-like (Horn) clause or a set thereof, + * a structured query to a relational database, + * an "elementary" function specified in algebraic (symbolic) form to undergo a symbolic differentiation, + * a declarative description of an input-form layout along with a description of its dynamic behavior, or + * just a part of a more complex construct (such as a name-meaning association _a_ `=` _b_ in a `let`-expression). + +In all of the above cases, the MANOOL parser is to be reused, which makes up a foundation for a rich but *uniformly accessible* toolset and emphasizes the +relation of the above listed areas to abstract algebra(s), so to speak. + +### The module system ################################################################################################## +[module system]: #h:the-module-system "Below: The module system" + +In classic modular programming languages, such as [Modula-2] and [Ada], a module definition may serve for a *combination* of one or more purposes *at a time*: + * to introduce name spaces, + * to control name visibility (and thus to allow for an information hiding principle realization), and + * to split programs across several source files (program units) and for separate compilation. + +In MANOOL, instead, a number of *separate* and mutually *orthogonal* features are available that cover the above mechanisms and can be easily *combined*: + * `export` is used to construct a module itself and thus introduces a name space; + * when it is combined with `let`, name hiding becomes possible; and + * `extern` allows you to reference a different program unit (including _foreign_ program units written in another programming language, for example, C++). + +Let's replace in the first factorial example the usual `extern` expression with a module constructor `{... in: export ...}` that describes our own module +exporting only four features: + + { { {extern "manool.org.18/std/0.6/all"} in -- my local module + : export let; proc; if; Out + } -- end of local module + in + : let rec + { Fact = + { proc { N } as + : if N == 0 then 1 else + N * Fact[N - 1] + } + } + in + Out.WriteLine["Factorial of 10 is "; Fact[10]] + } + +This corresponds to the idea of _local modules_ in Modula-2 or _nested packages_ in Ada, but in this example the module does not even have a name and is +imported immediately instead of being bound to some name or made an external entity. This is useful for selective import of features and for information hiding +in the spirit of the `local ... end` construct of [Standard ML]. + +Now, to illustrate the idea of a fully-replaceable standard library proposed above, let's create two text files and place them in the same directory: + +* `main.mnl`: + + { {extern "_my-lib.mnl"} in + : let rec + { Fact = + { proc { N } as + : if N == 0 then 1 else + N * Fact[N - 1] + } + } + in + Out.WriteLine["Factorial of 10 is "; Fact[10]] + } + +* and `_my-lib.mnl`: + + {{extern "manool.org.18/std/0.6/all"} in: export let; proc; if; Out} + +Although non-value entities are second-class entities, they are quite flexible otherwise --- they can be denoted by complex non-value expressions and bound to +names (aliased), statically (that is, at compile-time), for example:[^c7] + + { {{extern "manool.org.18/std/0.6/all"} in let} + { myLib = {{extern "manool.org.18/std/0.6/all"} in: export let; proc; if; Out} } in + : {myLib in let} { lambda = {myLib in proc} } in + : {myLib in let} rec + { Fact = + { lambda { N } as + : {myLib in if} N == 0 then 1 else + N * Fact[N - 1] + } + } + in + {myLib in Out}.WriteLine["Factorial of 10 is "; Fact[10]] + } + +[^c7]: While this looks like a contrived example, it is intended to illustrate various possibilities. + +The expression `{myLib in let}` denotes the feature `let` from the module `myLib` (also available as `let` from the standard library), and the `lambda` head +keyword in the expression `{lambda ... as ...}` behaves like `proc`. + +Note that the keywords `in`, `as`, `rec`, `then`, and `else` above do *not* refer to "features", and therefore they cannot be aliased or turned (on their own, +separately and independently) into members of a module, unfortunately. Incidentally, this is one of the limitations of MANOOL I am talking about above; I argue, +however, that the MANOOL solution is still aligned with the 80/20 rule. + +### Connection with S-expressions and Lisp(s) ########################################################################## + +As you may note, under certain circumstances the notation of MANOOL closely resembles the notation of S-expressions (in any Lisp-family programming language) +where instead of parentheses (`()`) braces (`{}`) are used and on the other hand some symbols (namely, non-alphanumeric ones) appear enclosed in parentheses. +With "proper" indentation the S-expression equivalent of both factorial examples presented above looks like: + + ((extern "manool.org.18/std/0.6/all") + in + (let rec ((= Fact + (proc (N) + as + (if (== N 0) + then + 1 + else + (* N (Fact (- N 1))))))) + in + (WriteLine Out "Factorial of 10 is " (Fact 10)))) + +In contrast to the case of Lisp-family programming languages, here, in some cases elements (keywords) like `in`, `rec`, `as`, `then`, and `else`, are +*essential* for meaning and thus cannot be stripped off without introducing ambiguity. However, an equivalent program in Scheme has an almost identical +syntactic structure: + + (letrec ((Fact + (lambda (N) + (if (= N 0) + 1 + (* N (Fact (- N 1))))))) + (display "Factorial of 10 is ") + (display (Fact 10)) + (newline)) + +Some people love S-expressions as a programming language syntax, whereas others hate them, and people often have strong opinions and/or preferences in respect +of programming language notation, since it lies on the surface and stays in constant contact with language users. Thus, I suspect the former are going to +criticize me for not using the (concrete, surface) syntax of S-expressions for MANOOL, especially having such a good opportunity.[^c8] For now, just for now, +however, please regard the concrete syntax as a matter of personal choice that is irrelevant to the subject of this article. As I have promised, I'll publish a +separate article about the MANOOL syntax and its rationale. + +[^c8]: This is not the first time that someone comes and suggests something to replace S-expressions and ... fails (beginning from never implemented + _M-expressions_ from classic Lisp, which incidentally slightly resemble the notation of MANOOL). I do *not*, however, expect the same fate for MANOOL + ;-). + +### Homoiconicity, metaprogramming, syntactic macros ################################################################### +[homoiconicity]: #h:homoiconicity-metaprogramming-syntactic-macros "Below: Homoiconicity, metaprogramming, syntactic macros" + +_Homoiconicity_ is the ability of a programming language to express programs that process other programs in a *very specific*, Lisp-like, way. Such languages +are called _homoiconic_, and hoiconicity has to do with _metaprogramming_ and _syntactic macros_. Homoiconicity, in particular, requires that ASTs could be +manipulated programmatically. Incidentally, MANOOL is a homoiconic language (and that is why its attributed context-free grammar has semantic evaluation +functions metacircularly specified as code in MANOOL). + +This simple but a bit contrived example in MANOOL demonstrates that we can translate `if ... then` and `if ... then ... else` into, say, Spanish by defining a +syntactic macro (but making it available in a limited region only): + + { {extern "manool.org.18/std/0.6/all"} in + : let + { si = + { macro + : proc { F } as + : if (Size[F] >= 7) & (F[2] == entonces') & (F[4] == si') & (F[5] == no') then + {array of if# F[1] then' F[3] else'} + F[Range[6; Size[F]]] -- {si ... entonces ... si no ...} + else + : if (Size[F] >= 4) & (F[2] == entonces') then + {array of if# F[1] then'} + F[Range[3; Size[F]]] -- {si ... entonces ...} + else + Nil -- fallback + } + } + in -- you can use {si ...} hereinafter, up to the end of the compound expression, except where shadowed + : let rec + { Fact = + { proc { N } as + : si N == 0 entonces 1 si no -- hablo espanol + N * Fact[N - 1] + } + } + in + Out.WriteLine["Factorial of 10 is "; Fact[10]] + } + +Don't worry if you don't understand this example fully right now --- just make sure that you can see for yourself from the code that this is possible (but stay +tuned if you are intrigued by this example and expect a future article with a detailed explanation of it ;-) + +Translation Scheme +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +In this section I explain *how* it all works in MANOOL by citing relevant parts of the official specification (so, please bear with me). + +This section presents the attributed context-free grammar and contains also quite a lot of advanced MANOOL code, but unfortunately, this article has *no* room +for detailed explanations. But don't be afraid --- this material is included merely for completeness and to better connect the ideas expressed in this article +with the MANOOL language reality --- you don't have to understand this material fully in order to appreciate these ideas (though, it may be helpful). Also +remember that you can always consult the MANOOL [Tutorial] and/or the official MANOOL [Specification] for more information. + +[Tutorial]: /tutorial/ "MANOOL Tutorial" +[Specification]: /specification/ "MANOOL Specification" + +### Compilation phases ################################################################################################# + +So, according to the official MANOOL specification: + +> To figure out the meaning of a form that makes up a program unit, the abstract machine transforms (compiles) the contents of the source file into an internal +> run-time representation, called _run-time code_. +> ... +> +> A three-stage translation (i.e., compilation) scheme is suggested for the abstract machine: +> +> * _lexical analysis_ --- The input string of characters is split into lexical elements (lexemes), whose meaning is then encoded in left-to-right order as a +> sequence of tokens. Note that in practice, whatever internal syntactic structure of individual lexemes is devised, it is generally unimportant for +> determination of their meaning; rather, the lexical syntax is used for their sheer classification. +> +> * _syntactic analysis_ --- The string of terminal symbols that corresponds to the sequence of tokens resulting from the previous compilation phase undergoes a +> syntactic analysis guided by a context-free grammar, which ultimately yields an _abstract syntax tree_ (AST) encoded as a MANOOL (semantic) value. Note that +> in contrast to lexical analysis, here syntactic structure is essential for correct interpretation of source code. +> +> * _semantic analysis_ and _code generation_ --- The form and consistency (e.g., the presence and placement of certain keywords) of the AST resulting from the +> previous compilation phase are checked, and finally, the run-time code is produced. Note that no new structural features are to be exposed on this stage, or +> they would at least reflect closely those of the AST. + +(See [The Abstract Machine](/specification/general#h:the-abstract-machine "MANOOL Specification: The Abstract Machine") and +[Translation Overview](/specification/general#h:translation-overview "MANOOL Specification: Translation Overview")) + +### Grammar productions ################################################################################################ + +The context-free grammar of MANOOL is in fact quite simple (having a total of only 34 productions): + +> S -> S +> S -> S +> S -> S[0] L[1] S[2] [[ MakeList[{array of A[1]; A[0]; A[2]}] ]] +> ^ +> S -> S +> S -> S[0] L[1] S[2] [[ MakeList[{array of A[1]; A[0]; A[2]}] ]] +> ^ +> S -> S +> S -> S[0] L[1] S[2] [[ MakeList[{array of A[1]; A[0]; A[2]}] ]] +> ^ +> S -> S +> S -> S[0] L[1] S[2] [[ MakeList[{array of A[1]; A[0]; A[2]}] ]] +> ^ +> S -> S +> S -> L[0] S[1] [[ MakeList[{array of A[0]; A[1]}] ]] +> ^ +> S -> S +> S -> S[0] "["L S[2] "]"L [[ MakeList[{array of A[0]} + A[2]] ]] +> S -> S[0] "."L S[2] "["L S[4] "]"L [[ MakeList[{array of A[2]; A[0]} + A[4]] ]] +> S -> S[0] L[1] [[ MakeList[{array of A[1]; A[0]}] ]] +> ^ +> S -> L[0] [[ A[0] ]] | "("L S[1] ")"L [[ A[1] ]] +> S -> "{"L S[1] "}"L [[ A[1] ]] | "("L S[1] ")"L [[ A[1] ]] +> ^ +> S -> L | L | L | L | L | L +> ^ +> S -> S | "" [[ MakeList[{array}] ]] +> ^ +> S -> S[0] S[1] [[ MakeList[{array of A[0]} + A[1]] ]] +> S -> S[0] ";"L S[2] [[ MakeList[{array of A[0]} + A[2]] ]] +> ^ +> S -> S | "" [[ MakeList[{array}] ]] +> ^ +> S -> S[0] S[1] [[ MakeList[{array of A[0]} + A[1]] ]] +> S -> S[0] ";"L S[2] [[ MakeList[{array of A[0]} + A[2]] ]] +> S -> S[0] ":"L S[2] [[ MakeList[{array of A[0]; A[2]}] ]] +> +> where `MakeList` is some (pure) function, and for any array `A` of ASTs, the following condition is met: +> +> MakeList[A].IsList[] & (Size[MakeList[A]] == Size[A]) & {for {E1 = MakeList[A]; E2 = A} all E1 == E2} + +(See [The context-free grammar](/specification/core-language/syntax#h:the-context-free-grammar "MANOOL Specification: The context-free grammar") +for information on the notation and +[Lexical Structure](/specification/core-language/syntax#h:lexical-structure "MANOOL Specification: Lexical Structure") +for information on lexical elements and lexical categories) + +### Compiler dispatcher #################################################################################################### +[core dispatcher]: #h:compiler-dispatcher "Below: Compiler dispatcher" + +The official MANOOL specification has the following to say about the third compilation stage: + +> Run-time code, which is the output of the semantic analysis and code generation translation phase, is represented by a _compiled entity_, which internally (in +> its turn) may contain other compiled entities. Incidentally, compiled entities are also what identifiers are bound to in a binding environment. +> +> A kind of object-oriented approach is used in this specification --- compiled entities are considered to be active agents (represented by a MANOOL +> value/object for the purposes of the metacircular description) capable of providing services whenever the abstract machine asks them to. +> +> The abstract machine has a (very) simple and compact semantic analysis and code generation _core dispatcher_. To compile an expression, the dispatcher first +> determines what kind of AST represents it: +> +> 1. If it is a symbol (explicitly) bound to a compiled entity in the active binding environment, that compiled entity becomes the result of compilation. +> +> 2. Otherwise, if it is an integer, a string, or a symbol, and the symbol starts with a character other than an ASCII lowercase letter (`a` thru `z`), the +> abstract machine constructs a compiled entity that represents a literal value. +> +> 3. Otherwise, if it is a compound expression, the dispatcher compiles (recursively) the head subexpression and then asks the resulting compiled entity to +> compile the whole expression. +> +> 4. Otherwise, if it is a special MANOOL value that encapsulates a compiled entity, which is produced by a `#`-expression (and for which the predicate `IsCode` +> returns `True`), then the encapsulated compiled entity becomes the result of compilation. +> +> 5. As a fallback, the dispatcher reports an error as appropriate. +> +> Note that checking the presence and placement of keywords (such as `then`, `else`, `do`, etc.) is performed, if needed, by compiled entities rather than +> directly by the dispatcher. Also, a compiled entity, when asked to compile an expression, may in turn call the dispatcher for subexpressions and possibly +> specify a different active binding environment to compile in. + +(See [Semantic Concepts](/specification/core-language/semantic-concepts#start "MANOOL Specification: Semantic Concepts") and +[Compiler Dispatcher](/specification/core-language/compiler-dispatcher#start "MANOOL Specification: Compiler Dispatcher")) + +### Metacircular specification ######################################################################################### + +In this last subsection I present hypothetic code in MANOOL that serves a double purpose: + * as a *formal*, _metacircular_ specification of the most important parts of the MANOOL semantics described above,[^d1] and + * as an illustration of what developing in MANOOL *looks* and *feels* like (but leaving a few details without explanation). + +[^d1]: Understanding a metacircular specification for a language _L_ requires prior knowledge of the language _L_, which raises a question about its utility. + However, normally only *partial* knowledge is required, and this roughly corresponds to how children learn communicative systems (i.e., natural + languages) from scratch. + +> For the purposes of illustration, let's assume that a compiled entity recognizes the following polymorphic operations (beyond the standard ones): +> +> * `IsRvalue` --- tell whether the compiled entity corresponds to an r-value expression +> +> * `IsLvalue` --- tell whether the compiled entity corresponds to an l-value expression (which shall imply a positive answer to the above question as well) +> +> * `Compile` --- given an active binding environment represented by a mapping `SymTab`, compile the specified compound expression (whose head subexpression +> corresponds to the compiled entity) to produce as a result another compiled entity +> +> * `Execute` --- given an evaluation context `Ctx`, evaluate the expression the compiled entity represents and produce a resulting value (applicable only in case +> of r-value expressions) +> +> * `ExecIn` --- given an evaluation context `Ctx` and a value `Val`, store the value into the location the compiled entity represents (applicable only in case of +> l-value expressions) +> +> * `ExecOut` --- given an evaluation context `Ctx`, move out the current value from the location the compiled entity represents (applicable only in case of +> l-value expressions) to produce as a result the value moved out +> +> The core dispatcher algorithm described above may be specified more formally using the following metacircular description (in MANOOL): +> +> { let rec +> { Compile = +> { proc { Form; SymTab } as +> : if IsSym[Form] & SymTab.Exists[Form] then SymTab[Form] else -- bound identifier +> : if IsInt[Form] | IsStr[Form] | IsSym[Form] & +> { do True after +> : if (Str[Form] <> "") & (Str[Form][0] >= "a"[0]$) & (Str[Form][0] <= "z"[0]$) then +> : signal CompileError with "unbound keyword (nested in this context)" +> } +> then -- literal value +> { let { _Form } in +> : object { _Form = Form } with +> IsRvalue = {proc {_} as True} +> IsLvalue = {proc {_} as False} +> Compile' = CompileApply +> Execute = {proc {Self; _} as Self[_Form]@} +> } +> else +> : if IsList[Form] & (Size[Form] <> 0) then Compile[Form[0]; SymTab].(Compile')[Form; SymTab] else -- compound expression +> : if IsCode[Form] then Form[_Entity]@ else -- a result of e# (used for metaprogramming) +> : signal CompileError with "invalid form" +> } +> } +> in +> : export Compile +> } +> +> As a matter of illustration, a compiled entity bound to the `if` keyword could be constructed by evaluating the expression +> +> { object {} with +> -- Classification +> IsRvalue = {proc {_} as False} +> IsLvalue = {proc {_} as False} +> -- Compilation +> Compile' = +> { proc { _; Form; SymTab } as +> : if (Size[Form] >= 6) & (Form[2] == then') & (Form[4] = else') then +> { let { _Cond; _Body1; _Body2 } in +> : object +> { _Cond = CompileRvalue[Form[1]; SymTab] +> _Body1 = CompileRvalue[Form[3]; SymTab] +> _Body2 = CompileRvalues[Form.Elems[Range[5; Size[Form]]]; SymTab] +> } +> with +> -- Classification +> IsRvalue = {proc {_} as True} +> IsLvalue = {proc {Self} as Self[_Body1]@.IsLvalue[] & Self[_Body2]@.IsLvalue[]} +> -- Execution +> Execute = {proc {Self; Ctx} as Execute[Self[{if Execute[Self[_Cond]@; Ctx] then _Body1 else _Body2}]@; Ctx]} +> ExecIn = {proc {Self; Val; Ctx} as ExecIn[Self[{if Execute[Self[_Cond]@; Ctx] then _Body1 else _Body2}]@; Val; Ctx]} +> ExecOut = {proc {Self; Ctx} as ExecOut[Self[{if Execute[Self[_Cond]@; Ctx] then _Body1 else _Body2}]@; Ctx]} +> -- Compilation +> Compile' = CompileApply +> } +> else +> : if (Size[Form] >= 4) & (Form[2] == then') then +> { let { _Cond; _Body } in +> : object +> { _Cond = CompileRvalue[Form[1]; SymTab] +> _Body = CompileRvalues[Form.Elems[Range[3; Size[Form]]]; SymTab] +> } +> with +> -- Classification +> IsRvalue = {proc {_} as True} +> IsLvalue = {proc {_} as False} +> -- Execution +> Execute = {proc {Self; Ctx} as: if Execute[Self[_Cond]@; Ctx] then Execute[Self[_Body]@; Ctx]} +> -- Compilation +> Compile' = CompileApply +> } +> else +> : signal CompileError with "invalid form" +> } +> } + +The above specification is very close to how expressions are evaluated in Lisp-family programming languages, although instead of evaluation we are talking here +about compilation. + + +Conclusions +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Please make your conclusions yourself about whether the presented above approach and the limitations of MANOOL make sense. + + +[Iota and Jot]: //en.wikipedia.org/wiki/Iota_and_Jot "Wikipedia: Iota and Jot" +[Lisp]: //en.wikipedia.org/wiki/Lisp_(programming_language) "Wikipedia: Lisp" +[Common Lisp]: //en.wikipedia.org/wiki/Common_Lisp "Wikipedia: Common Lisp" +[Scheme]: //en.wikipedia.org/wiki/Scheme_(programming_language) "Wikipedia: Scheme" +[Clojure]: //en.wikipedia.org/wiki/Clojure "Wikipedia: Clojure" +[Kernel]: http://klisp.org "klisp - a Kernel Programming Language implementation" +[Smalltalk]: //en.wikipedia.org/wiki/Smalltalk "Wikipedia: Smalltalk" +[Forth]: //en.wikipedia.org/wiki/Forth_(programming_language) "Wikipedia: Forth" +[Tcl]: //en.wikipedia.org/wiki/Tcl "Wikipedia: Tcl" +[Prolog]: //en.wikipedia.org/wiki/Prolog "Wikipedia: Prolog" +[Modula-2]: //en.wikipedia.org/wiki/Modula-2 "Wikipedia: Modula-2" +[Ada]: //en.wikipedia.org/wiki/Ada_(programming_language) "Wikipedia: Ada" +[Standard ML]: //en.wikipedia.org/wiki/Standard_ML "Wikipedia: Standard ML" + +{%endraw%}{%include post_footer.md%} diff --git a/_posts/2020-10-04-native-run-time-performance-for-a-high-level-dynamically-typed-programming-language.md b/_posts/2020-10-04-native-run-time-performance-for-a-high-level-dynamically-typed-programming-language.md new file mode 100644 index 0000000..408cf5f --- /dev/null +++ b/_posts/2020-10-04-native-run-time-performance-for-a-high-level-dynamically-typed-programming-language.md @@ -0,0 +1,90 @@ +--- +title: Native Run-Time Performance for a High-Level, Dynamically Typed Programming Language +updated: 2020-10-04 +excerpt: >- + MANOOL evolves into a general-purpose language equally suitable for exploratory programming and systems programming, and even high-performance computing +--- +{%include post_header.md%}{%raw%} + + +Since popularization of my project MANOOL has been apparently going to nowhere, I've decided to rather invest some time in my self-education, learning new +technologies and pondering about new opportunities. Surprisingly this has resulted in a concrete and viable plan for the future improvements (that is, for +MANOOL-2). If someone would like to join the project at this stage, I would be glad (any help is welcome, even with just testing the concepts). + +For those who are not familiar with the project: MANOOL seeks to bridge the gap, in the least troublesome way, between the exploratory style of programming (for +which languages like PHP, Python, Ruby, JavaScript, or Scheme are normally used) and the more thorough style (where languages like C, C++, Java, or Rust are a +better fit), and this is something I was in fact thinking about for more than 30 years. + +So, according to the plan, MANOOL evolves into a general-purpose language equally suitable for exploratory programming and systems programming, and even +high-performance computing (at least on traditional computer architectures, since nowadays some HPC solutions run on GPGPU and FPGA devices, and hypothetically +on emerging quantum computers, each case demanding a particular coding style usually available only in specialized, domain-specific programming languages). + +For certain reasons (not discussed here due to lack of space) exploratory programming normally involves high-level semantics and especially the dynamic typing +discipline (when data types in programs are associated with values or objects at run time instead of variables or expressions during program compilation, as +opposed to the static typing). On the other hand, systems programming and HPC presume, well, high run-time performance. These properties conflict with each +other, since dynamic typing usually means that computers make more decisions at program run time, which slows down performance by itself and also hinders +further performance optimizations. + +Nonetheless, real-world applications often consist of components with different flexibility and performance requirements. For instance, an application may +include inherently dynamic event-driven user interface code and much more static domain area (back-end) code where most hot (critical) instruction paths are +concentrated. + +Sophisticated (and expensive in implementation) JIT compilation techniques (used, e.g., in V8 and Mozilla's JavaScript VMs and LuaJIT), including the so-called +tracing JIT, allow you to gain great performance for dynamic languages. Still, such techniques hardly satisfy the above goal and offer notably lower performance +than classic ahead-of-time compilation for equivalent programs written in an inherently static language (such as C, Modula-2, Ada, or Rust, to name a more +recent language); the slowdown may be somewhere between 4 and 10 times (which is still an impressive improvement compared to what more affordable +implementations offer). This happens because in practice such VMs have to anticipate the program execution profile (and hence data types) at run time (with +varying success) instead of exploiting static hints the programmer might provide about the profile, either explicitly or rather implicitly. + +Due to the conflict described above, other languages that do achieve the above goal (e.g., Objective-C) are normally hybrid languages that solve the problem by +combining and providing both low-level but high-performance features with high-level but low-performance ones (for instance, Objective-C semantically and even +syntactically looks like a mix between C and Smalltalk). + +The approach MANOOL-2 adopts is different: MANOOL-2 is essentially a dynamically typed language with no explicit HPC-related features (such as static types), +but its type system is specifically devised to enable significant amount of type inference during compilation (with sporadic or rather implicit help from the +programmer). In MANOOL-2 this inference is based on long-established data and control flow analysis algorithms and function inlining, and there seems to be an +intimate connection between type inference and constant/value/condition propagation (including their interprocedural variants). Note that typing discipline +(static vs dynamic) is orthogonal to this issue: there is still no such thing as "false negatives due to failed type checks" in MANOOL-2. + +The advantage of this approach is that the programmer uses a more compact language and thus has to master fewer features and make fewer decisions as to which +features to use in each particular case and for each particular component of the program (the programmer still should be aware of how the compiler infers types +and performs other deductions and which coding techniques lead to the maximum performance boost in hot paths, but performance hints can be introduces gradually, +if needed at all). + +Perhaps the closest such project is Julia. However, Julia is specifically oriented on the area of scientific computing, has high startup times, and still offers +suboptimal performance (albeit better than JavaScript or Lua). MANOOL-2 should overcome such issues, and it is a viable goal according to my preliminary +experiments. + +Note that apart from higher run-time performance, statically typed languages are also traditionally associated with higher software engineering standards, as +opposed to "quick-and-dirty" exploratory style solutions. However, the position MANOOL-2 adopts is that a sophisticated static type system used for defect +preventing purposes (while being useful in practice) should belong better to external tools and not to the programming language itself (though, the type system +of MANOOL-2 makes it more suitable for programming in-the-large in comparison to an ordinary dynamically typed language). + +All of the above is not just a business idea. I have actually performed some experiments and studied viability of the optimization algorithms and (what's most +important) what limitations of such algorithms can and should be condoned in practice. And of course, there is also the current version of MANOOL as a starting +point. In conclusion and as a matter of simple illustration, here is a piece of code in MANOOL-2 with some comments regarding its expected high-performance +hallmarks: + + { {extern "manool.org.18/std/2.0/all"} in + : let + Fold = -- left-fold some elements yielded by some generator G + { proc I; Op; G as inline -- polymorphic procedure + : do I after -- refcounting for G on entry/exit is optimized out + : for E = G do -- iterate over elements in RAM, no dynamic dispatch + I = I!.Op[E] -- just "addsd" on x86, no dispatch or type checks + } + in + : let + Avg = -- average elements of an array A of Binary64 floats + { proc A as -- monomorphic procedure + {assert Size[A.as[{array F64}]] > 0} -- tiny O(1) overhead + Fold[F64[0]$; (+); A] / F64[Size[A]] -- no dispatch or type checks + } + in + -- The return type of Avg is actually known at compile-time - F64: + Out.Write_line[Avg[{array F64}[F64[1] F64[2] F64[3] F64[4] F64[5]]$]] + Out.Write_line[Avg[{array I64}[1 2 3 4 5]$]] -- signals Type_mismatch + } + + +{%endraw%}{%include post_footer.md%} diff --git a/_posts/2021-02-07-cfp2.md b/_posts/2021-02-07-cfp2.md new file mode 100644 index 0000000..8178701 --- /dev/null +++ b/_posts/2021-02-07-cfp2.md @@ -0,0 +1,88 @@ +--- +title: Native Run-time Performance for Dynamic Programming Languages +excerpt: Presentation for the [StrangeLoop](https://thestrangeloop.com) conference Sep-Oct'21 (CFP talk submission, prospective) +unlisted: true +--- + +{%include page_header.md%} + + +*{{page.excerpt}}* + +Abstract +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Much has been told about advantages and disadvantages of static vs dynamic type checking, without reaching a definitive conclusion. Being a low-budget project, +the programming language MANOOL has to seek major conceptual economy, leading to a compact implementation. For instance, MANOOL is a homoiconic language (like +Lisp or Tcl). On the other hand, its design and implementation simplicity is the most compelling reason for the dynamic typing choice. + +Static type checking is often associated with languages that admit high-performance implementations (e.g., C or Java), but this does not always has to be the +case. For instance, sophisticated dynamic specialization (e.g., in case of JavaScript/V8 or LuaJIT) bridges the performance gap between static and dynamic +typing languages. Unfortunately, such techniques may be prohibitively expensive for a small project like MANOOL, while being still insufficient to reach the +highest possible run-time performance (achievable for C/C++). + +Despite of dynamic typing, MANOOL is specifically designed to admit effective static optimizations and thus, nearly native run-time performance. While not a new +idea (e.g., Julia has the necessary properties for that), it seems that it has been underexplored so far in mainstream programming (and hopefully, MANOOL has a +more general-purpose design than Julia). + +You will learn about the overall architecture of MANOOL and its compiler, the intermediate representations, the sources of inefficiencies in high-level dynamic +languages (including the dynamic typing), and how they can be overcome. No prior knowledge of modern compiler technology is required. + +About the presenter +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +**Alexey Protasov** + +Alex is an enthusiastic independent developer with Russian origins living in Medellin, Colombia. He constantly dreams with "better" programming languages and +has over 30 years of experience with designing and implementing languages and development tools -- he offered in the past a shareware visual programming tool, +worked in the area of compilers at Intel and Sun Microsystems, and taught compiler construction and the theory of formal languages at a university. + +Alex speaks Spanish, English, and Russian. When he is not working on programming languages, he likes swimming, traveling, and dancing (Salsa, Porro, Merengue, +Cumbia, etc.). + +What will the attendee learn? --- Comments for reviewers +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +*Presentation outline* + +* Introduction + * About the author, the current status of MANOOL, references and contact details + * When run-time performance of a language implementation is relevant and when it is not + * Own middle-end/back-end rationale (why not just using LLVM) +* Language and compiler architecture + * Scanner and parser + * The compiler core dispatcher + * Optimization and code generation pipeline + * Object module linker/loader (for separate compilation) +* Intermediate representations + * Abstract syntax tree + * Compiled tree representation + * Register machine + * Object modules (persistent cache thereof) +* Sources of inefficiencies in high-level dynamic languages + * Run-time dispatch by type (AKA dynamic overloading in MANOOL), run-time type checking + * Run-time specification of polymorphic operations and composite member names (in case of MANOOL) + * Value (un)boxing (pointer dereferencing), tracing GC or reference counting, run-time memory allocation (from heap) + * General redundancies that can be eliminated by seeing through abstraction boundaries and applying static code specialization +* Overcoming the inefficiencies during design of a dynamic language + * Homogeneous composite data + * Procedure inlining and explicit precondition checking in monomorphic and polymorphic cases + * Value (copy-on-write) and move semantics (in case of MANOOL) +* Middle-end overview (analysis and transformations) + * Converting to SSA (static single assignment form), to enable advanced data and control flow analysis (especially IPSCCP) + * Interprocedural conditional constant propagation (more complete than in LLVM), inlining, constant folding, type inference as IPSCCP + * Constraints on procedure specialization (or inlining) + * Copy propagation, dead code elimination (including unused and unreachable code) + * Jump/branching optimizations (basic block merging and jump threading) + * Elimination of redundant loads/stores, reference counter increments/decrements, and heap memory allocations/deallocations + * Other analysis/transformations (GVN, CSE, PRE, invariant code motion, strength reduction, loop unrolling, autovectorization, etc.) +* Back-end overview (machine code generation) + * Converting out of SSA, register allocation + * Instruction selection, peephole optimizations, etc. +* Example (Conway's Game of Life) + * Source code in MANOOL + * Discussion of applicable optimizations and run-time performance +* Conclusions + Questions & Answers + + +{%include page_footer.md%} diff --git a/about.md b/about.md new file mode 100644 index 0000000..1ad508f --- /dev/null +++ b/about.md @@ -0,0 +1,55 @@ +--- +# about.md +title: Hello Everyone! -- Eh, Quiai? +updated: 2020-01-07 +--- + + + + + +{%include page_header.md%}{%raw%} + + +
@rusini
+ +My name is Alex Protasov (AKA rusini). I've been programming since I was 12 and often, when I was stuck with some problem, I was coming to ideas about solving +the problem either via a programming paradigm shift or a simple change of notation. Thus, programming languages have been always my passion and I've been always +dreaming about sharing this experience and offering my own software development tools. MANOOL is a pinnacle of those hopes, but what I didn't know is how +complex it was actually going to be ... + +At some time in the past I + * worked for two multinational corporations in the area of compilers (Intel and Sun Microsystems), + * had a shareware visual programming tool, + * taught compilers and the theory of formal languages at a university, and + * had an IT Director position (where I acquired strong Linux server deployment skills). + +I have an academic degree of MS of Computer Science, although I have reasons to not believe at all in the formal education system. + +I'm a Russian expat (from Saint Petersburg) and live in the beautiful City of the Eternal Spring, Medellín, Colombia, South America. My everyday language +is Spanish, my lingua franca is English, and my mother tongue is Russian. I also understood Portuguese and Italian quite well once in the past. + +When I'm not working on MANOOL, I like swimming, traveling, and dancing (Salsa, Porro, Merengue, Cumbia, etc.). + + +{%endraw%}{%include page_footer.md%} + + + diff --git a/bin/.htaccess b/bin/.htaccess new file mode 100644 index 0000000..571e869 --- /dev/null +++ b/bin/.htaccess @@ -0,0 +1,2 @@ +ExpiresActive On +ExpiresDefault "access" diff --git a/bin/eval.log b/bin/eval.log new file mode 100644 index 0000000..e69de29 diff --git a/bin/eval.php b/bin/eval.php new file mode 100644 index 0000000..e16a13a --- /dev/null +++ b/bin/eval.php @@ -0,0 +1,14 @@ + + + +MANOOL Online Evaluator Output + + +
+&1)'
+   . ' || printf "Exit status: $?"'
+), ENT_NOQUOTES, 'UTF-8')?>
+
+ diff --git a/bin/index.php b/bin/index.php new file mode 100644 index 0000000..c16b3ee --- /dev/null +++ b/bin/index.php @@ -0,0 +1 @@ +. + +You can find precompiled binaries for 14 combinations of OSes/ISAs/ABIs at . +Unpack the corresponding .tar archive manually into the installation root directory, e.g. (for Ubuntu 18.04 LTS, x86-64/lp64): + + wget -O- -q https://github.com/rusini/manool/releases/download/v0.6/manool-0.6-ubuntu-18.04-x86_64-lp64.tar.xz | + sudo tar xJv -C /usr/local + +Alternatively, you can first build the MANOOL binaries yourself. +In any case please refer to [Launching MANOOL] below for information on running MANOOL after installation. + +General Instructions +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Try, e.g.: + + git clone https://github.com/rusini/manool && cd manool + make + ./mnl <(echo $'{{extern "manool.org.18/std/0.6/all"} in Out.WriteLine["Hello, world!"]}') + +Note that there is no need to run `./configure` (though, it's harmless), since the set of supported host/target platforms is more homogeneous than it used to be +for GNU tools, and thus all platform-specific tuning can be done in a simpler way (that is, during actual building). +In theory, the source file `config.tcc` is intended to define required platform-specific feature test macros; in practice, there is rarely any need to touch it. + +To run MANOOL from within a different directory, point the environment variable `MNL_PATH` to the library directory (or a search list thereof) and invoke +`mnlexec` as in the following example: + + MNL_PATH=/build/lib /build/mnlexec \ + <(echo $'{{extern "manool.org.18/std/0.6/all"} in Out.WriteLine["Hello, world!"]}') + +The section Confirmed Builds provides more specific instructions together with recommended compilation options for 23 combinations of OSes/ISAs/ABIs/compilers. + +#### Build dependencies ######################################################## ++ Decent C99 compiler toolchain with support for some GCC-specific extensions (which includes clang and Intel's icc) ++ Decent C++11 compiler toolchain with support for some GCC-specific extensions (which includes sufficiently recent clang++ and Intel's icpc) ++ Full-blown Linux or FreeBSD target operating system (which includes a sufficiently recent Android with CrystaX NDK) ++ One of the following ISA/ABI targets: x86-64/lp64, i386+sse2/ilp32, x86-64/ilp32, aarch64+el/lp64, armv7+el+vfp/ilp32 ++ Sufficiently recent GNU `make` utility ++ POSIX-compliant shell (`sh`) + `mkdir`, `cp`, and `rm` + +#### Makefile phony targets #################################################### ++ `all` (default) --- build MANOOL; the result is placed into the directory `build` and its various subdirectories (created automatically if needed) ++ `run` --- run a short build integrity test; depends on an up-to-date MANOOL build ++ `run-valgrind` --- the same but run the test under Valgrind to look more closely for any build issues ++ `install` --- install MANOOL; depends on an up-to-date MANOOL build ++ `clean` --- clean up the `build` directory + +#### Makefile configuration variables ########################################## ++ `CC` --- command to invoke the C compiler; by default: `$(SCL)` `$(GCC)` `$(PIPE)` `-w` `$(MARCH)` `-pthread` `-std=c99` ++ `CXX` --- command to invoke the C++ compiler (including for linking); by default: `$(SCL)` `$(GXX)` `$(PIPE)` `-w` `$(MARCH)` `-pthread` `-std=c++11` ++ `CPPFLAGS` --- additional preprocessing options (for both C and C++ sources); e.g., refer to [Other preprocessor definitions] below ++ `CFLAGS` --- additional compilation options (normally optimization-related only) for the C compiler; by default just `-O3` ++ `CXXFLAGS` --- additional compilation options for the C++ compiler; by default specified by `CFLAGS` ++ `LDFLAGS` --- additional linking options; by default: `-s` `-Wl,--as-needed` ++ `LDLIBS` --- options to specify libraries for linking; by default: `-lm` `-ldl` `-lrt` ++ `SCL` --- command prefix for enabling RHEL/CentOS Software Collections (see `CC`/`CXX`), if needed; for instance: `scl` `enable` `devtoolset-8` `--` ++ `GCC` --- by default just `gcc` (see `CC`); use, for instance, `clang` to compile with clang ++ `GXX` --- by default just `g++` (see `CXX`); use, for instance, `clang++` for clang++ ++ `PIPE` --- by default `-pipe` to enable using pipes (see `CC`/`CXX`, may lead to build issues in some rare cases on some platforms) ++ `MARCH` --- to specify a target machine architecture (ISA/ABI) if needed; by default: `-msse2` `-mfpmath=sse` (relevant for the i386 ISA) ++ `LDFLAGS_SO` --- additional linking options to use when building shared (.so) libraries; by default `-fPIC` ++ `RUN_ARGS` --- to specify command line arguments for running the test; by default just `test.mnl` ++ `VALGRIND` --- command prefix to test under Valgrind; by default: `$(SCL)` `valgrind` ++ `PREFIX` --- destination root directory for the `install` target; by default `/usr/local` ++ `MNL_CONFIG` --- to enable/disable various features via conditional compilation flags (refer to [Conditional compilation] below) + +#### Conditional compilation ################################################### +[Conditional compilation]: #h:conditional-compilation "Below: Conditional compilation" +`MNL_CONFIG` is to contain one or more of the following space-separated flags (all features are enabled by default except `MNL_USE_DEBUG`): + * `-UMNL_WITH_OPTIMIZE` --- prevent compilation of VM operation fusion optimizations + (e.g., for benchmarking their effect, to reduce the object code size, or to reduce build times during debugging) + * `-UMNL_WITH_IDENT_OPT` --- for (in)equality comparisons, disable dynamic optimizations based on object identity + (for good or for bad) + * `-UMNL_WITH_MULTITHREADING` --- disable support for multiple threads of execution + (considerably improves single-threaded performance) + * `-UMNL_WITH_UUID_NS` --- use `mnl` as a top-level namespace instead of a UUID for MANOOL stuff + (useful to simplify object file analysis, but should be avoided otherwise) + * `-UMNL_USE_EXPECT` --- do not use branch prediction specifications (`__builtin_expect` gcc-specific builtins) + * `-UMNL_USE_INLINE` --- do not use inlining control (via `__always_inline__`/`__noinline__` gcc-specific attributes) + * `-UMNL_USE_PURE` --- do not mark pure functions (with `__const__` and `__pure__` gcc-specific attributes) + * `-UMNL_USE_NOCLOBBER` --- do not mark pure functions (with `__pure__` gcc-specific attributes); + `MNL_USE_PURE` is stronger than `MNL_USE_NOCLOBBER` + * `-DMNL_USE_DEBUG` --- enable the debugging facility (`using` `::std::cerr` in the `::mnl::aux` namespace) + +#### Other preprocessor definitions ############################################ +[Other preprocessor definitions]: #h:other-preprocessor-definitions "Below: Other preprocessor definitions" ++ `MNL_AUX_UUID` --- top-level namespace (rarely needs to be defined); forces the effect of `MNL_WITH_UUID_NS` ++ `MNL_STACK` --- hard-coded default for the `MNL_STACK` environment variable; by default `6291456` (6 MiB) ++ `MNL_HEAP` --- hard-coded default for the `MNL_HEAP` environment variable; by default `268435456` (256 MiB) ++ `MNL_PATH` --- hard-coded default for the `MNL_PATH` environment variable; by default `/usr/local/lib/manool:/usr/lib/manool` + +### Installation ####################################################################################################### + +To install MANOOL after building, try, e.g. (also read about the `PREFIX` makefile variable above): + + sudo make install + +### Launching MANOOL ################################################################################################### +[Launching MANOOL]: #h:launching-manool "Below: Launching MANOOL" + +To run installed MANOOL, point the environment variable `MNL_PATH` to the installed-library directory, e.g.: + + MNL_PATH=/usr/local/lib/manool mnlexec \ + <(echo $'{{extern "manool.org.18/std/0.6/all"} in Out.WriteLine["Hello, world!"]}') + +To get the `mnlexec` invocation synopsis and a short description of all recognized environment variables, just run it without arguments: `mnlexec`. + +Note that you can specify a MANOOL script to run on the command line (as in `mnlexec hello.mnl` if `mnlexec` is in `PATH`), or you can use a shebang feature and +turn your script into a directly executable file as in the following example (assuming `mnlexec` is in `PATH` and `MNL_PATH` is set accordingly in your +environment): + + cat >hello && chmod +x hello + #!/usr/bin/env mnlexec + {{extern "manool.org.18/std/0.6/all"} in Out.WriteLine["Hello, world!"]} + + ./hello + +Confirmed Builds +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +### Newer OSes ######################################################################################################### + ++ Ubuntu Server 18.04 LTS, x86-64, x86-64/lp64, g++ + + sudo apt install g++ make + make + ++ Ubuntu Server 18.04 LTS, x86-64, i386+sse2/ilp32, g++ + + sudo apt install g++-multilib make + make MARCH='-m32 -msse2 -mfpmath=sse' LDFLAGS_SO= + ++ Ubuntu Server 18.04 LTS, x86-64, x86-64/ilp32, g++ + + sudo apt install g++-multilib make + make MARCH=-mx32 + ++ Ubuntu Server 18.04 LTS, x86-64, x86-64/lp64, clang++ + + sudo apt install clang make + make GCC=clang GXX=clang++ + +******************************************************************************** + ++ RHEL 8, x86-64, x86-64/lp64, g++ + + sudo yum install gcc-c++ make + make + ++ RHEL 8, x86-64, i386+sse2/ilp32, g++ + + sudo yum install gcc-c++ make glibc-devel.i686 libstdc++-devel.i686 + make MARCH='-m32 -msse2 -mfpmath=sse' LDFLAGS_SO= + ++ RHEL 8, x86-64, x86-64/lp64, clang++ + + sudo yum install clang make + make GCC=clang GXX=clang++ + ++ RHEL 8, x86-64, i386+sse2/ilp32, clang++ + + sudo yum install clang make glibc-devel.i686 libstdc++-devel.i686 + make GCC=clang GXX=clang++ MARCH='-m32 -msse2 -mfpmath=sse' LDFLAGS_SO= + +******************************************************************************** + ++ Ubuntu Server 18.04 LTS, aarch64, aarch64+el/lp64, g++ + + sudo apt install g++ make + make MARCH= + ++ Ubuntu Server 18.04 LTS, aarch64, aarch64+el/lp64, clang++ + + sudo apt install clang make + make GCC=clang GXX=clang++ MARCH= + +******************************************************************************** + ++ FreeBSD 12, x86-64, x86-64/lp64, clang++ + + sudo pkg install gmake + gmake GCC=clang GXX=clang++ + ++ FreeBSD 12, x86-64, x86-64/lp64, g++ + + sudo pkg install lang/gcc gmake + gmake + +******************************************************************************** + ++ openSUSE Leap 15.1, x86-64, x86-64/lp64, g++ + + sudo zypper install gcc-c++ make + make + +******************************************************************************** + ++ Android 5.1 (Lollipop), armv7+vfp, armv7+el+vfp/ilp32, clang++ + + # (from cxxdroid terminal) + make GCC=clang GXX=clang++ MARCH= LDLIBS='-lm -ldl' + + +### Older OSes ######################################################################################################### + ++ CentOS 6, x86-64, x86-64/lp64, g++ + + sudo yum install centos-release-scl && sudo yum install devtoolset-8-gcc-c++ + make SCL='scl enable devtoolset-8 --' + +******************************************************************************** + ++ CentOS 7, x86-64, x86-64/lp64, g++ + + sudo yum install centos-release-scl && sudo yum install devtoolset-8-gcc-c++ + make SCL='scl enable devtoolset-8 --' + ++ CentOS 7, x86-64, x86-64/lp64, clang++ + + sudo yum install centos-release-scl && sudo yum install llvm-toolset-7-clang + make SCL='scl enable llvm-toolset-7 --' GCC=clang GXX=clang++ + +******************************************************************************** + ++ Debian GNU/Linux 9 (Stretch), x86-64, x86-64/lp64, clang++ + + sudo apt install clang-7 make + make GCC=clang-7 GXX=clang++-7 + ++ Debian GNU/Linux 9 (Stretch), x86-64, x86-64/lp64, g++ + + sudo apt install g++ make + make GXX='g++ -fpermissive' + +******************************************************************************** + ++ Ubuntu Server 16.04 LTS, x86-64, x86-64/lp64, clang++ + + sudo apt install clang-6.0 make + make GCC=clang-6.0 GXX=clang++-6.0 + ++ Ubuntu Server 16.04 LTS, x86-64, x86-64/lp64, g++ + + sudo apt install g++ make + make GXX='g++ -fpermissive' + +******************************************************************************** + ++ Debian GNU/Linux 8 (Jessie), x86-64, x86-64/lp64, clang++ + + sudo apt install clang-4.0 + make GCC=clang-4.0 GXX=clang++-4.0 + ++ Debian GNU/Linux 8 (Jessie), x86-64, x86-64/lp64, g++ + + sudo apt install g++ + make GXX='g++ -fpermissive' + + +{%endraw%}{%include page_footer.md%} diff --git a/eval.html b/eval.html new file mode 100644 index 0000000..5c8a315 --- /dev/null +++ b/eval.html @@ -0,0 +1,29 @@ +--- +title: MANOOL Online Evaluator +--- + +
+

{{page.title|smartify}}

+
+ +

Expression

+
+
+ +
+ + + (you have at most 15 s of CPU time per request) +
+

Output

+ + + diff --git a/specification/core-language/compiler-dispatcher.md b/specification/core-language/compiler-dispatcher.md new file mode 100644 index 0000000..5d558ed --- /dev/null +++ b/specification/core-language/compiler-dispatcher.md @@ -0,0 +1,173 @@ +--- +# specification/core-language/compiler-dispatcher.md +title: Compiler Dispatcher -- Specification +updated: 2019-12-21 +--- + + + +{%include spec_header.md%}{%raw%} + + +Metanotation +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Two metalanguages are used in this section: + * plain English and + * the language of MANOOL [expressions] (whereby involving a metacircular specification). + +All MANOOL expressions in the metacircular specification are to be considered in the binding environment of the MANOOL [standard library]. + +[expressions]: /specification/core-language/semantic-concepts#h:forms-expressions-control-flow "Forms, expressions, control flow" +[standard library]: /specification/standard-library/#start "Standard Library" + + +In Plain English +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Run-time code, which is the output of the semantic analysis and code generation [translation phase], is represented by a _compiled entity_, which internally (in +its turn) may contain other compiled entities. Incidentally, compiled entities are also what identifiers are [bound] to in a binding environment. + +A kind of object-oriented approach is used in this specification --- compiled entities are considered to be active agents (represented by a MANOOL value/object +for the purposes of the metacircular description) capable of providing services whenever the abstract machine asks them to. + +The abstract machine has a (very) simple and compact semantic analysis and code generation _core dispatcher_. To compile an expression, the dispatcher first +determines what kind of AST represents it: + +1. If it is a symbol (explicitly) bound to a compiled entity in the active binding environment, that compiled entity becomes the result of compilation. + +2. Otherwise, if it is an [integer], a [string], or a [symbol], and the symbol starts with a character other than an ASCII lowercase letter (`a` thru `z`), the + abstract machine constructs a compiled entity that represents a literal value. + +3. Otherwise, if it is a [compound expression], the dispatcher compiles (recursively) the head subexpression and then asks the resulting compiled entity to + compile the whole expression. + +4. Otherwise, if it is a special MANOOL value that encapsulates a compiled entity, which is produced by a [`#`-expression] (and for which the predicate `IsCode` + returns `True`), then the encapsulated compiled entity becomes the result of compilation. + +5. As a fallback, the dispatcher reports an error as appropriate. + +Note that checking the presence and placement of keywords (such as `then`, `else`, `do`, etc.) is performed, if needed, by compiled entities rather than +directly by the dispatcher. Also, a compiled entity, when asked to compile an expression, may in turn call the dispatcher for subexpressions[^b1] and possibly +specify a different active binding environment to compile in. + +[^b1]: .. and occasionally, even for expressions constructed artificially on-the-fly ... + +[translation phase]: /specification/general#h:translation-overview "Translation Overview" +[bound]: /specification/core-language/semantic-concepts#h:bindings-and-scopes-binding-environments "Bindings and scopes, binding environments" +[integer]: /specification/standard-library/basic-data-types#h:integer "Integer Type" +[string]: /specification/standard-library/basic-data-types#h:string "String Type" +[symbol]: /specification/standard-library/basic-data-types#h:symbol "Symbol Type" +[compound expression]: /specification/core-language/semantic-concepts#h:forms-expressions-control-flow "Forms, expressions, control flow" +[`#`-expression]: /specification/standard-library/custom-notation-syntactic-macros-metaprogramming#h:sharp-expressions "#-expressions" + +Metacircular Specification +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +For the purposes of illustration, let's assume that a compiled entity recognizes the following polymorphic operations (beyond the standard ones): + +* `IsRvalue` --- tell whether the compiled entity corresponds to an r-value expression + +* `IsLvalue` --- tell whether the compiled entity corresponds to an l-value expression (which shall imply a positive answer to the above question as well) + +* `Compile` --- given an active binding environment represented by a mapping `SymTab`, compile the specified compound expression (whose head subexpression + corresponds to the compiled entity) to produce as a result another compiled entity + +* `Execute` --- given an evaluation context `Ctx`, evaluate the expression the compiled entity represents and produce a resulting value (applicable only in case + of r-value expressions) + +* `ExecIn` --- given an evaluation context `Ctx` and a value `Val`, store the value into the location the compiled entity represents (applicable only in case of + l-value expressions) + +* `ExecOut` --- given an evaluation context `Ctx`, move out the current value from the location the compiled entity represents (applicable only in case of + l-value expressions) to produce as a result the value moved out + +The core dispatcher algorithm described above may be specified more formally using the following metacircular description (in MANOOL):[^c1] + + { let rec + { Compile = + { proc { Form; SymTab } as + : if IsSym[Form] & SymTab.Exists[Form] then SymTab[Form] else -- bound identifier + : if IsInt[Form] | IsStr[Form] | IsSym[Form] & + { do True after + : if (Str[Form] <> "") & (Str[Form][0] >= "a"[0]$) & (Str[Form][0] <= "z"[0]$) then + : signal CompileError with "unbound keyword (nested in this context)" + } + then -- literal value + { let { _Form } in + : object { _Form = Form } with + IsRvalue = {proc {_} as True} + IsLvalue = {proc {_} as False} + Compile' = CompileApply + Execute = {proc {Self; _} as Self[_Form]@} + } + else + : if IsList[Form] & (Size[Form] <> 0) then Compile[Form[0]; SymTab].(Compile')[Form; SymTab] else -- compound expression + : if IsCode[Form] then Form[_Entity]@ else -- a result of e# (used for metaprogramming) + : signal CompileError with "invalid form" + } + } + in + : export Compile + } + +[^c1]: Understanding a metacircular specification for a language _L_ requires prior knowledge of the language _L_, which raises a question about its utility. + However, normally only *partial* knowledge is required, and this roughly corresponds to how children learn communicative systems (i.e., natural + languages) from zero. + +As a matter of illustration, a compiled entity bound to the `if` keyword could be constructed by evaluating the expression + + { object {} with + -- Classification + IsRvalue = {proc {_} as False} + IsLvalue = {proc {_} as False} + -- Compilation + Compile' = + { proc { _; Form; SymTab } as + : if (Size[Form] >= 6) & (Form[2] == then') & (Form[4] = else') then + { let { _Cond; _Body1; _Body2 } in + : object + { _Cond = CompileRvalue[Form[1]; SymTab] + _Body1 = CompileRvalue[Form[3]; SymTab] + _Body2 = CompileRvalues[Form.Elems[Range[5; Size[Form]]]; SymTab] + } + with + -- Classification + IsRvalue = {proc {_} as True} + IsLvalue = {proc {Self} as Self[_Body1]@.IsLvalue[] & Self[_Body2]@.IsLvalue[]} + -- Execution + Execute = {proc {Self; Ctx} as Execute[Self[{if Execute[Self[_Cond]@; Ctx] then _Body1 else _Body2}]@; Ctx]} + ExecIn = {proc {Self; Val; Ctx} as ExecIn[Self[{if Execute[Self[_Cond]@; Ctx] then _Body1 else _Body2}]@; Val; Ctx]} + ExecOut = {proc {Self; Ctx} as ExecOut[Self[{if Execute[Self[_Cond]@; Ctx] then _Body1 else _Body2}]@; Ctx]} + -- Compilation + Compile' = CompileApply + } + else + : if (Size[Form] >= 4) & (Form[2] == then') then + { let { _Cond; _Body } in + : object + { _Cond = CompileRvalue[Form[1]; SymTab] + _Body = CompileRvalues[Form.Elems[Range[3; Size[Form]]]; SymTab] + } + with + -- Classification + IsRvalue = {proc {_} as True} + IsLvalue = {proc {_} as False} + -- Execution + Execute = {proc {Self; Ctx} as: if Execute[Self[_Cond]@; Ctx] then Execute[Self[_Body]@; Ctx]} + -- Compilation + Compile' = CompileApply + } + else + : signal CompileError with "invalid form" + } + } + + +{%endraw%}{%include spec_footer.md%} diff --git a/specification/core-language/index.html b/specification/core-language/index.html new file mode 100644 index 0000000..789e164 --- /dev/null +++ b/specification/core-language/index.html @@ -0,0 +1,16 @@ +--- +# specification/core-language/index.html +layout: null +--- + + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/specification/core-language/program-units.md b/specification/core-language/program-units.md new file mode 100644 index 0000000..79c82d4 --- /dev/null +++ b/specification/core-language/program-units.md @@ -0,0 +1,21 @@ +--- +# specification/core-language/program-units.md +title: Program Units -- Specification +updated: 2019-12-22 +--- + + + +{%include spec_header.md%}{%raw%} + + +Under construction! + + +{%endraw%}{%include spec_footer.md%} diff --git a/specification/core-language/semantic-concepts.md b/specification/core-language/semantic-concepts.md new file mode 100644 index 0000000..7d67702 --- /dev/null +++ b/specification/core-language/semantic-concepts.md @@ -0,0 +1,211 @@ +--- +# specification/core-language/semantic-concepts.md +title: Semantic Concepts -- Specification +updated: 2019-12-21 +--- + + + +{%include spec_header.md%}{%raw%} + + +Memory Model +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +### Values, objects, special entities ################################################################################## + +Conceptually, MANOOL programs operate with dynamically [typed][types] (and therefore _tagged_)[^1] values, which are _first-class_ citizens in the language --- +it is possible (at run-time and technically, also at compile-time) to + * construct values dynamically (out of other, computed values, for example), + * [assign](#) them to [variables], + * pass as [arguments](#) to [procedures](#), + * [return](#) as results of computations, + +and so forth. Here are some examples of values: + * [integer numbers](BasicDataTypes.html#h:integer "Integer") (such as `3`, `8`, or `10`), + * [character strings](BasicDataTypes.html#h:string "String") (such as `"foo"` or `"Hello, world!"`), + * procedures, + * [input/output streams](#) (or rather references to them), + * [dynamically managed variables](#) (or rather pointers to them). + +[^1]: Thus, the integral value `3`, for example, with the tag (type) `Integer`, is distinct from the integral value "3" resulting from an evaluation of the + expression `F64[3]` (which evaluates to a value of the type `Float64`). + +Any [variable][variables] or [aggregate component](#) that holds some value requires physical _resources_ to account for that fact.[^2] Values in MANOOL are to +be understood merely as mathematical abstractions; in practice, values under the hood are rather represented and materialized as _objects_ --- first-class +entities that encapsulate resources. + +[^2]: Resources are in many cases just memory blocks but are not restricted to that. + +However, exactly which particular object represents a given value should be regarded as internal representation details of the (value/object) abstract data +type. In other words, the (object) [identity](#) between any two objects should be undetectable by (and mostly irrelevant for) MANOOL programs unless it is the +same as the (value) [equality](#) or [equivalence](#) between the same two objects. In this document we prefer to talk in terms of values as long as object +identity is irrelevant to the discourse and in terms of objects otherwise. + +Note that: + * Physical resources may be [shared](#) (fully or partially) among two or more variables or aggregate components that hold the same value (or even different + values in case of partial sharing). + * Completely different resources may represent the same value. + * Different resources that represent the same value may be laid out in different structures (reflecting, for example, the history of their formation). + +--- The intention of the MANOOL design is that exactly what resources are used should be normally irrelevant to what MANOOL programs compute; however, it may +still affect how fast they respond or how much memory they consume. + +In MANOOL there is an important kind of (compile-time) entities, called _special entities_, which unlike objects are second-class citizens[^3] in the language +--- they may only be [bound](#) (similar to objects) to identifiers, by using [static](#) [bindings](#), and denoted at compile-time by non-value +[expressions](#). Entities made available from the MANOOL [standard library](#) as [`if`](#), [`while`](#), etc. as well as [modules](#) and [macros](#) are all +examples of special entities. + +[^3]: In particular, second-class entities cannot be assigned to variables, passed as arguments to procedures, or returned as results of computations. + +### Immutability/mutability of objects ################################################################################# + +Compare objects that represent integer numbers, character strings, input/output streams, and dynamically managed variables. The first two are examples of +_immutable_ objects. Conversely, the last two are examples of _mutable_ objects, whose externally observable _state_ can be altered in the course of program +execution and/or compilation. + +Often, an immutable object models some abstract mathematical entity, whereas a mutable object models some real-world artifact (especially under object-oriented +programming paradigm). Note that irrespective of mutability, in MANOOL it is possible otherwise to treat both kinds of objects in the same way. + +### Variables, evaluation contexts ##################################################################################### + +A _variable_ (or more accurately, a _temporary variable_) in MANOOL is a stateful (i.e., mutable) second-class entity that references some value (and therefore +some object), called the (current) _value of the variable_, at any given moment in the course of program execution and/or compilation. The current value of a +variable can be replaced by another value at any time (e.g., as a side effect resulting from an [assignment expression](#) evaluation), and more than one +variable at a time can reference the same object (whereby allowing for object and therefore resource _sharing_). + +Since a temporary variable has a modifiable state, it resembles a mutable object. However, temporary variables (being second-class entities, unlike objects) +cannot be referred to dynamically --- a temporary variable may only be denoted by a [bound] identifier, statically (which is resolved in some sense at +compile-time)[^4]. + +[^4]: Variable bindings themselves are classified as [dynamic](#h:bindings-and-scopes-binding-environments), in spite of the qualifier "statically" used here. + +An identifier can denote a temporary variable only indirectly, relative to an _evaluation context_, which will come into existence (at run-time only) upon each +[control-flow](#) entry into the corresponding [variable-binding expression](#) and cease its existence upon the matching exit (hence the name "temporary +variable"). Note that multiple evaluation processes may have been initiated and not yet completed for the same expression at the same time (due to a possibility +of [recursion](#) or even [concurrency or thread-level parallelism](#)) and that in this way multiple evaluation contexts for the same expression and +therefore multiple variables corresponding to the same variable binding may exist simultaneously. + +Note that parameter identifiers that appear in the header of a [λ-expression](#) denote in the body of that λ-expression (except where shadowed) +regular temporary variables that provide [access](#) to the corresponding arguments (at run-time). + +Also note that MANOOL has the concept of dynamic variables, which are different from ordinary (temporary) variables described here. For more information, see +[Dynamic Variables, pointers](#). + +[bound]: #h:bindings-and-scopes-binding-environments "Bindings and scopes, binding environments" + + +### Object life-cycle and resource management ########################################################################## + +Each object, whether mutable or not, may occupy _system resources_, such as virtual memory blocks, open file descriptors, or slots in a symbol table. + +A value (or object) is said to be live (active) as long as there exists at least one variable that references it, either directly or as a container [§2.4.2] +element or component. Otherwise, it is said to be dead. Note that strictly speaking, according to these definitions, an object can sometimes be resurrected, +which does not, however, pose any issues or contradictions for the purposes of this document. The quality of liveness is not always definitive... + +At any point in time, the abstract machine shall be able to reclaim and reuse the resources occupied by dead objects. For especially critical or scarce +resources, the abstract machine shall do it for each object once its lifetime becomes ended (becomes inactive). + +### Data Types ######################################################################################################### + +In MANOOL the whole object (and equivalently, value) space is partitioned into disjoint classes called _data types_ (or, for short, _types_). If an object `x` +is said to be of the type `t`, then we write it by convention as `x:t`. Note that being of a specific type affects for an object its capability to be +(meaningfully) involved in specific computations. + + + +[types]: #h:data-types "Below: Data types" +[variables]: #h:variables-evaluation-contexts "Below: Variables, evaluation contexts" + + +Compilation and Evaluation Model +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +### Bindings and scopes, binding environments ########################################################################## + +MANOOL is a block-structured programming language with static scoping --- each occurrence of an identifier in a program refers to a statically apparent binding +of that identifier. To _bind_ refers to an act of associating an identifier (represented by a [symbol]) with a meaning in a syntactically limited portion of +program code, called a _scope_. A _binding_ is a syntactic construct (which is a fragment of a [binding expression](#)) that represents such association. + +Bindings are classified into _static bindings_ and _dynamic bindings_, and by default each symbol in a program denotes itself (or if you like, is bound to +itself) unless it starts with an ASCII lowercase letter (`a`, `b`, `c`, ... , `z`). + +In case of a static binding, the meaning is either an object or a special entity. Static bindings may appear in [`let`-expressions](#).[^5] Using a module also +introduces static bindings into the scope, whereas an [`export`-expression](#) is used to record in a module specified static bindings for future reference. + +[^5]: Note that [`let`-`rec`-expressions](#) cannot bind special entities. + +In case of a dynamic binding, the identifier refers to a [temporary variable]. Dynamic bindings may appear in [`var`-forms](#), [`for`-forms](#), and as +parameters in parameter lists of [`proc`-forms](#). + +The set of all effective bindings at some point in a program is known as the _binding environment_ in effect at that point. In block-structured programming +languages, a (new) binding may (temporarily) _shadow_ a currently effective binding (if any) for the same identifier in the scope of the new binding. + +By convention, a symbol that denotes a special entity should not start with an ASCII uppercase letter (`A`, `B`, `C`, ... , `Z`) and should not start with a +lowercase letter (`a`, `b`, `c`, ... , `z`) otherwise. A symbol that starts with an underscore (`_`) should denote an [uninterned symbol][symbol]. + +[symbol]: BasicDataTypes.html#h:symbol "Symbol" +[temporary variable]: #h:variables-evaluation-contexts "Variables, evaluation contexts" + +### Forms, expressions, control flow ################################################################################### + +A _form_ in MANOOL is just a syntactic construct that is intended to be compiled (as a whole) into a run-time representation; the term "a form" may also refer +to an AST representation of such syntactic construct. When considered within a specific binding environment in which it is to be compiled, a form is referred to +as an _expression_, and the term may also refer to the corresponding run-time representation itself. + +The notions of form and expression are context-dependent --- the same construct may or may not be considered a form or expression depending on its role in a +program or even the programmer's intent. For example, for a program unit consisting of the following expression: + + {{extern "manool.org:18/std/0.2/all"} in: proc {X; Y; Z} as X + Y + Z} + +the following constituents are expressions: + * `{extern "manool.org:18/std/0.2/all"}`, + * `extern`, + * `"manool.org.18/std/0.6/all"`, + * `proc {X; Y; Z} as X + Y + Z`, + * `proc`, + +as well as `X + Y + Z`, `X + Y`, `X`, `Y`, `Z`, and both occurrences of `+`, whereas the following constructs (and their constituents, if any) are mere +fragments of forms: `in`, `{X; Y; Z}`, `as`. + +Note that although the construct `{X; Y; Z}` by itself looks like a valid expression (which might be re-written also as `X[Y; Z]`), it is actually a fragment +due to its location and consequently, role.[^6] + +[^6]: Also note that, for instance, `Y + Z` is not a syntactic construct at all; it is just an arbitrary source text fragment, due to left associativity of the + operator `(+)`. + +The classification of syntactic constructs into forms and form fragments reflects how the translator core of the abstract machine works, which is formally +specified [metacircularly](#). Forms, represented as ASTs, undergo directly a minimal analysis by the translator core, whereas fragments are not. The meaning of +each form can be determined only by considering the binding environment but otherwise independently of its placement in the code. + +An _r-value expression_ is an expression that may occur on the right-hand side in an [assignment expression](#) and contains instructions for computation: an +r-value expression evaluates to a first-class value/object and may optionally engender computation side effects during its evaluation. + +An _l-value expression_ is an expression that may occur on the left-hand side in an assignment expression: an l-value expression designates a virtual location +and provides instructions for storing an object into it. The currently stored object can be updated at any moment in the course of program compilation or +execution. Every l-value expression is also an r-value expression, and its evaluation yields the object currently stored in the virtual location. Thus, l-value +expressions may actually have different roles in MANOOL programs (either as an l-value or as an r-value expression). + +The _control flow_ is said to enter or to exit an expression upon initiating or completing, respectively, of either of the following: + * the expression evaluation, + * updating a virtual location designated by that expression, or + * moving out the current value of the virtual location designated by that expression. + +Unlike an r-value expression, a _non-value expression_ is an expression that instead of evaluating to a first-class value/object resolves at compile-time to a +special entity (hence the term "non-value"), for example: + + if + {{extern "manool.org.18/std/0.6/all"} in if} + {macro: proc {F} as: if Size[F] <> 2 then {array} else F[1]#} + +A form or expression that consists only of a literal or operator (when considering its AST representation) is called _primitive_ and _compound_ otherwise. + +A compound expression whose first element is an r-value expression is called an _applicative expression_ and a _special expression_ otherwise. + + +{%endraw%}{%include spec_footer.md%} diff --git a/specification/core-language/syntax.md b/specification/core-language/syntax.md new file mode 100644 index 0000000..dd12ea8 --- /dev/null +++ b/specification/core-language/syntax.md @@ -0,0 +1,350 @@ +--- +# specification/core-language/syntax.md +title: Syntax -- Specification +updated: 2019-12-22 +--- + + + +{%include spec_header.md%}{%raw%} + + +Metanotation +---------------------------------------------------------------------------------------------------------------------------------------------------------------- +[expression]: /specification/core-language/semantic-concepts#h:forms-expressions-control-flow "Forms, expressions, control flow" +[value]: /specification/core-language/semantic-concepts#h:values-objects-special-entities "Values, objects, special entities" +[standard library]: /specification/standard-library/#start "Standard Library" + +A combination of the following metalanguages and metalinguistic formalisms is used in this section: + * plain English, + * MANOOL [expressions][expression] (whereby involving a kind of metacircular description), + * _regular expressions_, and + * a formal _context-free grammar_[^a1] with attributes (i.e., _semantic values_). + +[^a1]: ... which is unambiguous and actually belongs to the LALR class of grammars ... + +All MANOOL expressions involved as metacircular descriptions are to be considered in the binding environment of the MANOOL [standard library]. + +### Regular expressions ################################################################################################ + +A regular expression `regexp` that describes a _lexical category_ `L` (which corresponds to a _terminal symbol_ from the context-free grammar) is +provided according to the following format: + + L -> regexp [[ ... ]] + +as in the following example: + + L -> ( | "_") ( | "_" | )* [[ {if E <> "_" then MakeSym[E] else MakeSym[]} ]] + +Alternatively, a lexical category may be denoted simply as `"chars"L` as in the example: + + ";"L -> ";" [[ Nil ]] + +Auxiliary (helper) definitions may also be provided, e.g.: + + -> "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" + +Inside `regexp` + * a single literal character or a sequence thereof is enclosed in either `"` or `'` (double or single quotes), + * a mere concatenation of subexpressions means sequencing, + * `*` (an asterisk metaoperator) means repetition zero or more times of the preceding item, + * `|` (a vertical bar) separates syntactic alternatives (and is read "or"), + * `*` bind stronger than `|` and plain concatenations whereas the later bind stronger than `|`, + * `(` and `)` (parentheses) are used for explicit grouping subexpressions, and + * `...` (ellipses) are used to represent "obvious" omissions and have a rather informal character. + +At the end of each "lexical production" a _semantic evaluation function_ as a MANOOL [expression] is provided enclosed in `[[` and `]]` whose purpose is to +describe construction of the corresponding lexical (semantic) [value] (i.e., an attribute value from the standpoint of attribute grammars) out of the lexeme +text, denoted as `E` (from "[lexical **e**lement]"). + +### The context-free grammar ########################################################################################### + +Each production of the context-free grammar for a _nonterminal symbol_ (i.e., a _syntactic category_) `S` is provided according to the following +format: + + S -> rhs [[ ... ]] + +where no metaoperators occur except `|` (which is just an optional shorthand notation to represent several alternative productions for the same left-hand +side). + +Inside all regular expressions and productions, `""` means "ε" (i.e., the empty string) and is useful to reinforce clarity. + +Again, at the end of each production a semantic evaluation function as a MANOOL [expression] may be provided enclosed in `[[` and `]]` that constructs the +corresponding semantic [value] out of the semantic values of constituents, accessible as elements of the array `A` (from "**a**ttributes"). In this case each +corresponding symbol in the right-hand-side of the production is followed by the corresponding index between `[` and `]` (brackets), as in the example: + + S -> S[0] ";"L S[2] [[ MakeList[{array of A[0]} + A[2]] ]] + +The default semantic evaluation function is assumed to be simply `A[0]`. + + +Lexical Structure +---------------------------------------------------------------------------------------------------------------------------------------------------------------- +[lexical **e**lement]: #h:lexical-structure "Below: Lexical Structure" + +During the [translation phase] of lexical analysis, the abstract machine decomposes the input character string into a concatenation of _lexical elements_ and +_lexical separators_ (or just _separators_, for short). The later are ignored except as they serve to separate the former. At least one separator is required +between an integer or symbol [literal][literals] and a nearby integer or symbol literal.[^b1] + +[^b1]: This rule ensures that the decomposition is unique. + +A lexical separator is either a [whitespace] character or a [comment][comments]. + +Lexical elements are classified into + * [literals] ([integer][integer literals], [string][string literals], and [symbol][symbol literals]), + * [infix operators] (equivalence/association, relational, additive, and multiplicative), + * [unary operators] (prefix and postfix), and + * [delimiters and punctuators][delimiters, punctuators]. + +### Source character set and encoding ################################################################################## +[whitespace]: #h:source-character-set-and-encoding "Below: Source character set and encoding" + +This specification deliberately makes no provisions for particular character sets or encodings to be used to interpret program units as text files. Moreover, it +is generally possible to represent MANOOL programs as series of bytes (octets) in a manner agnostic to character set and character encoding. Such +representation, however, shall cover the ASCII character set and shall be backward compatible with ASCII in respect to character encoding. As an extreme +example, it would be perfectly valid to assume the UTF-8 character encoding for some source code fragments and ISO-8859-1 for other fragments, even in the same +source file. + +Program units are composed only of the following graphical ASCII characters: + +* 26 uppercase letters: + + A B C D E F G H I J K L M N O P Q R S T U V W X Y Z + +* 26 lowercase letters: + + a b c d e f g h i j k l m n o p q r s t u v w x y z + +* 10 decimal digits: + + 0 1 2 3 4 5 6 7 8 9 + +* 30 special characters: + + ! " # $ % & ' ( ) * + - . / : ; < = > ? @ [ \ ] ^ _ { | } ~ + +plus the following _whitespace_ ASCII characters: + * `SP` --- space; + * `HT` --- tab; + * `VT` --- vertical tab; + * `FF` --- form feed; + * `CR` --- carriage return; + * `LF` --- line feed (_line separator_); + +except that [string literals] and [comments] may contain any additional characters (regardless of encoding). + +As a special case, the abstract machine considers a zero byte (corresponding to the ASCII `NUL` character) to be an optional end-of-file marker (effectively +ignoring it and the rest of the file). A line separator character at end of file in a program unit is recommended but not required as per this specification. + +Note that MANOOL is a case-sensitive language, so the abstract machine considers lowercase and uppercase letters as distinct for all purposes. Also note that +the following two characters are illegal in program units anywhere outside of string literals and comments and are reserved for future use: + * , --- comma; + * ` --- ASCII grave accent or backquote. + +### Literals ########################################################################################################### +[literals]: #h:literals "Below: Literals" + +In this section the following helper definitions hold: + + -> "A" | "B" | "C" | ... | "Z" | "a" | "b" | "c" | ... | "z" + -> "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" + -> ... + +#### Integer literals +[integer literals]: #h:integer-literals "Below: Integer literals" +[integer]: /specification/standard-library/basic-data-types#h:integer "Integer Type" + +An _integer literal_ consists of one or more decimal digits and denotes a lexical value of type [Integer] by using the conventional decimal notation: + + L -> * [[ Int[E] ]] + +Examples: + + 0 1 2 5 10 123 2018 140737488355327 + +#### String literals +[string literals]: #h:string-literals "Below: String literals" +[string]: /specification/standard-library/basic-data-types#h:string "String Type" + +A _string literal_ consists of zero or more characters --- other than double quotes (`"`) and line separators --- enclosed in double quotes. With the quotes +stripped off, a string literal denotes a lexical value of type [String], as is: + + L -> '"' * '"' [[ E[Range[1; Size[E] - 1]] ]] + +Examples: + + "" "foo" "This is a string" "manool.org.18/std/0.6/all" + +Alternatively, `\}` and `\{` may be used instead of double quotes.[^b2] In this case the lexical value may contain anything except `\{`. Thus, + + \} +

This is a paragraph.

+ \{ + +is equivalent to + + (Lf + Sp + Sp + "

This is a paragraph.

" + Lf)$ + +[^b2]: That's right --- a closing brace is used to start a literal and an opening brace to end it. This notation is convenient for "embedding" MANOOL in text + documents as a "macro" language in the spirit of PHP, JSP, ASP, etc. + +#### Symbol literals +[symbol literals]: #h:symbol-literals "Below: Symbol literals" +[symbol]: /specification/standard-library/basic-data-types#h:symbol "Symbol Type" + +A _symbol literal_ consists of one or more letters, digits, and underscore characters (`_`) and starts with a letter or underscore. Each occurrence in a program +unit of a symbol literal denotes a lexical value of type [Symbol]. For any symbol literal distinct from a single underscore, the value is obtained by +constructing a symbol out of the literal (i.e., straight out of the text); otherwise, the value is represented by a unique uninterned symbol generated by the +abstract machine during the lexical analysis phase on each occurrence of the literal: + + L -> ( | "_") ( | "_" | )* [[ {if E <> "_" then MakeSym[E] else MakeSym[]} ]] + +Examples: + + A WriteLine extern Log10 _ _Num + +Note that negative integers, some categories of strings, extremely long strings, and some categories of symbols have no literal representation whatsoever. + +### Infix operators #################################################################################################### +[infix operators]: #h:infix-operators "Below: Infix operators" + +An _infix operator_ is similar to a symbol literal, although unlike the later it consists of one or two special characters and is classified from the syntactic +analysis standpoint as either `L`, `L`, `L`, or `L`: + +* Equivalence/association operator: + + L -> "=" [[ Sym[E] ]] + +* Relational operators: + + L -> "==" | "<>" | "<" | "<=" | ">" | ">=" [[ Sym[E] ]] + +* Additive operators: + + L -> "+" | "-" | "|" [[ Sym[E] ]] + +* Multiplicative operators: + + L -> "*" | "/" | "&" [[ Sym[E] ]] + +### Unary operators #################################################################################################### +[unary operators]: #h:unary-operators "Below: Unary operators" + +A _unary operator_ is similar to a symbol literal, although unlike the later it consists of a single special character and is classified from the syntactic +analysis standpoint as either `L` or `L`: + +* Prefix operator: + + L -> "~" [[ Sym[E] ]] + +* Postfix operators: + + L -> "!" | "#" | "$" | "%" | "'" | "?" | "@" | "^" [[ Sym[E] ]] + +### Delimiters, punctuators ############################################################################################ +[delimiters, punctuators]: #h:delimiters-punctuators "Below: Delimiters, punctuators" + +The only _delimiter_ in MANOOL is `;`: + + ";"L -> ";" [[ Nil ]] + +_Punctuators_ in MANOOL are + + ( ) . : [ ] { } + +Delimiters and punctuators lack any meaningful lexical value and serve only for grouping syntactically other values and overriding operator precedence and +associativity. + +### Comments ########################################################################################################### +[comments]: #h:comments "Below: Comments" + +MANOOL syntax allows for two kinds of comments: _line comments_ and _block comments_. + +#### Line comments + +A line comment starts with two adjacent `-` (ASCII hyphen-minus or dash) characters and extends up to the nearest end of line. Here is an example of a line +comment: + + -- This is a line comment + +#### Block comments + +Block comments may be recursively nested inside one another. A block comment starts with a combination of `/*` (ASCII solidus or slash plus asterisk) characters +and extends up through the matching combination `*/`, which shall not immediately precede an asterisk. Inside a block comment, combinations `/*` and `*/` do not +start or end a nested comment when encountered within what would look like a line comment or string literal if it had occurred outside of a comment. This +applies even to malformed string literals implicitly terminating at end of line.[^b3] This is a complete, properly terminated block comment (provided no +asterisk immediately follows it): + + /* This is a block comment + */*** This is a nested comment + -- This is a line ***/comment/*** + Out.WriteLine["Comments terminate on */ and start on /*"] + "Malformed ***/string literal/*** ends here -> + end of comment ***/ + end of comment */ + +[^b3]: Thus, block comments are ideal for temporary commenting out fragments of source code (e.g., while debugging) but may be used for other purposes as well. + + +Syntactic Analysis +---------------------------------------------------------------------------------------------------------------------------------------------------------------- +[array]: /specification/standard-library/composite-data-types#h:array "Array Type" + +On the second [translation phase], the abstract machine parses the input in the form of string of terminal symbols according to the context-free grammar +provided below, builds up a parse tree, and then constructs the AST intermediate representation. + +Context-free grammar (with attributes and semantic evaluation functions): + + S -> S + S -> S + S -> S[0] L[1] S[2] [[ MakeList[{array of A[1]; A[0]; A[2]}] ]] +^ + S -> S + S -> S[0] L[1] S[2] [[ MakeList[{array of A[1]; A[0]; A[2]}] ]] +^ + S -> S + S -> S[0] L[1] S[2] [[ MakeList[{array of A[1]; A[0]; A[2]}] ]] +^ + S -> S + S -> S[0] L[1] S[2] [[ MakeList[{array of A[1]; A[0]; A[2]}] ]] +^ + S -> S + S -> L[0] S[1] [[ MakeList[{array of A[0]; A[1]}] ]] +^ + S -> S + S -> S[0] "["L S[2] "]"L [[ MakeList[{array of A[0]} + A[2]] ]] + S -> S[0] "."L S[2] "["L S[4] "]"L [[ MakeList[{array of A[2]; A[0]} + A[4]] ]] + S -> S[0] L[1] [[ MakeList[{array of A[1]; A[0]}] ]] +^ + S -> L[0] [[ A[0] ]] | "("L S[1] ")"L [[ A[1] ]] + S -> "{"L S[1] "}"L [[ A[1] ]] | "("L S[1] ")"L [[ A[1] ]] +^ + S -> L | L | L | L | L | L +^ + S -> S | "" [[ MakeList[{array}] ]] +^ + S -> S[0] S[1] [[ MakeList[{array of A[0]} + A[1]] ]] + S -> S[0] ";"L S[2] [[ MakeList[{array of A[0]} + A[2]] ]] +^ + S -> S | "" [[ MakeList[{array}] ]] +^ + S -> S[0] S[1] [[ MakeList[{array of A[0]} + A[1]] ]] + S -> S[0] ";"L S[2] [[ MakeList[{array of A[0]} + A[2]] ]] + S -> S[0] ":"L S[2] [[ MakeList[{array of A[0]; A[2]}] ]] + + where `MakeList` is some (pure) function, and for any [array] `A` of ASTs, the following condition is met: + + MakeList[A].IsList[] & (Size[MakeList[A]] == Size[A]) & {for {E1 = MakeList[A]; E2 = A} all E1 == E2} + + +[translation phase]: /specification/general#h:translation-overview "Translation Overview" + +{%endraw%}{%include spec_footer.md%} diff --git a/specification/general.md b/specification/general.md new file mode 100644 index 0000000..7a8cf7d --- /dev/null +++ b/specification/general.md @@ -0,0 +1,118 @@ +--- +# specification/general.md +title: General -- Specification +updated: 2019-12-21 +--- + + + +{%include spec_header.md%}{%raw%} + + +Definitions +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +To avoid any misunderstandings, let's agree on some important definitions that hold throughout this specification: + +A _syntax_ of a formal language (such as a programming language) is a set of rules that determine how to discover a _syntactic structure_ of any phrase in that +language, where a syntactic structure of a phrase is a tree-like (i.e., hierarchical) structure induced on that phrase (in the form of a character string) that +enables formulation of the _meaning_ of the phrase (or a constituent phrase thereof) by means of a straightforward recursive definition.[^a1] Sometimes +different syntaxes may adequately describe the same programming language, and the syntax is normally specified by a context-free grammar plus lexical rules. + +[^a1]: For instance, according to these definitions, the syntax of [Lisp]-family programming languages ([Common Lisp], [Scheme], [Clojure], [Kernel], etc.) is + considered to be *exclusively* the syntax of S-expressions (sometimes called the surface or concrete syntax), regardless of any further (structural) + requirements placed on the corresponding Lisp data (i.e., the abstract syntax trees), since in practice, the later provide sufficient guidance to deduce + the meaning of a phrase in Lisp by *straightforward recursion* (anyway). + +A _semantics_ of a formal language is a set of rules that determine how to discover the meaning of any phrase in that language. + +Note that these definitions may be at odds with your intuition and may slightly differ from the corresponding definitions as they appear in some other contexts +(such as specifications of other programming languages, theoretic and applied linguistics, etc.). + + +General Program Structure +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +A program in MANOOL consists of one or more source files, referred to as _program units_ (or more accurately, _native program units_), each written in the +formal language of MANOOL [forms]. Thus, the MANOOL specification concerns, in fact, with syntactic structure and meaning of MANOOL forms (or equally, with +syntax and semantics of the language of forms). + +A MANOOL program contains a designated _main program unit_ and all program units it depends on, either directly or indirectly (that is, recursively). The +presence of circular dependencies between program units would result in a meaningless program and even may be a cause of a non-terminating behavior thereof. + +Note that program units written in MANOOL may also depend on _foreign program units_, implemented in other programming languages. For more information on +program units and their dependencies, refer to [Program Units]. + +[forms]: /specification/core-language/semantic-concepts#h:forms-expressions-control-flow "Forms, expressions, control flow" +[Program Units]: /specification/core-language/program-units#start "Program Units" + + +The Abstract Machine +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +This specification introduces the concept of a fictional device implementing MANOOL, called the _abstract machine_, and in a few occasions its structure and +behavior are discussed explicitly. This is for illustration purposes only; by no means the MANOOL specification places requirements on either structure or +internal mode of operation of conforming implementations, which instead are to emulate the observable behavior of the abstract machine.[^c1] This in practice +extends to its asymptotic complexity characteristics whenever such characteristics are explicitly specified. + +[^c1]: This principle is also known as the "as-if" principle. + + +Translation Overview +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +To figure out the meaning of a form that makes up a program unit, the abstract machine transforms (compiles) the contents of the source file into an internal +run-time representation, called _run-time code_. + +Note that here the distinction between a compilation phase and a post-compilation (i.e., execution) phase is introduced not just for illustration purposes --- +in particular, some constituent [expressions] may actually need to be evaluated (once!) [during compilation] of the whole expression.[^d1] In this specification +a compilation phase is referred to hereinafter as _compile-time_ whereas a post-compilation phase as _run-time_. + +[^d1]: Thus, strictly speaking, several compilation and execution phases may be interleaved in time. + +A three-stage translation (i.e., compilation) scheme is suggested for the abstract machine: + +* _lexical analysis_ --- The input string of characters is split into [lexical elements] (lexemes), whose meaning is then encoded in left-to-right order as a + sequence of tokens.[^d2] Note that in practice, whatever internal syntactic structure of individual lexemes is devised, it is generally unimportant for + determination of their meaning; rather, the lexical syntax is used for their sheer classification. + +* _syntactic analysis_ --- The string of terminal symbols that corresponds to the sequence of tokens resulting from the previous compilation phase undergoes a + [syntactic analysis] guided by a context-free grammar, which ultimately yields an _abstract syntax tree_ (AST) encoded as a MANOOL (semantic) [value]. Note + that in contrast to lexical analysis, here syntactic structure is essential for correct interpretation of source code. + +* _semantic analysis_ and _code generation_ --- The form and consistency (e.g., the presence and placement of certain keywords) of the AST resulting from the + previous compilation phase are checked, and finally, the run-time code is produced.[^d3] Note that no new structural features are to be exposed on this stage, + or they would at least reflect closely those of the AST. + +[^d2]: Each of the tokens belongs to some class, encoded as a [terminal symbol] from the [syntactic analysis] standpoint, and has an optional semantic (MANOOL) + [value]. + +[^d3]: In MANOOL (as opposed to other languages and with a notable exception of those based on the notation of S-expressions) many aesthetic aspects of source + code that are traditionally examined on the syntactic analysis stage are irrelevant to the language syntax. + +Semantic analysis and code generation is a compositional process; that is, to carry out the semantic analysis and code generation for a form (encoded in an +AST), the abstract machine performs (among other things) the semantic analysis and code generation for its constituent forms (represented by some subtrees of +the original AST). For a description of this process, refer to [Compiler Dispatcher]. + +[expressions]: CoreSemantics#h:forms-expressions-control-flow "Forms, expressions, control flow" +[during compilation]: # +[terminal symbol]: Syntax#h:metanotation "Metanotation" +[value]: CoreSemantics#h:values-objects-special-entities "Values, objects, special entities" +[lexical elements]: Syntax#h:lexical-structure "Lexical Structure" +[syntactic analysis]: Syntax#h:syntactic-analysis "Syntactic Analysis" +[Compiler Dispatcher]: /specification/core-language/compiler-dispatcher#start + + +[Lisp]: //en.wikipedia.org/wiki/Lisp_(programming_language) "Wikipedia: Lisp" +[Common Lisp]: //en.wikipedia.org/wiki/Common_Lisp "Wikipedia: Common Lisp" +[Scheme]: //en.wikipedia.org/wiki/Scheme_(programming_language) "Wikipedia: Scheme" +[Clojure]: //en.wikipedia.org/wiki/Clojure "Wikipedia: Clojure" +[Kernel]: http://klisp.org "klisp - a Kernel Programming Language implementation" + +{%endraw%}{%include spec_footer.md%} diff --git a/specification/standard-library/assignments-explicit-sequencing.md b/specification/standard-library/assignments-explicit-sequencing.md new file mode 100644 index 0000000..cc05234 --- /dev/null +++ b/specification/standard-library/assignments-explicit-sequencing.md @@ -0,0 +1,13 @@ +--- +# specification/standard-library/assignments-explicit-sequencing.md +title: Assignments, Explicit Sequencing -- Specification +updated: 2019-12-22 +--- + +{%include spec_header.md%}{%raw%} + + +Under construction! + + +{%endraw%}{%include spec_footer.md%} diff --git a/specification/standard-library/basic-data-types.md b/specification/standard-library/basic-data-types.md new file mode 100644 index 0000000..2883599 --- /dev/null +++ b/specification/standard-library/basic-data-types.md @@ -0,0 +1,1608 @@ +--- +# specification/standard-library/basic-data-types.md +title: Basic Data Types -- Specification +updated: 2019-11-05 +--- + +{%include spec_header.md%}{%raw%} + + +This section describes the most fundamental non-composite data types provided by the MANOOL standard library. + +Metanotation +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +The following metalinguistic formalisms and metalanguages are used in this section: + * plain English, + * basic mathematical notation, + * tree regular grammars, + * simple patterns and templates for MANOOL r-value expressions (namely, operation invocations) with pattern/template variables (also known as + _metavariables_). + +An invocation template looks just like a normal operation invocation augmented with metalinguistic _placeholders_ (i.e., variables that represent invocation +arguments) and a result data type --- separated by `=>` --- plus a result name when it helps clarity. Names of metalinguistic variables are all lowercase and +are followed by a datatype after a `:` character (except for dispatch control parameters where the data type is implied and except for type-unrestricted +parameters). The result name (if present) precedes the result type and a `:` character. For each invocation template a semantic description is provided. + + +Integer +---------------------------------------------------------------------------------------------------------------------------------------------------------------- +[integer literals]: /specification/core-language/syntax#h:integer-literals "Integer literals" + +The type `Integer` corresponds to the (finite) set of integral numbers in the range -(247-1) thru +(247-1) (i.e., -140737488355327 thru ++140737488355327), inclusive.[^1] Objects of type `Integer` (and values they represent) are often referred to simply as _integers_, and like most abstract +mathematical entities, integers are [immutable]. Non-negative integers can have [integer literals] in MANOOL programs. + +[^1]: The value -247, typical for two's complement binary representations, is excluded from this range. + +All arithmetic and comparison operations on integers shall have constant asymptotic time complexity. + +### Constructors ####################################################################################################### + + ****************************************************************************** + I48[S::String] => Integer + + -- evaluates to an integer that represents the value specified by the argument in the conventional[^2] + + [^2]: This notation is also stipulated by [C] and [POSIX] specifications. + + * decimal notation: + + ("+" | "-" | ) ( | "0")* + + * hexadecimal notation: + + ("+" | "-" | ) "0" ("x" | "X") * + + * or octal notation: + + ("+" | "-" | ) "0" * + + where + + -> "1" | "2" | "3" | ... | "9" + -> "0" | "1" | "2" | ... | "9" | "A" | "B" | "C" | ... | "F" | "a" | "b" | "c" | ... | "f" + -> "0" | "1" | "2" | ... | "7" + + **time complexity**: unspecified; **example**: `I48["123"] => 123` + + ****************************************************************************** + I48[I::Integer] => JustI + + -- evaluates to the argument itself (this constructor is provided merely for completeness) + +### Type predicate ############################################################# + IsI48[Object] => Boolean + +### Polymorphic operations ############################################################################################# + + ****************************************************************************** + X + Y::Integer => Integer + + -- addition + + ****************************************************************************** + X - Y::Integer => Integer + + -- subtraction + + ****************************************************************************** + X * Y::Integer => Integer + + -- multiplication + + ****************************************************************************** + X / Y::Integer => Integer + + -- integer division -- the fractional part of the real result is truncated, e.g. `8 / 3 => 2`, `8 / ~3 => ~2` + + ****************************************************************************** + x.Rem[y:Integer] => Integer + + --- remainder of `(/)` --- equivalent to + + {unless 0 <> y signal Undefined else x - x / y * y} + + ****************************************************************************** + x.Div[y:Integer] => Integer + + --- floored division --- the real result is floored, e.g.: + + (8.Div[3] == 2) & (8.Div[~3] == ~3) + + ****************************************************************************** + x.Mod[y:Integer] => Integer + + --- modulo (i.e., remainder of `Div`) --- equivalent to + + {unless 0 <> y signal Undefined else x - x.Div[y] * y} + + ****************************************************************************** + Neg[X] => Integer, ~X => MinusX::Integer + + -- negation (unary minus) + + ****************************************************************************** + X == Y => Boolean + + --- comparison for equality + + ****************************************************************************** + x <> y => Boolean + + --- comparison for inequality + + ****************************************************************************** + x < y => Boolean + + --- comparison for "less than" + + ****************************************************************************** + x <= y => Boolean + + --- comparison for "less than or equal" + + ****************************************************************************** + x > y => Boolean + + --- comparison for "greater than" + + ****************************************************************************** + x >= y => Boolean + + --- comparison for "greater than or equal" + + ****************************************************************************** + Order[x; y:Integer] => Integer + + --- total order/equivalence relation --- equivalent to + + {if x < y then ~1 else: if x > y then 1 else 0} + + ****************************************************************************** + Abs[x] => Integer + + --- absolute value (magnitude, modulus) + + ****************************************************************************** + Str[x] => String + + --- decimal representation, e.g.: + + (Str[10] == "10") & (Str[~3] == "-3") + + ****************************************************************************** + Str[x; format:String] => String + + --- argument string representation formatted according to a [C]/[POSIX] `printf` [format specifier] for the `int` type, with the leading `%` character + stripped, e.g.: + + (Str[10; "3d"] == " 10") & (Str[10; "+03d"] == "+010") + + ****************************************************************************** + Clone[x] => just-x:Integer, DeepClone[x] => just-x:Integer + + -- evaluate to the argument itself + +### Exceptions ################################################################# +* `Overflow` --- no object of type `Integer` can represent the mathematical result of the operation (e.g., when calculating the product of 2147483648 by + 2147483648) +* `DivisionByZero` --- attempting to divide a dividend different from zero by zero +* `Undefined` --- attempting to divide zero by zero +* `SyntaxError` --- a string representation or format specifier is malformed (has invalid syntax) +* `UnrecognizedOperation` +* `InvalidInvocation` +* `TypeMismatch` +* `HeapExhausted` +* `StackOverflow` +* `LimitExceeded` + + +String +---------------------------------------------------------------------------------------------------------------------------------------------------------------- +[string literals]: /specification/core-language/syntax#h:string-literals "String literals" + +The type `String` corresponds to the (infinite) set of (finite) sequences of 0 or more elements (octets)[^3] drawn from a (finite) alphabet of 28 +(i.e., 256) elements. Objects of type `String` are often referred to simply as _strings_ (or more accurately, _raw strings_), and like most abstract +mathematical entities, strings are [immutable]. Strings can have [literal representation][string literals] in MANOOL programs in most practically interesting +cases. + +[^3]: In practice, however, there will be always some dynamic limit imposed on the maximum size of strings representable in computer memory at any given moment. + +Typically, strings are not directly interpreted in accordance with the above definition. Instead, raw strings should contain actual character strings encoded +using some specific character encoding, like ASCII, UTF-8, ISO-8859-1, or even UCS-32. However, by themselves and when considered outside of any additional +context, raw strings are otherwise character-encoding agnostic and even might be used to manipulate non-character data, such as the contents of arbitrary binary +files. + +String octets are indexed starting from zero and are accessible as [Unsigned] values from `U32[0]` to `U32["0xFF"]`, inclusive. + +### Constructors ####################################################################################################### + + ****************************************************************************** + S8[S::String] => JustS::String + + -- evaluates to the argument itself (this constructor is provided merely for completeness) + +### Named constants #################################################################################################### + Nul == "" | U32[ 0] + Bel == "" | U32[ 7] + Bs == "" | U32[ 8] + Ht == "" | U32[ 9] + Lf == "" | U32[10] + Vt == "" | U32[11] + Ff == "" | U32[12] + Cr == "" | U32[13] + Esc == "" | U32[27] + Sp == "" | U32[32] -- equivalent to " " + Qq == "" | U32[34] -- double quote + +### Type predicate ############################################################# + IsS8[Object] => Boolean + +### Polymorphic operations ############################################################################################# + + ****************************************************************************** + S[Index::Integer] => Octet::Unsigned + + -- octet at the specified position; + **time complexity**: O(1); **example**: + + "Hello, world!"[7] => U32[119] + + ****************************************************************************** + S[Indexes::Range] => Substring::String, S[Indexes::RevRange] => Substring::String + + -- substring of octets in the specified range of positions (reversed for `RevRange`); + **time complexity**: O(`Size[Indexes]`); **examples**: + + "Hello, world!"[Range[7; 12]] => "world", "Hello, world!"[RevRange[7; 12]] => "dlrow" + + ****************************************************************************** + S.Repl[Index::Integer; Octet::Unsigned] => ModS::String + + -- evaluates to a string that represents the original string value with the specified octet replaced by the specified new value; + **time complexity**: O(1) for an unshared object `S`, O(`Size[S]`) otherwise; **example**: + + "Hello, world!".Repl[7; "W"[0]] => "Hello, World!" + + ****************************************************************************** + S.Repl[Indexes::Range; Substring::String] => ModS::String, S.Repl[Indexes::RevRange; Substring::String] => ModS::String + + -- equivalent to + + S[Range[Lo[Indexes]] + Substring + S[Range[Hi[Indexes]; Size[S]] + + or (respectively) + + S[Range[Lo[Indexes]] + Substring[RevRange[Size[Substring]]] + S[Range[Hi[Indexes]; Size[S]] + + ****************************************************************************** + Size[S] => Integer + + -- string size (length, number of elements); + **time complexity**: O(1); **example**: + + Size["Hello, world!"] => 13 + + ****************************************************************************** + X + Y::String => Concat::String + + -- concatenation; + **time complexity**: amortized O(`Size[Y]`) for an unshared object `X`, O(`Size[X]` + `Size[Y]`) otherwise; **example**: + + "Hello, " + "world!" => "Hello, world!" + + ****************************************************************************** + S | Octet::Unsigned => Concat::String + + -- concatenation with a single element; + **time complexity**: amortized O(1) for an unshared object `S`, O(`Size[S]`) otherwise; **example**: + + "Hello, world" | "!"[0] => "Hello, world!" + + ****************************************************************************** + Elems[S] => JustS + + -- evaluates to the argument itself + + ****************************************************************************** + S.Elems[Indexes::Range] => Slice::Iterator, S.Elems[Indexes::Range] => Slice::Iterator + + -- evaluates to a slice iterator that represents a lazily evaluated substring of octets in the specified range of positions (reversed for `RevRange`); + **time complexity**: O(1); **see** [slice iterators](#) + + ****************************************************************************** + Keys[S] => Indexes::Range + + -- evaluates to a (forward) range that represents element indexes + **time complexity**: O(1) + + ****************************************************************************** + S.Keys[Indexes::Range] => JustIndexes, S.Keys[Indexes::RevRange] => JustIndexes + + -- evaluates to the specified range of element indexes + + ****************************************************************************** + S^ => JustS + + -- evaluates to the argument itself + + ****************************************************************************** + X == Y => Boolean + + -- comparison for equality; + **time complexity**: O(`Size[X]` + `Size[Y]`) if `Y` is a string, O(1) otherwise; **example**: + + "Hello, world!" == "Hello, world!" => True, "123" == 123 => False + + ****************************************************************************** + X <> Y => Boolean + + -- comparison for inequality; + **time complexity**: O(`Size[X]` + `Size[Y]`) if `Y` is a string, O(1) otherwise; **example**: + + "Hello, world!" <> "Hello, world!" => False, "123" <> 123 => True + + ****************************************************************************** + Order[X; Y] => Integer + + -- total order/equivalence relation --- `~1` iff `X` lexicographically precedes `Y`, `1` iff `Y` lexicographically precedes `X`, and `0` otherwise + **time complexity**: O(`Size[X]` + `Size[Y]`); **examples**: + + Order["Hello"; "Hello"] => 0, Order["Hello"; "world"] => ~1, Order["world", "Hello"] => 1 + + ****************************************************************************** + Str[S] => JustS + + -- evaluates to the argument itself + + ****************************************************************************** + Str[S; Format::String] => String + + -- argument string representation formatted according to a [C]/[POSIX] `printf` [format specifier] for the `const char *` type, with the leading `%` character + stripped; + **time complexity**: unspecified; **examples**: + + Str["Hello"; "6s"] => " Hello", Str["Hello"; "-6s"] => "Hello " + + ****************************************************************************** + Clone[S] => String, DeepClone[S] => String + + -- evaluates to an (initially) unshared string that represents the same value as the argument + **time complexity**: O(1) for an unshared object `S`, O(`Size[S]`) otherwise; **examples**: + + Clone["Hello"] => "Hello", DeepClone["Hello"] => "Hello" + +### Exceptions ################################################################# +* `IndexOutOfRange` +* `ConstraintViolation` +* `UnrecognizedOperation` +* `InvalidInvocation` +* `TypeMismatch` +* `LimitExceeded` +* `HeapExhausted` +* `StackOverflow` + + +Symbol +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Objects of type `Symbol` are often referred to simply as _symbols_. Symbols resemble strings --- they can represent arbitrary sequences of octets (with very few +exceptions). However, symbols are different from strings in either the set of basic operations the MANOOL standard library provides for these types, their exact +behavior, or performance characteristics of some operations. Symbol is one of the fundamental data types in MANOOL (like [Integer](#h:integer "Above: Integer") +and [String](#h:string "Above: String")). + +Compared to general-purpose strings, symbols are intended to identify other entities in a given context by using symbolic names (except uninterned symbols), and +their internal representation shall be optimized for this purpose.[^4] In particular, symbols shall be comparable in constant time and also shall be able to be +used as keys in certain lookup operations with constant-time complexity (so-called symbol-table lookup operations). The cost of such advantage may be paid at +the moment of construction of a symbol, which may take longer than for strings. + +[^4]: There may be imposed some implementation-defined limit on the total number of alive symbols that can exist at any given moment in the course of program + execution and/or compilation. + +There are two kinds of symbols: _interned symbols_ and _uninterned symbols_. The former can be obtained by conversion from strings, whereas the later +cannot --- they can only be generated (by invocations of `MakeSym`): + +### Constructors ####################################################################################################### + + ****************************************************************************** + -> ' + + --- see [Metaprogramming](MetaProg.html) + + ****************************************************************************** + MakeSym[s:String] => Symbol + + --- unless the argument starts with `, evaluates to an interned symbol that represents the same sequence of octets as the argument + + ****************************************************************************** + MakeSym[] => Symbol + + --- evaluates to an (randomly generated) uninterned symbol that is not alive at the moment of the evaluation + +### Named constants #################################################################################################### +In MANOOL programs a symbol (name) that has not been explicitly bound denotes itself unless it starts with an ASCII lowercase letter. Note that in neither case +symbol construction is completely referentially transparent. + +### Type predicates ############################################################ + IsSym[object] => Boolean + +### Polymorphic operations ############################################################################################# + + ****************************************************************************** + x == y => Boolean + + --- comparison for equality + + ****************************************************************************** + x <> y => Boolean + + --- comparison for inequality + + ****************************************************************************** + Order[x; y:Symbol] => Integer + + --- total order/equivalence relation (once a symbol is obtained for the first time in the course of program execution or compilation or resurrected, its order + with respect to other alive symbols is established in an implementation-defined way) + + ****************************************************************************** + op[...] + + --- polymorphic operation on the arguments + + ****************************************************************************** + Str[s] => String + + --- string representation (e.g., `Str[Foo'] == "Foo"`, whereas `Str[MakeSym[]]` returns something random like "`25120") + ****************************************************************************** + Clone[s] => just-s:Symbol, DeepClone[s] => just-s:Symbol + + evaluates to the argument itself + +### Exceptions ################################################################# +* `ConstraintViolation` +* `UnrecognizedOperation` +* `InvalidInvocation` +* `TypeMismatch` +* `HeapExhausted` +* `StackOverflow` +* `LimitExceeded` + + +Boolean +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +There are only two objects of type `Boolean`---these are truth values _true_ and _false_, which are bound to the names `True` and `False` in the MANOOL standard +library, respectively. + +### Named constants ############################################################ + True + False + +### Type predicates ############################################################ + IsBool[object] => Boolean + +### Polymorphic operations ############################################################################################ + + ****************************************************************************** + x & y:Boolean => Boolean + + --- conjunction (logical "and") + + ****************************************************************************** + x | y:Boolean => Boolean + + --- disjunction (logical "or") + + ****************************************************************************** + ~x => Boolean + + --- negation (logical "not") + + ****************************************************************************** + x.Xor[y:Boolean] => Boolean + + --- logical exclusive "or" + + ****************************************************************************** + x == y => Boolean + + --- comparison for equality + + ****************************************************************************** + x <> y => Boolean + + --- comparison for inequality + + ****************************************************************************** + Order[x; y:Boolean] => Integer + + --- total order/equivalence relation: + + Order[False; False] == Order[True; True] == 0, Order[False; True] == ~1, Order[True; False] == 1 + + ****************************************************************************** + Str[x] => String + + --- representation as a string: + + Str[True] == "True", Str[False] == "False" + + ****************************************************************************** + Clone[x] => just-x:Boolean, DeepClone[x] => just-x:Boolean + + --- evaluates to the argument itself + +### Exceptions ################################################################# +* `UnrecognizedOperation` +* `InvalidInvocation` +* `TypeMismatch` +* `HeapExhausted` +* `StackOverflow` +* `LimitExceeded` + + +Null +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +There is just one object of type `Null` --- _nil_, which is bound to the name `Nil` in the MANOOL standard library. + +### Named constants ############################################################ + Nil + +### Type predicates ############################################################ + IsNull[object] => Boolean + + --- provided merely for completeness --- equivalent to `Nil == object` + +### Polymorphic operations ############################################################################################# + + ****************************************************************************** + Nil == y => Boolean + + --- comparison for equality to `Nil` + + ****************************************************************************** + Nil <> y => Boolean + + --- comparison for inequality to `Nil` + + ****************************************************************************** + Nil^, Set[Nil; object] + + --- `IndirectionByNil` is raised + + ****************************************************************************** + Order[Nil; Nil] => 0 + + --- total order/equivalence relation + + ****************************************************************************** + Str[Nil] => "Nil" + + --- representation as a string + + ****************************************************************************** + Clone[Nil] => Nil, DeepClone[Nil] => Nil + + --- evaluates to the argument itself + +### Exceptions ################################################################# +* `IndirectionByNil` +* `UnrecognizedOperation` +* `InvalidInvocation` +* `TypeMismatch` +* `HeapExhausted` +* `StackOverflow` +* `LimitExceeded` + + +Floating-Point +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +The type Floating-Point (or more accurately, Binary Floating-Point) corresponds to a (finite) set of real numbers and special values representable according to +the IEEE 754-2008 standard[^5] in a base-2 format, including all normal and subnormal numbers and (positive and negative) zeros and excluding not-a-numbers +(NaNs) and (positive and negative) infinities. Like most abstract mathematical entities, objects of type Binary Floating-Point are [immutable]. + +[^5]: ... also designated as ISO/IEC/IEEE 60559:2011 and as ANSI/IEEE 754-2008 ... + +All basic operations on Binary Floating-Point values provided by the MANOOL standard library shall be performed in conformance with IEEE 754-2008 unless +explicitly required otherwise. The effective rounding mode for all calculations shall be always "round to nearest, ties to even" (which has no relation to the +rounding performed by such operations as `Trunc` and `Round`). + +### Constructors ####################################################################################################### + +There are actually two disjoint Binary Floating-Point types (one for each of the two common base-2 formats specified in IEEE 754-2008): 32-bit +(single-precision) Floating-Point and 64-bit (double-precision) Floating-Point. In the following chart these types are denoted as `Float32` and `Float64`, +respectively: + + ***************************************************************************** + F64[s:String] => Float64 + F32[s:String] => Float32 + + --- evaluates to a 64-bit or 32-bit Floating-Point object, respectively, that represents the value specified by the argument in the conventional decimal + notation: + + ("+" | "-" | ) ( * ("." * | ) | "." *) (("e" | "E") ("+" | "-" | ) * | ) + + where + + -> "0" | "1" | "2" | ... | "9" + + ***************************************************************************** + F64[i:Integer] => Float64 + F32[i:Integer] => Float32 + + --- equivalent to `F64[Str[i]]` or `F32[Str[i]]`, respectively + +### Type predicates ############################################################ + IsF64[object] => Boolean + IsF32[object] => Boolean + +### Polymorphic operations ############################################################################################# + +In the following chart `Float` refers to the underlying Binary Floating-Point type, and `t` stands for the corresponding constructor (i.e., either `F32` or +`F64`): + + ***************************************************************************** + x + y:Float => Float + + --- addition + + ***************************************************************************** + x - y:Float => Float + + --- subtraction + + ***************************************************************************** + x * y:Float => Float + + --- multiplication + + ***************************************************************************** + x / y:Float => Float + + --- division + + ***************************************************************************** + x.Rem[y:Float] => Float + + --- remainder of integer division, e.g.: + + F64["9.5"].Rem[F64["3.5"]] == F64["2.5"] + + ***************************************************************************** + Neg[x] => Float, ~x => minus-x:Float + + --- negation (unary minus) + + ***************************************************************************** + x.Fma[y:Float; z:Float] => Float + + --- fused multiply-add operation --- computes `y`*`z`+`x` avoiding undue loss of precision + + ***************************************************************************** + x == y => Boolean + + --- comparison for equality + + ***************************************************************************** + x <> y => Boolean + + --- comparison for inequality + + ***************************************************************************** + x < y:Float => Boolean + + --- comparison for "less than" + + ***************************************************************************** + x <= y:Float => Boolean + + --- comparison for "less than or equal" + + ***************************************************************************** + x > y:Float => Boolean + + --- comparison for "greater than" + + ***************************************************************************** + x >= y:Float => Boolean + + --- comparison for "greater than or equal" + + ***************************************************************************** + Order[x; y:Float] => Integer + + --- total order/equivalence relation --- `~1` iff `x` is ordered before `y`, `1` iff `y` is ordered before `x`, and `0` otherwise + + ***************************************************************************** + Abs[x] => Float + + --- absolute value (magnitude, modulus) + + ***************************************************************************** + Sign[x] => Float + + --- sign function --- for non-zero arguments evaluates to `t[1]`/`~t[1]` iff the argument is positive/negative, respectively; otherwise, evaluates to the + argument itself (i.e., either `t[0]` or `~t[0]`) + + ***************************************************************************** + Sign[x; y:Float] => copysign:Float + + --- evaluates to a value with the magnitude of `x` and the sign of `y` + + ***************************************************************************** + Exp[x] => Float + + --- base-e exponential of the argument + + ***************************************************************************** + Expm1[x] => Float + + --- computes the base-e exponential of the argument, minus 1, with a greater accuracy than `Exp[x] - t[1]` does + + ***************************************************************************** + Log[x] => Float + + --- base-e (natural) logarithm of the argument + + ***************************************************************************** + Log1p[x] => Float + + --- computes the base-e (natural) logarithm of 1 plus the argument with a greater accuracy than `Log[t[1] + x]` does + + ***************************************************************************** + Log[base; x:Float] => Float + + --- equivalent to `Log[x] / Log[base]` + + ***************************************************************************** + Log10[x] => Float + + --- computes the base-10 logarithm of the argument (with a greater accuracy than `Log[t[10]; x]` does) + + ***************************************************************************** + Log2[x] => Float + + --- computes the base-2 logarithm of the argument (with a greater accuracy than `Log[t[2]; x]` does) + + ***************************************************************************** + Sqr[x] => Float + + --- square (`x`2, `x`*`x`) + + ***************************************************************************** + Sqrt[x] => Float + + --- square root of the argument + + ***************************************************************************** + Hypot[x; y:Float] => Float + + --- computes the square root of the sum of the squares of both arguments avoiding undue overflow + + ***************************************************************************** + Cbrt[x] => Float + + --- cube root of the argument + + ***************************************************************************** + x.Pow[y:Float] => Float + + --- computes `x` raised to the power `y` + + ***************************************************************************** + Sin[x] => Float + + --- sine of an angle in radians + + ***************************************************************************** + Cos[x] => Float + + --- cosine of an angle in radians + + ***************************************************************************** + Tan[x] => Float + + --- tangent of an angle in radians + + ***************************************************************************** + Asin[x] => Float + + --- arcsine in radians + + ***************************************************************************** + Acos[x] => Float + + --- arccosine in radians + + ***************************************************************************** + Atan[x] => Float + + --- arctangent in radians + + ***************************************************************************** + Atan[y; x:Float] => atan2:Float + + --- computes the value of the arctangent of `y`/`x` using the signs of both arguments to determine the quadrant of the result + + ***************************************************************************** + Sinh[x] => Float + + --- hyperbolic sine + + ***************************************************************************** + Cosh[x] => Float + + --- hyperbolic cosine + + ***************************************************************************** + Tanh[x] => Float + + --- hyperbolic tangent + + ***************************************************************************** + Asinh[x] => Float + + --- inverse hyperbolic sine + + ***************************************************************************** + Acosh[x] => Float + + --- inverse hyperbolic cosine + + ***************************************************************************** + Atanh[x] => Float + + --- inverse hyperbolic tangent + + ***************************************************************************** + Erf[x] => Float + + --- error function of the argument + + ***************************************************************************** + Erfc[x] => Float + + --- complementary error function of the argument (equivalent to: `t[1] - Erf[x]`) + + ***************************************************************************** + Gamma[x] => Float + + --- gamma function of the argument + + ***************************************************************************** + Lgamma[x] => Float + + --- computes the natural logarithm of the gamma function of the argument avoiding undue overflow + + ***************************************************************************** + Jn[x; n:Integer] => Float + + --- Bessel function of the first kind of order `n` of `x` + + ***************************************************************************** + Yn[x; n:Integer] => Float + + --- Bessel function of the second kind of order `n` of `x` + + ***************************************************************************** + Trunc[x] => Float + + --- evaluates to the argument with the fractional part discarded + + ***************************************************************************** + Round[x] => Float + + --- evaluates to the argument rounded to the nearest integral value, half ties toward infinity, e.g.: + + (Round[t[".5"]] == t[1]) & (Round[~t[".5"]] == ~t[1]) + + ***************************************************************************** + Floor[x] => Float + + --- evaluates to the nearest integral value less than or equal to the argument + + ***************************************************************************** + Ceil[x] => Float + + --- evaluates to the nearest integral value greater than or equal to the argument + + ***************************************************************************** + Int[x] => Integer + + --- `Trunc[x]` represented as an object of type `Integer` + + ***************************************************************************** + Str[x] => String + + --- decimal representation, e.g.: + + Str[F64["10.5"]] == "1.0500000000000000e+01" + + ****************************************************************************** + Str[x; format:String] => String + + --- argument string representation formatted according to a [C]/[POSIX] `printf` [format specifier] for the `double` type, with the leading `%` character + stripped, e.g.: + + (F64["10.5"].Str["6.2f"] == " 10.50") & (F64["10.5"].Str["9.2E"] == " 1.05E+01") + + ****************************************************************************** + Clone[x] => just-x:Float, DeepClone[x] => just-x:Float + + --- evaluates to the argument itself + +### Exceptions ################################################################# +* `Overflow` --- the rounded mathematical result of the operation does not fit into the destination floating-point format (e.g., when evaluating + `Exp[F64[1000]]`) +* `DivisionByZero` --- the mathematical result of the operation is undefined and the function or operation has a pole for the exact value of the argument (e.g., + when evaluating `F64[1] / F64[0]` or `Log[F64[0]]`) +* `Undefined` --- in any other case when the mathematical result of the operation is undefined (e.g., when evaluating `F64[0] / F64[0]`, `Sqrt[~F64[1]]`, or + `Log[~F64[1]]`) +* `SyntaxError` --- a string representation or format specifier is malformed (has invalid syntax) +* `UnrecognizedOperation` +* `InvalidInvocation` +* `TypeMismatch` +* `HeapExhausted` +* `StackOverflow` +* `LimitExceeded` + + +Decimal Floating-Point +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +The type Decimal Floating-Point corresponds to the (finite) set of floating-point values representable according to the [IEEE 754-2008] standard in a base-10 +format excluding negative zero(s), not-a-numbers (NaNs), and (positive and negative) infinities. Like most abstract mathematical entities, objects of type +Decimal Floating-Point are immutable. + +All basic operations on Decimal Floating-Point values provided by the MANOOL standard library shall be performed in conformance with IEEE 754-2008 unless +explicitly required otherwise. + +### Constructors ####################################################################################################### + +There are actually four disjoint Decimal Floating-Point types, one for each combination of precision (64-bit or 128-bit) and rounding mode for calculations +("round to nearest, ties away from zero" or "round to nearest, ties to even"): Common 64-bit Decimal Floating-Point, Bankers 64-bit Decimal Floating-Point, +Common 128-bit Floating-Point, and Bankers 128-bit Decimal Floating-Point, respectively. In the following chart these types are denoted as `Common64`, +`Bankers64`, `Common128`, and `Bankers128`, respectively: + + ***************************************************************************** + C64 [s:String] => Common64 + D64 [s:String] => Bankers64 + C128[s:String] => Common128 + D128[s:String] => Bankers128 + + evaluates to a Decimal Floating-Point object that represents the value specified by the argument in the conventional decimal notation (in the corresponding + format and using the corresponding rounding mode): + + ("+" | "-" | ) ( * ("." * | ) | "." *) (("e" | "E") ("+" | "-" | ) * | ) + + where + + -> "0" | "1" | "2" | ... | "9" + + ***************************************************************************** + C64 [i:Integer] => Common64 + D64 [i:Integer] => Bankers64 + C128[i:Integer] => Common128 + D128[i:Integer] => Bankers128 + + --- equivalent to `C64[Str[i]]`, `D64[Str[i]]`, `C128[Str[i]]`, or `D128[Str[i]]`, respectively + +### Type predicates ############################################################ + IsC64 [object] => Boolean + IsD64 [object] => Boolean + IsC128[object] => Boolean + IsD128[object] => Boolean + +### Polymorphic operations ############################################################################################# + +In the following chart `Decimal` refers to the underlying Decimal Floating-Point type, and `t` stands for the corresponding constructor (i.e., either `C64`, +`D64`, `C128`, or `D128`): + + ***************************************************************************** + x + y:Decimal => Decimal + + --- addition + + ***************************************************************************** + x - y:Decimal => Decimal + + --- subtraction + + ***************************************************************************** + x * y:Decimal => Decimal + + --- multiplication + + ***************************************************************************** + x / y:Decimal => Decimal + + --- division + + ***************************************************************************** + Neg[x] => Decimal, ~x => minus-x:Decimal + + --- negation (unary minus) + + ***************************************************************************** + x.Fma[y:Decimal; z:Decimal] => Float + + --- fused multiply-add operation --- computes `y`*`z`+`x` avoiding undue loss of precision + + ***************************************************************************** + x == y => Boolean + + --- comparison for equality + + ***************************************************************************** + x <> y => Boolean + + --- comparison for inequality + + ***************************************************************************** + x < y:Decimal => Boolean + + --- comparison for "less than" + + ***************************************************************************** + x <= y:Decimal => Boolean + + --- comparison for "less than or equal" + + ***************************************************************************** + x > y:Decimal => Boolean + + --- comparison for "greater than" + + ***************************************************************************** + x >= y:Decimal => Boolean + + --- comparison for "greater than or equal" + + ***************************************************************************** + Order[x; y:Decimal] => Integer + + --- total order/equivalence relation --- `~1` iff `x` is ordered before `y`, `1` iff `y` is ordered before `x`, and `0` otherwise + + ***************************************************************************** + Abs[x] => Decimal + + --- absolute value (magnitude, modulus) + + ***************************************************************************** + Exp[x] => Decimal + + --- base-e exponential of the argument + + ***************************************************************************** + Log[x] => Decimal + + --- base-e (natural) logarithm of the argument + + ***************************************************************************** + Log[base; x:Decimal] => Decimal + + --- equivalent to `Log[x] / Log[base]` + + ***************************************************************************** + Log10[x] => Decimal + + --- computes the base-10 logarithm of the argument with a greater accuracy than `Log[t[10]; x]` does + + ***************************************************************************** + Sqr[x] => Decimal + + --- square (`x`2, `x`*`x`) + + ***************************************************************************** + Sqrt[x] => Decimal + + --- square root of the argument + + ***************************************************************************** + x.Pow[y:Decimal] => Decimal + + --- computes `x` raised to the power `y` + + ***************************************************************************** + Trunc[x] => Decimal + + --- evaluates to the argument with the fractional part discarded + + ***************************************************************************** + Round[x] => Decimal + + --- evaluates to the argument rounded to the nearest integral value, half ties toward infinity, e.g.: + + (Round[t[".5"]] == t[1]) & (Round[~t[".5"]] == ~t[1]) + + ***************************************************************************** + Floor[x] => Decimal + + --- evaluates to the nearest integral value less than or equal to the argument + + ***************************************************************************** + Ceil[x] => Decimal + + --- evaluates to the nearest integral value greater than or equal to the argument + + ***************************************************************************** + Int[x] => Integer + + --- `Trunc[x]` represented as an object of type `Integer` + + ***************************************************************************** + Str[x] => String + + --- decimal representation, e.g.: + + Str[C128["99.90"]] == "99.90" + + ***************************************************************************** + Quantize[x; y:Decimal] => Decimal + + --- quantize operation as specified in IEEE 754-2008 + + ****************************************************************************** + Clone[x] => just-x:Decimal, DeepClone[x] => just-x:Decimal + + --- evaluates to an (initially) unshared Decimal Floating-Point object that represents the same value as the argument + +### Exceptions ################################################################# +* `Overflow` --- the rounded mathematical result of the operation does not fit into the destination floating-point format (e.g., when evaluating + `Exp[F64[1000]]`) +* `DivisionByZero` --- the mathematical result of the operation is undefined and the function or operation has a pole for the exact value of the argument (e.g., + when evaluating `F64[1] / F64[0]` or `Log[F64[0]]`) +* `Undefined` --- in any other case when the mathematical result of the operation is undefined (e.g., when evaluating `F64[0] / F64[0]`, `Sqrt[~F64[1]]`, or + `Log[~F64[1]]`) +* `SyntaxError` --- a string representation or format specifier is malformed (has invalid syntax) +* `UnrecognizedOperation` +* `InvalidInvocation` +* `TypeMismatch` +* `HeapExhausted` +* `StackOverflow` +* `LimitExceeded` + + +Unsigned +---------------------------------------------------------------------------------------------------------------------------------------------------------------- +[Unsigned]: #h:unsigned "Below: Unsigned" + +The type `Unsigned` corresponds to the (finite) set of integral numbers in the range 0 thru 232-1 (i.e., 0 thru 4294967295), inclusive. However, in +comparison to the case of integers, all basic arithmetic operations on `Unsigned` values are performed by modulo 232, and the standard library also +provides operations defined in terms of a positional binary representation (i.e, so-called bitwise operations). Like most abstract mathematical entities, +objects of type `Unsigned` are [immutable]. + +### Constructors ####################################################################################################### + + ****************************************************************************** + + U32[x] => Unsigned + + --- `I48[x].Mod[4294967296]` represented as an object of type `Unsigned` + +### Type predicates ############################################################ + IsU32[object] => Boolean + +### Polymorphic operations ############################################################################################# + + ****************************************************************************** + x + y:Unsigned => Unsigned + + --- addition by modulo 232 + + ****************************************************************************** + x - y:Unsigned => Unsigned + + --- subtraction by modulo 232 + + ****************************************************************************** + x * y:Unsigned => Unsigned + + --- multiplication by modulo 232 + + ****************************************************************************** + x / y:Unsigned => Unsigned, x.Div[y:Unsigned] => Unsigned + + --- integer division --- the fractional part of the real result is truncated, e.g.: + + U32[8] / U32[3] == U32[2] + + ****************************************************************************** + x.Rem[y:Unsigned] => Unsigned, x.Mod[y:Unsigned] => Unsigned + + --- remainder of `(/)` --- equivalent to + + {unless U32[0] <> y signal Undefined else x - x / y * y} + + ****************************************************************************** + Neg[x] => Unsigned + + --- negation --- two's complement (equivalent to `~x + U32[1]`) + + ****************************************************************************** + x == y => Boolean + + --- comparison for equality + + ****************************************************************************** + x <> y => Boolean + + --- comparison for inequality + + ****************************************************************************** + x < y:Unsigned => Boolean + + --- comparison for "less than" + + ****************************************************************************** + x <= y:Unsigned => Boolean + + --- comparison for "less than or equal" + + ****************************************************************************** + x > y:Unsigned => Boolean + + --- comparison for "greater than" + + ****************************************************************************** + x >= y:Unsigned => Boolean + + --- comparison for "greater than or equal" + + ****************************************************************************** + Order[x; y:Unsigned] => Integer + + --- total order/equivalence relation --- equivalent to + + {if x < y then ~1 else: if x > y then 1 else 0} + + ****************************************************************************** + Abs[x] => just-x:Unsigned + + --- evaluates to the argument itself + + ****************************************************************************** + x & y:Unsigned => Unsigned + + --- bitwise "and" + + ****************************************************************************** + x | y:Unsigned => Unsigned + + --- bitwise "or" + + ****************************************************************************** + ~x => Unsigned + + --- bitwise "not" (bitwise negation, complement) + + ****************************************************************************** + x.Xor[y:Unsigned] => Unsigned + + --- bitwise exclusive "or" + + ****************************************************************************** + Lsh[x; n:Integer] => Unsigned + + --- logical bit shift (computes `x`*2n mod 232) + + ****************************************************************************** + Ash[x; n:Integer] => Unsigned + + --- arithmetic bit shift, left for positive `n`, right for negative `n`, the MSB[^6] counts as the "sign" bit + +[^6]: most significant bit + + ****************************************************************************** + Rot[x; n:Integer] => Unsigned + + --- bit rotation, left for positive `n`, right for negative `n`, and + + Rot[x; n] == Rot[x; n.Rem[32]] + + ****************************************************************************** + Ctz[x] => Unsigned + + --- trailing zeros count, for non-zero arguments + + ****************************************************************************** + Clz[x] => Unsigned + + --- leading zeros count, for non-zero arguments + + ****************************************************************************** + Log2[x] => Unsigned + + --- equivalent to `U32[31] - Clz[x]` + + ****************************************************************************** + C1s[x] => popcount:Unsigned + + --- ones count (population count) + + ****************************************************************************** + Str[x] => String + + --- hexadecimal representation, e.g.: + + Str[U32[123]] == "0x0000006F" + + ******************************************************************************* + Str[x; format:String] => String + + --- argument string representation formatted according to a [C]/[POSIX] `printf` [format specifier] for the `unsigned` type, with the leading `%` character + stripped, e.g.: + + Str[U32[10]; "3u"] == " 10", Str[U32[10]; "04X"] == "000A" + + ****************************************************************************** + Int[x] => Integer + + --- value represented as an object of type Integer + + ****************************************************************************** + Clone[x] => just-x:Unsigned, DeepClone[x] => just-x:Unsigned + + --- evaluates to the argument itself + +### Exceptions ################################################################# +* `DivisionByZero` +* `Undefined` +* `SyntaxError` +* `UnrecognizedOperation` +* `InvalidInvocation` +* `TypeMismatch` +* `HeapExhausted` +* `StackOverflow` +* `LimitExceeded` + + +Complex +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +An object of type `Complex` (or more accurately, Binary Floating-Point Complex) consists of a pair of two Binary Floating-Point values in the same +floating-point format, which represent a complex number in Cartesian form. Like most abstract mathematical entities, objects of type Binary Floating-Point +Complex are immutable. + +### Constructors ####################################################################################################### + +Since there are two floating-point data types, there are also two corresponding complex data types. In the following chart these types are denoted as +`Complex32` and `Complex64`: + + ****************************************************************************** + Z64[re:Float64; im:Float64] => Complex64 + Z32[re:Float32; im:Float32] => Complex32 + + --- evaluates to a 64-bit or 32-bit Floating-Point Complex object, respectively, with the specified real and imaginary part + + ****************************************************************************** + Z64[re:Float64] => Complex64 + Z32[re:Float32] => Complex32 + + --- evaluates to a 64-bit or 32-bit Floating-Point Complex object, respectively, that represents the same floating-point value as the argument (with the + imaginary part set to positive zero) + + ****************************************************************************** + Z64[s:String] => Complex64 + Z32[s:String] => Complex32 + + --- evaluates to a 64-bit or 32-bitFloating-Point Complex object, respectively, that represents a value specified by the argument in the conventional decimal + notation, which can include both real and imaginary parts, separated by a comma: + + ("+" | "-" | ) ( * ("." * | ) | "." *) + (("e" | "E") ("+" | "-" | ) * | ) ("," + ("+" | "-" | ) ( * ("." * | ) | "." *) + (("e" | "E") ("+" | "-" | ) * | ) | ) + + where + + -> "0" | "1" | "2" | ... | "9" + + ****************************************************************************** + Z64[i:Integer] => Complex64 + Z32[i:Integer] => Complex32 + + --- equivalent to `Z64[Str[i]]` or `Z32[Str[i]]`, respectively + +### Type predicates ############################################################ + IsZ64[object] => Boolean + IsZ32[object] => Boolean + +### Polymorphic operations ############################################################################################# + +In the following chart `Complex` refers to the underlying `Complex` type, `Float` refers to the Binary Floating-Point type using the corresponding +floating-point format, and `t` stands for the corresponding constructor (i.e., either `Z32` or `Z64`): + + ****************************************************************************** + x + y:Complex => Complex + + --- addition + + ****************************************************************************** + x - y:Complex => Complex + + --- subtraction + + ****************************************************************************** + x * y:Complex => Complex + + --- multiplication + + ****************************************************************************** + x / y:Complex => Complex + + --- division + + ****************************************************************************** + Neg[x] => Complex, ~x => minus-x:Complex + + --- negation (unary minus) + + ****************************************************************************** + Conj[x] => Complex + + --- complex conjugate + + ****************************************************************************** + x == y => Boolean + + --- comparison for equality + + ****************************************************************************** + x <> y => Boolean + + --- comparison for inequality + + ****************************************************************************** + Order[x; y:Complex] => Integer + + --- total order/equivalence relation --- objects of a `Complex` type are ordered lexicographically with respect to their Cartesian components + + ****************************************************************************** + Re[x] => Float + + --- real part + + ****************************************************************************** + Im[x] => Float + + --- imaginary part + + ****************************************************************************** + Abs[x] => Float + + --- absolute value (modulus) + + ****************************************************************************** + Arg[x] => Float + + --- argument (phase) of a complex value + + ****************************************************************************** + Exp[x] => Complex + + --- base-e exponential of the argument + + ****************************************************************************** + Log[x] => Complex + + --- base-e (natural) logarithm of the argument + + ****************************************************************************** + Log[base; x:Complex] => Complex + + --- equivalent to `Log[x] / Log[base]` + + ****************************************************************************** + Log10[x] => Complex + + --- computes the base-10 logarithm of the argument (with a greater accuracy than `Log[t[10]; x]` does) + + ****************************************************************************** + Sqrt[x] => Complex + + --- square root of the argument + + ****************************************************************************** + x.Pow[y:Complex] => Complex + + --- computes `x` raised to the power `y` + + ****************************************************************************** + Sin[x] => Complex + + --- sine + + ****************************************************************************** + Cos[x] => Complex + + --- cosine + + ****************************************************************************** + Tan[x] => Complex + + --- tangent + + ****************************************************************************** + Asin[x] => Complex + + --- arcsine + + ****************************************************************************** + Acos[x] => Complex + + --- arccosine + + ****************************************************************************** + Atan[x] => Complex + + --- arctangent + + ****************************************************************************** + Sinh[x] => Complex + + --- hyperbolic sine + + ****************************************************************************** + Cosh[x] => Complex + + --- hyperbolic cosine + + ****************************************************************************** + Tanh[x] => Complex + + --- hyperbolic tangent + + ****************************************************************************** + Asinh[x] => Complex + + --- inverse hyperbolic sine + + ****************************************************************************** + Acosh[x] => Complex + + --- inverse hyperbolic cosine + + ****************************************************************************** + Atanh[x] => Complex + + --- inverse hyperbolic tangent + + ****************************************************************************** + Str[x] => String + + --- decimal representation, e.g.: + + Str[Exp[Z64["0,-1"]]] == "5.4030230586813977e-01,-8.4147098480789650e-01" + + ****************************************************************************** + Clone[x] => just-x:Complex, DeepClone[x] => just-x:Complex + + --- evaluates to an (initially) unshared Floating-Point Complex object that represents the same value as the argument + +### Exceptions ################################################################# +* `Overflow` --- the rounded mathematical result of the operation does not fit into the destination floating-point format (e.g., when evaluating + `Exp[F64[1000]]`) +* `DivisionByZero` --- the mathematical result of the operation is undefined and the function or operation has a pole for the exact value of the argument (e.g., + when evaluating `F64[1] / F64[0]` or `Log[F64[0]]`) +* `Undefined` --- in any other case when the mathematical result of the operation is undefined (e.g., when evaluating `F64[0] / F64[0]`, `Sqrt[~F64[1]]`, or + `Log[~F64[1]]`) +* `SyntaxError` --- a string representation or format specifier is malformed (has invalid syntax) +* `UnrecognizedOperation` +* `InvalidInvocation` +* `TypeMismatch` +* `HeapExhausted` +* `StackOverflow` +* `LimitExceeded` + + +[immutable]: /specification/core-language/semantic-concepts#h:immutabilitymutability-of-objects "Immutability/mutability of objects" + +[C]: //en.wikipedia.org/wiki/C_(programming_language "Wikipedia: C" +[POSIX]: //en.wikipedia.org/wiki/POSIX "Wikipedia: POSIX" +[format specifier]: //en.wikipedia.org/wiki/printf "Wikipedia: printf" + +{%endraw%}{%include spec_footer.md%} diff --git a/specification/standard-library/basic-input-output.md b/specification/standard-library/basic-input-output.md new file mode 100644 index 0000000..7b7231f --- /dev/null +++ b/specification/standard-library/basic-input-output.md @@ -0,0 +1,13 @@ +--- +# specification/standard-library/basic-input-output.md +title: Basic Input/Output -- Specification +updated: 2019-12-22 +--- + +{%include spec_header.md%}{%raw%} + + +Under construction! + + +{%endraw%}{%include spec_footer.md%} diff --git a/specification/standard-library/composite-data-types.md b/specification/standard-library/composite-data-types.md new file mode 100644 index 0000000..e6fd2df --- /dev/null +++ b/specification/standard-library/composite-data-types.md @@ -0,0 +1,34 @@ +--- +# specification/standard-library/composite-data-types.md +title: Composite Data Types -- Specification +updated: 2019-12-22 +--- + +{%include spec_header.md%}{%raw%} + + +In MANOOL an aggregate value (or object, depending on your point of view) incorporate other values (or objects, respectively). This normally happens +irrespective of aggregate element types and without placing restrictions on the number of elements on behalf of the aggregate type itself. Thus, for instance, +complex numbers are *not* aggregates, at least for the purposes of this section. + +In MANOOL aggregate elements shall have some default order within that aggregate, and each aggregate element shall be associated with its key, which is either a +value of arbitrary type or an integer in the range 0 thru the number of elements minus 1 (called in this case an _index_). All aggregate types should allow for +partial updates in some way in an asymptotically efficient way. + +Elements of an aggregate are sometimes also referred to as aggregate members. Note that in MANOOL it is impossible to construct an aggregate that incorporates +itself (due to non-referential semantics). + +An aggregate view is an object (or value, depending on your point of view) that provides access to elements of an aggregate (real or imaginary) as though the +view itself were an indexed aggregate; that is, by providing the `Size` (number of elements) and `Apply` (access by index) polymorphic operations. No other +requirements are placed on aggregate views (in particular, the existence of update or non-default comparison operations is not required and normally not +needed); a proper indexed aggregate can be considered an aggregate view as well (providing access to its own elements). + +Thus, an aggregate view may be + * a proper indexed aggregate, which has its own storage for the elements; + * an object that provides (on-demand) access to elements of some proper aggregate (or even a portion thereof with optional transformation applied on-demand); + or + * an object that provides access to a completely virtual (storage-less) aggregate, such as a Fibonacci sequence generated on-demand or an object that provides + an aggregate-like view onto an (practically end-less) input stream or a relational database recordset. + + +{%endraw%}{%include spec_footer.md%} diff --git a/specification/standard-library/concurrency-thread-level-parallelism.md b/specification/standard-library/concurrency-thread-level-parallelism.md new file mode 100644 index 0000000..300485d --- /dev/null +++ b/specification/standard-library/concurrency-thread-level-parallelism.md @@ -0,0 +1,13 @@ +--- +# specification/standard-library/concurrency-thread-level-parallelism.md +title: Concurrency (Thread-Level Parallelism) -- Specification +updated: 2019-12-22 +--- + +{%include spec_header.md%}{%raw%} + + +Under construction! + + +{%endraw%}{%include spec_footer.md%} diff --git a/specification/standard-library/control-flow-bindings.md b/specification/standard-library/control-flow-bindings.md new file mode 100644 index 0000000..9e58644 --- /dev/null +++ b/specification/standard-library/control-flow-bindings.md @@ -0,0 +1,13 @@ +--- +# specification/standard-library/control-flow-bindings.md +title: Control Flow, Bindings -- Specification +updated: 2019-12-22 +--- + +{%include spec_header.md%}{%raw%} + + +Under construction! + + +{%endraw%}{%include spec_footer.md%} diff --git a/specification/standard-library/custom-data-types-data-encapsulation.md b/specification/standard-library/custom-data-types-data-encapsulation.md new file mode 100644 index 0000000..8712d24 --- /dev/null +++ b/specification/standard-library/custom-data-types-data-encapsulation.md @@ -0,0 +1,13 @@ +--- +# specification/standard-library/custom-data-types-data-encapsulation.md +title: Custom Data Types, Data Encapsulation -- Specification +updated: 2019-12-22 +--- + +{%include spec_header.md%}{%raw%} + + +Under construction! + + +{%endraw%}{%include spec_footer.md%} diff --git a/specification/standard-library/custom-notation-syntactic-macros-metaprogramming.md b/specification/standard-library/custom-notation-syntactic-macros-metaprogramming.md new file mode 100644 index 0000000..2a7da54 --- /dev/null +++ b/specification/standard-library/custom-notation-syntactic-macros-metaprogramming.md @@ -0,0 +1,13 @@ +--- +# specification/standard-library/custom-notation-syntactic-macros-metaprogramming.md +title: Custom Notation (Syntactic Macros), Metaprogramming -- Specification +updated: 2019-12-22 +--- + +{%include spec_header.md%}{%raw%} + + +Under construction! + + +{%endraw%}{%include spec_footer.md%} diff --git a/specification/standard-library/dynamic-variables-pointers.md b/specification/standard-library/dynamic-variables-pointers.md new file mode 100644 index 0000000..9cdf3e3 --- /dev/null +++ b/specification/standard-library/dynamic-variables-pointers.md @@ -0,0 +1,13 @@ +--- +# specification/standard-library/dynamic-variables-pointers.md +title: Dynamic Variables (Pointers) -- Specification +updated: 2019-12-22 +--- + +{%include spec_header.md%}{%raw%} + + +Under construction! + + +{%endraw%}{%include spec_footer.md%} diff --git a/specification/standard-library/index.md b/specification/standard-library/index.md new file mode 100644 index 0000000..3f49665 --- /dev/null +++ b/specification/standard-library/index.md @@ -0,0 +1,100 @@ +--- +# specification/standard-library.md +title: Standard Library -- Specification +updated: 2019-11-7 +--- + +{%include page_header.md%}{%raw%} + +Metanotation +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +A combination of the following metalanguages and metalinguistic formalisms is used in the standard library specification: + * plain English, + * basic mathematical notation, + * (regular) tree grammars, + * MANOOL expressions with _syntactic placeholders_ (also known as _metalinguistic variables_ or _metavariables_, for short), + * patterns matching MANOOL r-value expressions (namely, operation invocations), with _argument placeholders_ intended to match invocation arguments, augmented + with data type annotations where it makes sense, + * MANOOL expressions with argument placeholders (i.e., expression templates). + +The standard library specification contains some introductory explanations in plain English followed by entries, each one describing a MANOOL feature or a group +of related features. An entry begins with either a tree grammar or an invocation pattern; a semantic description is then provided. + +### Tree grammars ###################################################################################################### + +A tree grammar resembles a traditional formal grammar, but instead of describing a set of strings it describes a set of terms (i.e, trees). That is, in place of +string concatenation, term formation is used in the process of derivation. Tree grammars are always qualified as regular because they also resemble traditional +regular grammars due to similar fundamental properties they have (which is anyway irrelevant for our purposes). + +The following is an example of a tree regular grammar (describing `if` special forms): + + -> {if then else [0] [1] ... [n-1]} + ->
+ -> + -> + +^ + + [0] [1] ... [n-1] + + means repetition 1 -- `n` times of items matching ``, whereas + + [0] ... [n-1] + + would mean repetition just 0 -- `n` times. + +### Expressions with syntactic placeholders ############################################################################ + +An example of an expression template with syntactic placeholders (used in the semantic specification for an `if` special form): + + {if then {do [0]; ...; [n-1]} else Nil} + +^ + + [0] ... [n-1] + + means repetition of all matches according to the corresponding tree grammar. + +### Invocation patterns ################################################################################################ + +Examples of invocation patterns: + +* The following is an invocation pattern with an argument placeholder `object` and a result type specified after `=>`: + + IsI48[object] => Boolean + +* And the following is a similar invocation pattern where an argument placeholder `s` is followed by a type annotation `String` after `:`: + + I48[s:String] => Integer + +* Sometimes concrete values may be specified instead of argument placeholders and result types: + + Order[Nil; Nil] => 0 + +* Dispatching arguments of polymorphic operations do not need type annotations; the type `Integer` is assumed here for the argument placeholder `x`: + + x + y:Integer => Integer + +* Results may be sometimes named to reinforce clarity: + + ~x => minus-x:Integer + +### Expression templates with argument placeholders #################################################################### + +Example of an expression template with argument placeholders `x` and `y` (from the specification of `Rem` for `Integer`): + + {unless 0 <> y signal Undefined else x - x / y * y} + + + + +Contents +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +* [Basic Data Types](BasicDataTypes.html) + +{%endraw%}{%include page_footer.md%} diff --git a/specification/standard-library/miscellaneous-features.md b/specification/standard-library/miscellaneous-features.md new file mode 100644 index 0000000..adfd0f0 --- /dev/null +++ b/specification/standard-library/miscellaneous-features.md @@ -0,0 +1,13 @@ +--- +# specification/standard-library/miscellaneous-features.md +title: Miscellaneous Features -- Specification +updated: 2019-12-22 +--- + +{%include spec_header.md%}{%raw%} + + +Under construction! + + +{%endraw%}{%include spec_footer.md%} diff --git a/specification/standard-library/out-of-memory-handling-memory-accounting.md b/specification/standard-library/out-of-memory-handling-memory-accounting.md new file mode 100644 index 0000000..957bfd3 --- /dev/null +++ b/specification/standard-library/out-of-memory-handling-memory-accounting.md @@ -0,0 +1,13 @@ +--- +# specification/standard-library/out-of-memory-handling-memory-accounting.md +title: Out-of-Memory Handling (Memory Accounting) -- Specification +updated: 2019-12-22 +--- + +{%include spec_header.md%}{%raw%} + + +Under construction! + + +{%endraw%}{%include spec_footer.md%} diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..ab532ea --- /dev/null +++ b/styles.css @@ -0,0 +1,627 @@ +--- +# styles.css +layout: null +--- + +/* Reset (some) browser defaults */ + +@media all { + body { + margin: 0; + background-color: white; + color: black; + text-size-adjust: none; /* braindamage! */ + -webkit-text-size-adjust: none; /* just in case */ + -moz-text-size-adjust: none; + -ms-text-size-adjust: none; + -o-text-size-adjust: none; + line-height: 1.3333; + } + sub, + sup { + font-size: .8464em; + } + sub { + vertical-align: -.1814em; + } + sup { + vertical-align: +.1814em; + } + h4 { + margin-top: 1em; + margin-bottom: 1em; + } + ul, + ol { + padding-left: 32px; + } + blockquote { + margin-left: 28px; + margin-right: 0; + border-left-style: solid; + border-left-width: 2px; + padding-left: 2px; + } + figure { + margin-left: 0; + margin-right: 0; + } + textarea, + iframe { + box-sizing: border-box; + } +} + +/* Font families */ + +@media all { + body { + font-family: "Source Sans Pro", "Calibri", sans-serif; + } + h1 { + font-family: "Signika", "Calibri", sans-serif; + } + code { + font-family: "Inconsolata", "Consolas", monospace; + } +} + +/* General layout */ + +@media all { + body { + min-width: 320px; + } + body > main { + z-index: 0; + } + body > * { + overflow: hidden; + position: relative; + z-index: 1; + } + aside { + overflow: hidden; + } + + body > header .flex { + display: flex; + display: -webkit-flex; + } + body > header .flex.center { + align-items: center; + -webkit-align-items: center; + } + body > header .flex > .resizable { + flex-grow: 1; + -webkit-flex-grow: 1; + -moz-flex-grow: 1; + } + body > header .center, + body > footer, + figure { + text-align: center; + } + main .right:not(aside) { + text-align: right; + } + body > header .gutter { + border-bottom-style: solid; + border-bottom-width: thin; + } + + aside, + main > * > pre, + main > * > blockquote pre { + clear: both; + } + aside > :first-child { + margin-top: 0; + } + aside > :last-child { + margin-bottom: 0; + } + pre, + main .scroll { + overflow: auto; + } + figure, + body > header .logo { + font-size: 0; + } + figure figcaption { + font-size: 14.1774px; + } + body > header img, + body > footer img { + width: auto; + } + body > header ul { + list-style-type: none; + padding-left: 0; + } + body > footer { + vertical-align: middle; + white-space: nowrap; /* just in case */ + } + body > footer > * { + margin-top: 2em; + margin-bottom: 1.5em; + } + body > footer img { + height: 1.3333em; + } +} + +/* Viewport-dependent properties */ + +@media all { /* the most compact layout */ + body, + small { + font-size: 12px; + } + main, + body > header .logo + * { + font-size: 14.1774px; /* ignore completely browser's "font size" settings */ + } + code { + font-size: 14px; + } + + body > * > * { + margin: 2px; + } + aside { + padding: 8px; + margin: 16px 8px; + } + main header { + margin-top: 2.5em; + } + main footer { + margin-bottom: 1.5em; + } + body > header img { + height: 48px; + } + body > header ul { + margin-top: 0; + margin-bottom: 0; + } + body > header li { + margin-left: 8px; + margin-right: 8px; + } + body > header li.dynamic { + display: none; + } +} +@media (min-width: 480px) { + body > header .slogan { + font-size: 14.1774px; + } + + body > * > * { + margin-left: 8px; + margin-right: 8px; + } + body > header li.dynamic { + display: list-item; + } +} +@media (min-width: 640px) { + body, + aside, + small, + main .footnotes { + font-size: 14.1774px; + } + main, + body > header .logo + *, + body > header .slogan { + font-size: 16.75px; + } + aside code, + main .footnotes code { + font-size: 14px; + } + code { + font-size: 16px; + } + + body > * > * { + margin: 8px 16px; + } + aside.right { + float: right; + width: 252px; + margin: 8px 0 8px 16px; + position: relative; + left: 8px; + } + aside { + margin-left: 16px; + margin-right: 16px; + } + main header { + margin-top: 4.5em; + } + main footer { + margin-bottom: 3.5em; + } + body > header img { + height: 64px; + } + body > header li { + margin-left: 16px; + margin-right: 16px; + } +} +@media (min-width: 840px) { + body > * > * { + margin-left: auto; + margin-right: auto; + width: 768px; + } + aside.right { + left: 16px; + } + body > header .slogan { + letter-spacing: .6667px; + } +} +@media (min-width: 918px) { + body > * > * { + width: 846px; + } +} +@media (min-width: 996px) { + body > * > * { + width: 924px; + } +} +@media (min-width: 1074px) { + body > * > * { + width: 1002px; + } +} +@media (min-width: 1152px) { + body > * > * { + width: 1080px; + } +} +@media (min-width: 1230px) { + body > * > * { + width: 1158px; + } + body > header .slogan { + word-spacing: .25em; + } + body > header ul { + margin-top: 9.5px; + margin-bottom: 9.5px; + } + body > header li { + margin-left: 12px; + margin-right: 12px; + } + body > header li a { + padding-left: 4px; + padding-right: 4px; + } +} + +/* Decorations */ + +@media all { + body > :not(main), + aside { + background-color: white; + box-shadow: 0 4px 8px rgba(0, 0, 0, .2), 0 6px 20px rgba(0, 0, 0, .19); /* from https://www.w3schools.com/css/css3_shadows_box.asp */ + } + main { + line-height: 1.4329; + } + pre { + line-height: 1.3333; + } + h1, + h2 { + border-bottom-style: solid; + border-bottom-width: thin; + overflow: hidden; + padding-left: 1em; + text-indent: -1em; + } + hr { + border-style: none; + border-bottom-style: dashed; + border-bottom-width: thin; + overflow: hidden; + } + h1, + h2, + hr, + body > header .gutter { + border-bottom-color: #B0B0B0; + } + pre, + p > code, + li > code, + table { + background-color: #F0F0F0; + } + small { + color: #808080; + } + blockquote { + border-left-color: #808080; + } + a { + color: #25405D; + border-radius: .3em; + transition: background-color 1.33s; + } + body > header a, + a.footnote, + a:hover { + text-decoration: none; + } + a.footnote { + padding-left: .1em; + padding-right: .1em; + } + a:hover { + background-color: rgba(0, 80, 112, .75); + color: white; + } + figure figcaption { + font-style: italic; + } + + :focus { + outline: thin solid #800000; + } + main .footnotes li:target { + color: #E80000; + } + + table { + border-collapse: collapse; + margin-left: auto; + margin-right: auto; + } + td, + th { + border-bottom: thin solid white; + border-right: .3em solid white; + padding-left: 0; + padding-right: 0; + } + th { + font-weight: inherit; + border-bottom-width: .3em; + padding-top: .3em; + padding-bottom: .3em; + text-align: center !important; + } + td:last-child, + th:last-child { + border-right-style: none; + } +} + +/* Viewport-dependent properties */ + +@media all { /* the most compact layout */ + aside, + pre, + p > code, + li > code, + main img, + table { + border-radius: 3px; + } + pre { + padding-top: .1em; + padding-bottom: .1em; + } +} +@media (min-width: 480px) { + aside p, + main .footnotes p { + text-align: inherit; + } + p { + text-align: justify; + } + td, + th { + padding-left: 1px; + padding-right: 1px; + } +} +@media (min-width: 640px) { + main > * > ul, + main > * > ul ul, + main > * > ol ul, + main > * > blockquote ul, + main > * > ol, + main > * > ul ol, + main > * > ol ol, + main > * > blockquote ol { + padding-left: 39px; + } + main > * > blockquote, + main > * > ul blockquote, + main > * > ol blockquote, + main > * > blockquote blockquote { + margin-left: 32px; + border-left-width: 3px; + padding-left: 4px; + } + aside, + pre, + p > code, + li > code, + main img, + table { + border-radius: 4px; + } +} +@media (min-width: 840px) { + p > code, + li > code { + white-space: nowrap; + } +} +@media (min-width: 1074px) { + main > * > pre { + padding-top: 8px; + padding-bottom: 8px; + } + main > * > pre, + main > * > blockquote pre { + padding-left: .1em; + padding-right: .1em; + } +} +@media (min-width: 1152px) { + main > * > ul > li, + main > * > ol > li, + main > * > pre { + margin-right: 39px; + } + main > * > pre, + main > * > blockquote > pre, + main > * > blockquote > blockquote > pre { + margin-left: 39px; + } +} +@media (min-width: 1230px) { + main > * > p, + main > * > hr { + margin-right: 78px; + } + main > * > ul > li, + main > * > ol > li, + main > * > pre { + margin-right: 117px; + } + main > * > blockquote, + main #markdown-toc, + main .footnotes, + h3, + h4, + main > * > blockquote > pre, + main > * > blockquote > ul > li, + main > * > blockquote > ol > li { + margin-right: 39px; + } + aside:not(.right) { + padding-left: 19.5px; + padding-right: 19.5px; + } +} + +/* Eval forms */ + +@media all { + textarea, + iframe { + border: thin solid #B0B0B0; + resize: vertical; + min-height: 100px; + background-color: white; + padding: 2px 2px; + } + textarea { + color: black; + font-family: "Inconsolata", "Consolas", monospace; + font-size: 15.5px; + } + textarea:focus { + outline-style: none; + border-color: #800000; + } + + input[type=submit], + input[type=reset] { + border-style: none; + border-radius: 10px; + background-color: #F6F6F6; + transition: background-color 1.33s; + color: #25405D; + font-family: inherit; + font-size: 14.1774px; + line-height: 1.3333; + text-shadow: 0 2px 3px rgba(0, 0, 0, .5); + vertical-align: middle; + padding: 1px 8px; + } + input[type=submit]::-moz-focus-inner, + input[type=reset]::-moz-focus-inner { + border-style: none; + } + input[type=submit]:hover, + input[type=submit]:active, + input[type=reset]:hover, + input[type=reset]:active { + background-color: rgba(0, 80, 112, .75); + color: white; + text-shadow: 0 2px 3px rgba(255, 255, 255, .75); + } + input[type=submit]:active:hover, + input[type=reset]:active:hover { + text-shadow: none; + padding-top: 2px; + padding-bottom: 0px; + } +} +@media (min-width: 1230px) { + textarea, + iframe { + padding-left: 8px; + padding-right: 8px; + } +} + +/* For printing */ + +@media print { + body > :not(main) { + display: none; + } + aside { + border: thin solid black; + box-shadow: 2px 2px #808080; + } + a { + color: inherit; + text-decoration: none; + } + pre code, + table { + font-size: 12px; + } + main .footnotes li:target { + color: inherit; + } + + textarea, + iframe { + resize: none; + } + input[type=submit], + input[type=reset] { + color: inherit; + text-shadow: none; + } +} +@media print and (min-width: 640px) { + main > * > pre code, + main > * > blockquote pre code, + table { + font-size: 14px; + } +} diff --git a/t1.md b/t1.md new file mode 100644 index 0000000..2b14f40 --- /dev/null +++ b/t1.md @@ -0,0 +1,90 @@ +--- +# t1.md +title: The Programming Language MANOOL +--- + +{%include page_header.md%}{%raw%} + + +First and foremost, I believe programming languages should never be regarded as good or bad in an absolute sense. After all, even COBOL (a language commonly +ignored and even despised by us, language designers, computer scientists, and practicing software engineers) has its ideal market niche and things that it does +right for that niche out-of-the-box. Rather, each language is just a result of many conflicting design goals and many design tradeoffs; any improvement a +language is expected to make to programming practice has to be evaluated only for specific (and often narrow) range of practical situations. + +The purpose of this language overview is to present and discuss such result for the case of MANOOL (i.e., MANOOL-1, AKA _the original_ version). Here I do not +mention deliberately many design goals and decisions or rationale of MANOOL, to make the article more concise. Instead, I provide a lot of examples giving a +taste of the language and thus making this overview immediately appealing to language geeks and curious software engineers. + +In any case, you should be informed about one irrefutable drawback of MANOOL at this moment: lack of user community and culture around it (and lack of advocacy +whatsoever). Originally, I started to work on my language to explore a specific design space in programming languages and overcome some limitations of existing +languages in situations I used to deal with in the past. (I do plan, nonetheless, to use it for my other programming tasks and would be comfortable with it. I +am also going to complete the design and implementation of a minimally different language, MANOOL-2, with slightly different tradeoffs where the ultimate goal +is run-time performance.) + +--- + +MANOOL is a general-purpose *high-level* language with largely orthogonal features and few special cases, though a substantial attention to run-time +*performance* is paid (and especially to asymptotic complexity of basic operations, which is covered by the language specification); it is specifically designed +as both an exploratory-programming and production-level language. For instance, it has by default value semantics, which is actually Copy-On-Write semantics (so +there is no need for tracing garbage collector, and resource management is more deterministic). Thus, after `A[0] = 1; B = A; A[0] = 2`, `B[0]` equals to `1`, +but the complexity of `A[0] = 1` and `B = A` is normally constant. (Similarly, after `A[0] = 1; B = A; B[0] = 2`, `A[0]` equals to `1`.) + +From the standpoint of data manipulation philosophy in MANOOL, the programmer can focus either on _values_ or _objects_ that represent them whenever greater +control over run-time performance is needed. To speed up things, there is an explicit _move_ operator to break unwanted object sharing (inspired from "move" in +C++ but a bit more straightforward), which assigns to an assignable location the value `Nil` and evaluates to its previous value. Thus, the concatenation `S = +S! + "Hello"` has amortized constant complexity (same for `A[0] = A[0]! + "Hello"`), whereas `S = S + "Hello"` has indeed linear run-time complexity. + +There is syntactic sugar: `A[0] = 1` is equivalent to `A = Repl[A!; 0; 1]`, so in-place updates can have value semantics (and amortized constant complexity) +even for user-defined datatypes (just provide the `Repl` operation). On even more rare occasions, the user may need to clone objects explicitly (because +incrementing/decrementing a refcounter for shared objects may be expensive, especially in a multithreaded setting); so he could write `V.Clone[]` (or +`Clone[V]`). To obtain reference semantics (should your programming style require it, on rare occasions), MANOOL provides explicitly a pointer type: after `A = +MakePtr[1]; B = A; A^ = 2`, `B^` equals to `2`. + +--- + +MANOOL is a general-purpose *dynamic* language; that is, it relies on run-time type checking and supports unrestricted compile-time evaluation (including +syntactic macro-expansion) and other dynamic features (including first-class _polymorphic operations_, AKA abstract methods according to OOP terminology), which +in fact makes the language more simple and uniform from its definition standpoint. From this point of view, MANOOL could be seen as an alternative to other +dynamic languages, such as Python, Ruby, JavaScript, Lua, PHP, or Scheme. + +On the other hand, syntax-wise, MANOOL is definitely on the side of Lisp-family languages (Common Lisp, Scheme, Clojure, and others), although it may be a bit +unobvious, since it uses an alternative surface syntax that can be seen as a more sugared variant of the syntax of S-expressions (like Dylan but more +streamlined). In any case, MANOOL also has some more unusual aspects described below. + +It takes a more extreme approach than even Lisp(s): Every construct in MANOOL, such as conditionals or even name bindings (except one construct: `extern`) is in +fact imported from a library, and thus alternative languages can be provided as long as the surface syntax and the core semantic aspects admit it. This is +rather the result of the implementation strategy, which vastly streamlines and modularizes the MANOOL translator. The most clear precedents of this approach are +Tcl and Forth. + +It has by default value semantics (which is actually Copy-On-Write semantics, so there's no need for a tracing garbage collector, and resource management is +more deterministic). That is, after `A[0] = 1; B = A; A[0] = 2`, `B[0]` equals to `1` (but the complexity of `A[0] = 1` and `B = A` is normally constant). + +Let's first consider the second aspect. From MANOOL's philosophy standpoint, the user can either focus on values or objects that represent them if he needs to +have greater control over run-time performance, including asymptotic complexity. To speed up things, there's an explicit move operator to break unwanted object +sharing (inspired from C++'s move but a bit more straightforward), which assigns to an assignable location the value Nil and evaluates to its previous value. +That is, S = S! + "Hello" has constant complexity (same for A[0] = A[0]! + "Hello"), whereas S = S + "Hello" has linear complexity of course. There is syntactic +sugar: A[0] = 1 is equivalent to A = Repl[A!; 0; 1], so in-place updates can have value semantics (and amortized constant complexity) even for user-defined +datatypes (just provide the Repl operation). On even more rare occasions, the user may need to clone objects explicitly (because incrementing/decrementing a +refcounter for shared objects may be expensive, especially in a multithreaded setting); so he could write V.Clone[] (or Clone[V]). To obtain reference semantics +(should your programming style require it, on rare occasions), MANOOL provides explicitly a pointer type: after A = MakePtr[1]; B = A; A^ = 2, B^ equals to 2. + +Note that this value semantics makes the language more on the functional side-effect-free side without disallowing traditional assignment altogether (would not +work well otherwise because of the move operation) and that whether two objects that represent the same value are shared (i.e., are the same object) is +irrelevant to the program semantics (only for performance). There's no official way to test for object sameness unless they have reference semantics like +pointers or are essentially mutable objects like I/O streams or unless when defining your own datatype (an abstract datatype). Also note that immutability that +arises from COW speeds up multithreaded programs (MANOOL's implementation is fully multithreaded) and simplifies aliasing analysis in real optimizing compilers. +At this moment I am implementing such optimizing compiler trying to: propagate constants and types (MANOOL as such has run-time type checking, to simplify the +language), eliminate redundant refcount increments/decrements, unbox values, and eliminate redundant malloc/free. + +The syntax of MANOOL is based on a homoiconic representation but is different from S-expressions. Many do not like it. There's no much space in MANOOL to +provide "beautiful", tailored constructs for composite data constructors, but in return it is very uniform and very extensible. For example, to construct an +array you could write {array of 1 2 3}, to construct a set: {set of 1 2 3} (in MANOOL-2 they will be: {array I64}[1 2 3] and {set I64}[1 2 3], or even (array +I64)[1 2 3] or (set I64)[1 2 3]). Unfortunately, syntax is a highly debated topic, as well as compile-time vs run-time type checking. + +More examples of syntax: {let rec Fact = {proc {N} as: if N == 0 then 1 else N * Fact[N - 1]} in ...}. In MANOOL-2 it might have a more Haskell/ML-like +appearance: let rec Fact = (proc N as: if N == 0 then 1 else N * Fact[N - 1]) in .... The later is less uniform (including due to my default indenting +principles, which I do not reproduce here), but it might be less surprising and make happy more people :-) Note that in any case all equivalent forms of coding +in MANOOL are covered by just less than 40 productions of the context-free grammar (which is of LL(2) class after usual left recursion elimination). + + +{%endraw%}{%include page_footer.md%} diff --git a/tutorial/index.html b/tutorial/index.html new file mode 100644 index 0000000..45a7326 --- /dev/null +++ b/tutorial/index.html @@ -0,0 +1,16 @@ +--- +# tutorial/index.html +layout: null +--- + + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/tutorial/lesson-1.md b/tutorial/lesson-1.md new file mode 100644 index 0000000..1f60da1 --- /dev/null +++ b/tutorial/lesson-1.md @@ -0,0 +1,335 @@ +--- +title: Lesson 1 -- Tutorial +updated: 2020-06-01 +--- + +Go to Lesson: [1] \| [2] \| [3] \| [4] \| [5] + +[1]: #start +[2]: lesson-2#start +[3]: lesson-3#start +[4]: lesson-4#start +[5]: lesson-5#start + + + +{%include page_header.md%}{%raw%} + + +Hello World +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +The following is a traditional "Hello World" program in MANOOL: + + -- Hello World program -- in "applicative" notation + {{extern "manool.org.18/std/0.6/all"} in WriteLine[Out; "Hello, world!"]} + +You can play with MANOOL examples by using the [online evaluator] or running them from the command line according to the [instructions]. Here are just a couple +of ideas about how to run MANOOL programs from the command line: + + mnlexec hello.mnl + + (assuming you have placed your source code into `hello.mnl`) or, for short scripts: + + mnlexec <(echo $'{{extern "manool.org.18/std/0.6/all"} in WriteLine[Out; "Hello, world!"]}') + + (we use `$'...'` with a leading `$` here just to be sure we can more easily escape `'` characters in Bash in the future). + +The expected output is unsurprisingly + + Hello, world! + +Using the online evaluator is convenient but has certain limitations. Whatever approach you choose, first consult [How to Download and Install +MANOOL][instructions] for more tips. + +[online evaluator]: /eval "MANOOL Online Evaluator" +[instructions]: /download "How to Download and Install MANOOL" + +#### How it works + +1. The first line `-- ...` is just a comment, which is ignored by the MANOOL translator (see later). + +2. The construct `WriteLine[Out; "..."]` on the second line resembles a *function call* in many languages, where the construct `WriteLine` would specify a + function, and `Out` and `"Hello, world!"` would be the arguments.[^a1] Here, as a side effect of an evaluation of this expression, the phrase `Hello, + world!`, specified by a string literal, eventually appears on the standard output, specified by the argument `Out`. + +3. During compilation of the whole expression `{{extern "..."} in ...}`, all identifier definitions (particularly the one for `Out`)[^a2] from the standard + library module specified by the string literal `"manool.org.18/..."` are *imported* (injected) into the scope that follows the keyword `in`.[^a3] + +[^a1]: In MANOOL ordinary functions are called _procedures_, and the construct `WriteLine[Out; "..."]` is more generally called an _applicative expression_ + (more on this later). In overall, MANOOL is a multiparadigm language with a functional core. + +[^a2]: ...or rather their analogs in MANOOL called _bindings_... + +[^a3]: `{extern "..."}` actually has its own meaning here (more on this later), but for now you can safely ignore this fact. + + + +### Alternative notations ############################################################################################## + +The following two alternative "Hello World" implementations are equivalent to the above one, up to an internal representation called abstract syntax tree: + + -- Hello World program -- OOP-ish notation (equivalent to the above, up to abstract syntax tree) + {{extern "manool.org.18/std/0.6/all"} in Out.WriteLine["Hello, world!"]} + + (note how the first argument now corresponds to a receiver, in OOP parlance), + + -- Hello World program -- LISP-ish notation (ditto) + {{extern "manool.org.18/std/0.6/all"} in {WriteLine Out "Hello, world!"}} + + (note how `WriteLine`, the _target_ of an applicative expression, is now the first element of a Lisp-inspired syntactic list)[^a4]. + +[^a4]: The later example uses the notation that reflects abstract syntax trees (ASTs) for all syntactic constructs in MANOOL in the most straightforward way + (like S-expressions in Lisps). + + + +### Code formatting recommendations #################################################################################### + +MANOOL is a free-form and case-sensitive language. The most basic principles of MANOOL code formatting are illustrated in the following example (you are invited +to see for yourself more principles in action further in this tutorial):[^a5] + + -- Most recommended formatting for multi-line expressions + { {extern "manool.org.18/std/0.6/all"} in + WriteLine[Out; "Hello, world!"] + } + +[^a5]: Nonetheless, this particular example might fit better on a single line as we've already seen. + +### Commenting code in MANOOL ########################################################################################## + +MANOOL supports two kinds of comments (please see [Comments] for a complete reference): + + {{extern "manool.org.18/std/0.6/all"} in Out.WriteLine[/*Out;*/ "Hello, world!"]} -- this is a comment + +[comments]: /specification/core-language/syntax#h:comments "Comments" + + +Combining Multiple Expressions +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Now let's assemble the `Hello, world!` phrase from several fragments. Although this may sound a bit boring, this task is perfect to illustrate how to combine +multiple applicative expressions together. + +First, to stipulate their sequential evaluation (one after another), just for the purposes of side effects (i.e., ignoring any results they return), you can +simply write down the expressions one after another, optionally delimiting them by semicolons (`;`):[^b1] + + { {extern "manool.org.18/std/0.6/all"} in -- Evaluation of multiple expressions in a row + Out.Write["Hello"]; Out.Write[", "]; Out.Write["world"]; Out.Write["!"] + Out.WriteLine[] -- produce a newline without any other output + } + + (you can replace `WriteLine` with just `Write` to avoid producing newlines). + +[^b1]: We are switching here to the "Object-Oriented" notation and multi-line formatting just to dive into more real-world code examples as soon as possible. + +Output: + + Hello, world! + +#### Using multiple arguments + +You can instead specify more than two arguments to `WriteLine` (or `Write`) to achieve the same net effect: + + { {extern "manool.org.18/std/0.6/all"} in -- Several arguments to WriteLine + Out.WriteLine["Hello"; ", "; "world"; "!"]; -- semicolon delimiters are optional + Out.WriteLine["Hello" ", "; "world" "!"] -- everywhere where they are allowed at all + } + + (note how the rule regarding optionality of semicolons applies here as well)[^b2]. + +[^b2]: Similar to the case of multiple notations, this feature is rather unintentional but may be useful in some cases. + +Output: + + Hello, world! + Hello, world! + +### String concatenation, infix operators ############################################################################## + +Or, you can apply the conventional infix operator `+` to several string values to produce a concatenation of them and output the result as usual: + + { {extern "manool.org.18/std/0.6/all"} in -- String concatenation + Out.WriteLine["Hello" + ", " + "world" + "!"] -- (true) infix notation + } + +Output: + + Hello, world! + +An infix/prefix/postfix operator in MANOOL is actually nothing more than a normal _symbol_ (like `Write`/`WriteLine`) specially recognized and "desugared" by +the MANOOL parser. To suppress such special treatment, enclose an operator in parentheses (as in `(+)`). + +To illustrate how alternative notations would work and what the properly infix notation actually maps to (preserving the original AST):[^b3] + + { {extern "manool.org.18/std/0.6/all"} in -- String concatenation -- alternative notations + Out.WriteLine[(+)[(+)[(+)["Hello"; ", "]; "world"]; "!"]] -- applicative (prefix) notation (equivalent up to AST) + Out.WriteLine[{(+) {(+) {(+) "Hello" ", "} "world"} "!"}] -- LISPish (prefix) notation (ditto) + Out.WriteLine["Hello".(+)[", "].(+)["world"].(+)["!"]] -- OOPish (infix) notation (ditto) + } + + (note that the `+` operator is, unsurprisingly, left-associative)[^b4]. + +[^b3]: The following alternative constructs actually make no sense otherwise (in most practical cases). + +[^b4]: Nonetheless, this does not affect a result of concatenation. + +Output: + + Hello, world! + Hello, world! + Hello, world! + +Simple Arithmetic +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Let's perform some simple arithmetic operations on integral numbers. + +To display results, you can directly pass the values to `WriteLine` (or `Write`), or you can first explicitly convert the values to a string representation by +applying the `Str` operation:[^b5] + + { {extern "manool.org.18/std/0.6/all"} in -- Performing Integer arithmetic operations and displaying results + Out.WriteLine["2 + 3 = " 2 + 3; ", "; "5 - 2 = " 5 - 2] -- outputting piece-wise + Out.WriteLine["2 + 3 = " + Str[2 + 3] + ", " + "5 - 2 = " + Str[5 - 2]] -- explicit conversion to String and concatenation + } + +[^b5]: MANOOL is a strongly typed language where `5` is not the same thing as `"5"`, and implicit coercion has actually limited scope. + +Output: + + 2 + 3 = 5, 5 - 2 = 3 + 2 + 3 = 5, 5 - 2 = 3 + +#### Multiplication, operator precedence and associativity + +You can also use more complex expressions involving multiple operators, which follow nearly conventional precedence and associativity rules: + + { {extern "manool.org.18/std/0.6/all"} in -- Demonstrating operator precedence (binding strength) and associativity + Out.WriteLine["2 + 3 * 4 - 5 = " 2 + 3 * 4 - 5; ", "; "(2 + (3 * 4)) - 5 = " (2 + (3 * 4)) - 5] + Out.WriteLine["(2 + 3) * (4 - 5) = " (2 + 3) * (4 - 5)] -- overriding with explicit grouping of operands + } + +All these operators associate to the left, and `*` binds tighter than `+` and `-` unless where explicitly overriden with parentheses (please refer to [Syntactic +Analysis] for the complete formal grammar of MANOOL).[^b6] + +[^b6]: MANOOL has simple operator precedence rules inspired in classic (Pascal-family) languages. In particular, unlike Lisp, Smalltalk, and APL, MANOOL does + have precedence rules, but unlike ML and Haskell it has a limited set of operators that occupy few fixed precedence levels. MANOOL uses only 4 levels for + infix operators. + +Output: + + 2 + 3 * 4 - 5 = 9, (2 + (3 * 4)) - 5 = 9 + (2 + 3) * (4 - 5) = -5 + +[syntactic analysis]: /specification/core-language/syntax#h:syntactic-analysis "Syntactic Analysis" + +#### Unary minus + +In MANOOL an arithmetic negation (or unary minus) operation on a number can be always performed by applying the symbol `Neg` to the argument. Alternatively, the +prefix operator `~` works the same way for most (but not all) types of numbers.[^b7] Here is a short example: + + {{extern "manool.org.18/std/0.6/all"} in Out.WriteLine["Neg[2] = " Neg[2]; ", "; "5 + ~2 = " 5 + ~2]} + +[^b7]: One disadvantage of the `~` operator is that it is also overloaded to mean Boolean/bitwise negation/complement for some data types, and one advantage is + that it resembles more the traditional notation `-` (which cannot be used as-is in MANOOL because of grammar ambiguity introduction in such case). + +Output: + + Neg[2] = -2, 5 + ~2 = 3 + +### Division ########################################################################################################### + +From a number-theoretic as well as practical standpoint, MANOOL provides a fairly comprehensive set of division-related operations on integral numbers:[^b8] +`(/)`, `Rem`, `Div`, and `Mod`. + +[^b8]: This is partly because MANOOL and its specification strive to be extremely accurate about the exact semantics and behavior of various operations in the + language. + +Without going into much detail about Integer division, which is beyond the scope of this tutorial, let's construct a simple program that displays on the +standard output a table that illustrates how these operations work (please see [Integer operations] for a complete reference): + + { {extern "manool.org.18/std/0.6/all"} in -- Integer division and related operations + -- Heading + Out.WriteLine[" +8,+3 -8,+3 +8,-3 -8,-3"] + -- Integer division (truncating) + Out.WriteLine[" / " (8 / 3).Str["+6d"] (~8 / 3).Str["+6d"] (8 / ~3).Str["+6d"] (~8 / ~3).Str["+6d"]] + -- Remainder from Integer division + Out.WriteLine["Rem" 8.Rem[3].Str["+6d"] (~8).Rem[3].Str["+6d"] 8.Rem[~3].Str["+6d"] (~8).Rem[~3].Str["+6d"]] + -- Flooring division + Out.WriteLine["Div" 8.Div[3].Str["+6d"] (~8).Div[3].Str["+6d"] 8.Div[~3].Str["+6d"] (~8).Div[~3].Str["+6d"]] + -- Modulo (remainder from flooring division) + Out.WriteLine["Mod" 8.Mod[3].Str["+6d"] (~8).Mod[3].Str["+6d"] 8.Mod[~3].Str["+6d"] (~8).Mod[~3].Str["+6d"]] + } + +For certain simple data types, the `Str` operation can accept an optional string argument that is a C `printf` conversion specification (excluding an initial +`%`). + +Output: + + +8,+3 -8,+3 +8,-3 -8,-3 + / +2 -2 -2 +2 + Rem +2 -2 +2 -2 + Div +2 -3 -3 +2 + Mod +2 +1 -1 -2 + +[integer operations]: /specification/standard-library/basic-data-types#h:polymorphic-operations "Integer operations" + + +Quiz +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Try to figure out what is going on here (you should have acquired all the clues after completing [Lesson 3](Lesson-3#start)): + + { {extern "manool.org.18/std/0.6/all"} in + { (WriteLine) Out -- What do Write/WriteLine return? + Out.Write["Hello"] ", " Out.Write[", "] ", " Out.Write["world"] ", " Out.Write["!"] ", " + Out.WriteLine[] + } + { (WriteLine) Out -- What are Out, WriteLine, (+)?.. Or can we print "unprintable" stuff? + Out ", " WriteLine ", " (+) ", " (~) ", " Foo ", " Bar + } + } + +Output: + + Hello, world! + Nil, Nil, Nil, Nil, Nil + value/object, WriteLine, +, ~, Foo, Bar + + +* * * * * +**[Continue](lesson-2 "Next Lesson")** + + +{%endraw%}{%include page_footer.md%} diff --git a/tutorial/lesson-2.md b/tutorial/lesson-2.md new file mode 100644 index 0000000..711dbed --- /dev/null +++ b/tutorial/lesson-2.md @@ -0,0 +1,280 @@ +--- +title: Lesson 2 -- Tutorial +updated: 2020-06-01 +--- + + + +{%include page_header.md%}{%raw%} + + +Factorial +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +The usual "Hello World" analog for functional languages consists of a recursive function definition for calculating factorial of a number. The following +variation for MANOOL incorporates in addition some test code: + + -- Factorial -- recursive version in MANOOL-ish ("cascading") notation + { {extern "manool.org.18/std/0.6/all"} in + : let rec + { Fact = -- compile-time constant binding + { proc { N } as -- precondition: 0 <= N + : if N == 0 then 1 else + N * Fact[N - 1] + } + } + in + Out.WriteLine["Factorial of 10 = " Fact[10]] + } + +And the following equivalent program (up to AST) is intended to make the syntactic structure of the above program a bit more apparent: + + -- Factorial -- recursive version in conventional notation (equivalent to the above code, up to AST) + { {extern "manool.org.18/std/0.6/all"} in + { let rec + { Fact = -- compile-time constant binding + { proc { N } as -- precondition: 0 <= N + { if N == 0 then 1 else + N * Fact[N - 1] + } + } + } + in + Out.WriteLine["Factorial of 10 = " Fact[10]] + } + } + + (here another piece of syntactic sugar is demonstrated -- any two constructs +`{`_a_0 _a_1 ... _a__m_-1 `{`_b_0 _b_1 ... _b__n_-1`}``}` and +`{`_a_0 _a_1 ... _a__m_-1`:` _b_0 _b_1 ... _b__n_-1`}` are always equivalent one to another). + +Output: + + Factorial of 10 = 3628800 + +#### How it works + +1. The expression `{proc {N} as ...}` resembles a lambda-expression in many languages, where `N` would specify a parameter and the expression that follows `as` + would be the body. The whole expression evaluates to a procedure, which returns the result of evaluation of the body.[^a1] + +2. During compilation of the expression `{let rec {Fact = ...} in ...}`, a binding between `Fact` and the entity specified on the right-hand side of the infix + operator `=` is injected into the scope that follows `in`.[^a2] Since we use `let rec` and not just `let` here, the right-hand side expression is also + included in the scope of `Fact`, so we can refer to it recursively. + +3. The construct `{if ... then ... else ...}` is a conditional expression here. During its evaluation either of the two branches is evaluated depending on + whether the condition specified between `if` and `then` holds and producing the result for the whole expression. + +[^a1]: In MANOOL, a `proc`-expression does not implicitly capture temporary variables from its initial binding environment (by default, the body of + `proc`-expression is simply excluded from the scope of such variables). This is due to a deliberate language design decision. + +[^a2]: That entity is obtained in full at compile-time. + +### Iterative version ################################################################################################## + +Although MANOOL has a functional core, it is a multiparadigm language, for which an iterative version of the factorial function, which uses a `while`-loop (or +`for`-loop), may be more appropriate:[^a3] + + -- Factorial -- iterative version (in MANOOL, this is probably more appropriate for factorial) + { {extern "manool.org.18/std/0.6/all"} in + : let + { Fact = -- compile-time constant binding + { proc { N } as -- precondition: 0 <= N + : var { Res = 1 } in -- variable binding + : do Res after -- return result + : while N <> 0 do -- loop while N not equals zero + Res = N * Res; N = N - 1 + } + } + in + Out.WriteLine["Factorial of 10 is "; Fact[10]] + } + + (the output is the same as above). + +[^a3]: The MANOOL specification does not require tail-call optimizations, though this is not the only reason. + +#### How it works + +1. During compilation of the expression `{var {Res = 1} in ...}`, the body expression(s), which follow `in`, are considered in a binding environment with a + temporary variable named `Res` injected.[^a4] The variable is initialized to `1` just before evaluating the body expression(s). + +2. The expression `{do Res after ...}` is equivalent to `{do ...; Res}`, which is evaluated by evaluating its constituents one by one, and thus this expression + evaluates to `Res`. + +3. The expression `{while ... do ...}` is a traditional `while`-loop. During its evaluation, the body expression(s), which follow `do`, are evaluated + repetitively, one by one, while the pre-condition specified between `while` and `do` holds. + +4. `Res = N * Res` and `N = N - 1` are assignment expressions. As a side effect of an evaluation of such expression, the current value of the location specified + on the left-hand side of the `=` operator is replaced with the value specified on the right-hand side of the `=` operator. + +[^a4]: Temporary variables in MANOOL (including procedure parameters) are statically scoped. + + +Value Comparisons, Data Typing Issues +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Let's see how comparison operations work in MANOOL and how the principle of strong data typing affects certain aspects of the MANOOL semantics. + +### Comparing for equality ############################################################################################# + +First, any pair of values in MANOOL (even of different types) can be always compared for equality/inequality, two values of different types simply being deemed +unequal (even if they "look" similar): + + { {extern "manool.org.18/std/0.6/all"} in + Out.WriteLine[2 == 2 ", " 2 <> 2 ", " "2" == "2" ", " "2" <> "2"] + Out.WriteLine[2 == "2" ", " 2 <> "2" ", " "2" == 2 ", " "2" <> 2] + } + +Output: + + True, False, True, False + False, True, False, True + + (In MANOOL `True` and `False` are members of a special data type _Boolean_.) + +### Other operations ################################################################################################### + +Any pair of integral values can be also compared for less-than as well as less-than-or-equal, greater-than, and greater-then-or-equal: + + { {extern "manool.org.18/std/0.6/all"} in + Out.WriteLine[2 < 3 ", " 3 < 2 ", " 2 < 2] -- less-than + Out.WriteLine[2 <= 3 ", " 2 > 3 ", " 2 >= 3] -- less-than-or-equal, greater-than, greater-then-or-equal + } + +Output: + + True, False, False + True, False, False + +On the other hand, two values of different types cannot be compared for less-than, etc.; also, the `+` operator cannot be applied to an integer and a string and +vice versa: + + {{extern "manool.org.18/std/0.6/all"} in Out.WriteLine[2 < "3"]} -- run-time error +^ + {{extern "manool.org.18/std/0.6/all"} in Out.WriteLine[2 + "3"]} -- run-time error +^ + {{extern "manool.org.18/std/0.6/all"} in Out.WriteLine["2" + 3]} -- run-time error + +Output: + + Uncaught signal TypeMismatch + ====== invocation backtrace ====== + 00 at () 1:56-1:62 evaluating + ======== end of backtrace ======== + + (The expression does not evaluate to any value here; such outcome is called in MANOOL _signaling_ an exception.) + +And in the following example, the string `"2"` does not even "know" how to compare itself for less-than with another value, even with another string: + + {{extern "manool.org.18/std/0.6/all"} in Out.WriteLine["2" < "3"]} -- run-time error +^ + {{extern "manool.org.18/std/0.6/all"} in Out.WriteLine["2" < 3]} -- run-time error + +Output: + + Uncaught signal UnrecognizedOperation + ====== invocation backtrace ====== + 00 at () 1:56-1:62 evaluating + ======== end of backtrace ======== + +### Type predicates #################################################################################################### + +For many data types in MANOOL, there exists a type predicate, a Boolean-valued procedure that determines whether its argument belongs to the underlying type: + + { {extern "manool.org.18/std/0.6/all"} in + Out.WriteLine[2.IsI48[] ", " "2".IsI48[]] -- is "2" an integer? + Out.WriteLine[2.IsS8[] ", " "2".IsS8[]] -- is "2" a string? + } + +Output: + + True, False + False, True + + +Compound Conditions +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +You can express complex conditions by using operators `&` (conjunction for Booleans), `|` (disjunction for Booleans), and `~` (negation for Booleans). The +operators `&` and `|` are short-circuiting (the right-hand side is unevaluated unless strictly necessary): + + {{extern "manool.org.18/std/0.6/all"} in Out.WriteLine["2".IsI48[] & ("2" < 3 "), " ~"2".IsI48[] | ("2" < 3)]} + +Output: + + False, True + + +More Exceptions +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +The MANOOL specification is precise about which exception is signaled in each particular erroneous case. We are already familiar with two of them: +`TypeMismatch` and `UnrecognizedOperation`. Let's look at other basic signals:[^a5] + +First, an inappropriate number of arguments is reported by signaling `InvalidInvocation` as in + + {{extern "manool.org.18/std/0.6/all"} in Neg[2; 3]} + +[^a5]: Your program in MANOOL can catch and react on any signal, and they are also used as a general control-flow mechanism. + +### Arithmetic exceptions ############################################################################################## + +Overflows during arithmetic operations are reported using signals: + + {{extern "manool.org.18/std/0.6/all"} in MaxI48 + 1} + +Signals: `Overflow` + +The same applies to division by zero and other operations near a pole of the argument (e.g. near zero for logarithms): + + {{extern "manool.org.18/std/0.6/all"} in 1 / 0} + +Signals: `DivisionByZero` + +In other cases `Undefined` may be signaled: + + {{extern "manool.org.18/std/0.6/all"} in 0 / 0} +^ + {{extern "manool.org.18/std/0.6/all"} in 1.Rem[0]} + +Signals: `Undefined` + +Quiz +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Try to figure out what is going on here (you should have acquired all the clues after completing [Lesson 3](Lesson-3#start)): + + {{extern "manool.org.18/std/0.6/all"} in Out.WriteLine[(&)]} + +Compilation error: + + () 1:42-1:62 Error: not an R-value expression (nested in this context) + +And here: + + {{extern "manool.org.18/std/0.6/all"} in Out.WriteLine[then]} + +Compilation error: + + () 1:42-1:58 Error: unbound keyword (nested in this context) + + +* * * * * +**[Continue](lesson-3 "Next Lesson")** + + +{%endraw%}{%include page_footer.md%} diff --git a/tutorial/lesson-3.md b/tutorial/lesson-3.md new file mode 100644 index 0000000..46fdbba --- /dev/null +++ b/tutorial/lesson-3.md @@ -0,0 +1,190 @@ +--- +title: Lesson 3 -- Tutorial +updated: 2020-06-01 +--- + + + +{%include page_header.md%}{%raw%} + + +Symbol Data Type +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Symbol is one of the most fundamental and versatile data types in MANOOL. `extern`, `in`, `WriteLine`, `Out`, `+`, etc. are all symbols. + + + +If an expression in MANOOL consists of a literal symbol, by default that symbol denotes itself (e.g. `WriteLine`, `(+)`, `Foo`) unless it starts with a +lowercase letter (e.g. `then`, `do`) or is bound to some other entity in the current scope (e.g. `Out`, `extern`, `if`, `(&)`). In the later case, the symbol +denotes that entity. + +Here's a quick test code: + + {{extern "manool.org.18/std/0.6/all"} in Out.WriteLine[Foo ", " Foo == "Foo" ", " Foo.IsSym[]]} + +Output: + + Foo, False, True + +Values of type Symbol can identify + * polymorphic operations (e.g. `WriteLine`, `(+)`, `(~)`, `Div`),[^a1] + * record components and object attributes, and + * any states, modes, options, tags, etc. you would like to represent by a simple identifier. + +[^a1]: Moreover, symbols *are* polymorphic operations, which are directly applied to arguments. + +To force literal interpretation of a syntactic construct in MANOOL, use the postfix operator `'` (single quote): + + { {extern "manool.org.18/std/0.6/all"} in + Out.WriteLine[Foo' ", " Out' ", " extern' ", " if' ", " (&)' ", " then'] + Out.WriteLine[Out.WriteLine["Hello, world!"]'] + } + +Output:[^a2] + + Foo, Out, extern, if, &, then + value/object + +[^a2]: The second expression tries to display a _syntactic list_ object (more on this later), and the operation `Str` returns a generic `"value/object"` for + most data types in MANOOL. + +A symbol can also be dynamically constructed from a string by using the constructor `MakeSym` and converted back to String with the operation `Str`: + + {{extern "manool.org.18/std/0.6/all"} in Out.WriteLine[MakeSym["Foo"] ", " MakeSym["Foo"] == Foo]} + +Output: + + Foo, True + + +Numbers Beyond Integers +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- + +**Caution!!! Work in progress!!!** + +--- + +You have full control over precise semantics of arithmetic operations in MANOOL. To cover a variety of practical needs and situations, the language provides +several numeric data types to choose from, such as Integer, several kinds of Floating Point with binary and decimal base, Complex, and Unsigned. In most cases, +you can expect MANOOL programs to give you reproducible numeric results regardless of peculiarities of your language implementation. + +{%endraw%}{%comment%} +The MANOOL specification places explicit requirements on precise semantics of arithmetic operations in the language; thus, MANOOL should have in most cases a +completely reproducible behavior in this respect regardless of the implementation details. +{%endcomment%}{%raw%} + +In the following examples we are going to learn about how to perform operations on fractional numbers, but first let's learn one minor but important feature: + + { {extern "manool.org.18/std/0.6/all"} in + Out.WriteLine[{do Out.WriteLine["One"]; 2 + 3}] -- do - explicit sequencing + Out.WriteLine[{do Out.WriteLine["Two"]; 5 - 2}$] -- $ - compile-time evaluation + } + +Output: + + Two + One + 5 + 3 + +Example: + + { {extern "manool.org.18/std/0.6/all"} in + -- Scientific-oriented arithmetic (base-2 internal representation) + Out.WriteLine["one quarter (F64) = " F64[".25"]$ " = " (F64[1] / F64[4])$] -- precise representation + Out.WriteLine["one tenth (F64) = " F64["1e-1"]$ " = " (F64[1] / F64[10])$] -- approximate representation + Out.WriteLine["one third (F64) = " (F64[1] / F64[3])$] -- periodic fraction (when represented in decimal form) + Out.WriteLine["one third (F32) = " (F32[1] / F32[3])$] -- ditto + -- Human-oriented arithmetic (base-10 internal representation) + Out.WriteLine["one tenth (D64) = " D64["1e-1"]$ " = " D64[".10"]$] + Out.WriteLine["one third (D64) = " (D64[1] / D64[3])$] + Out.WriteLine["one third (D128) = " (D128[1] / D128[3])$] + } + +Output: + + one quarter (F64) = 2.5000000000000000e-01 = 2.5000000000000000e-01 + one tenth (F64) = 1.0000000000000001e-01 = 1.0000000000000001e-01 + one third (F64) = 3.3333333333333331e-01 + one third (F32) = 3.33333343e-01 + one tenth (D64) = 0.1 = 0.10 + one third (D64) = 0.3333333333333333 + one third (D128) = 0.3333333333333333333333333333333333 + +### Decimal rounding ################################################################################################### + +MANOOL supports two rounding modes for decimal floating-point arithmetic: + * round to the nearest, break ties to even (also known as Bankers' rounding), and + * round to the nearest, break ties away from zero (also known as commercial rounding). + + Bankers' rounding is suitable for lengthy calculations where rounding errors should be avoided as much as possible, whereas commercial rounding is more +wide-spread in many cultures. + +MANOOL provides two groups of decimal floating-point data types, one for performing arithmetic using the Bankers' rounding mode and another for using +commercial rounding: + + { {extern "manool.org.18/std/0.6/all"} in + Out.WriteLine["Banker's rounding: " D64["2.000000000000021"]$ / D64[2]$ ", " D64["2.000000000000031"]$ / D64[2]$ + ", " D64[".25"]$.Quantize[D64[".0"]$] ", " D64[".35"]$.Quantize[D64[".0"]$]] + Out.WriteLine["Common rounding: " C64["2.000000000000021"]$ / C64[2]$ ", " C64["2.000000000000031"]$ / C64[2]$ + ", " C64[".25"]$.Quantize[C64[".0"]$] ", " C64[".35"]$.Quantize[C64[".0"]$]] + } + +Output: + + Banker's rounding: 1.000000000000010, 1.000000000000016, 0.2, 0.4 + Common rounding: 1.000000000000011, 1.000000000000016, 0.3, 0.4 + +### Signed zeros ####################################################################################################### + +MANOOL supports signed zeros in accordance with the specification IEEE-754 (only for binary floating-point arithmetic): + + { {extern "manool.org.18/std/0.6/all"} in + -- Signed zeros supported + Out.WriteLine[F64["1e-300"]$ / F64["1e300"]$ ", " ~F64["1e-300"]$ / F64["1e300"]$] + Out.WriteLine[F64["+0"]$ ", " F64["-0"]$] + Out.WriteLine[F64["+0"]$ == F64["-0"]$] + Out.WriteLine[Atan[F64[0]$; F64["+0"]$]] + Out.WriteLine[Atan[F64[0]$; F64["-0"]$]] + -- Signed zeros unsupported + Out.WriteLine[D64["1e-300"]$ / D64["1e300"]$ ", " ~D64["1e-300"]$ / D64["1e300"]$] + Out.WriteLine[D64["+0"]$ ", " D64["-0"]$] + } + +Output: + + 0.0000000000000000e+00, -0.0000000000000000e+00 + 0.0000000000000000e+00, -0.0000000000000000e+00 + True + 0.0000000000000000e+00 + 3.1415926535897931e+00 + 0e-398, 0e-398 + 0, 0 + + +* * * * * +**[Continue](lesson-4 "Next Lesson")** + + +{%endraw%}{%include page_footer.md%} diff --git a/tutorial/lesson-4.md b/tutorial/lesson-4.md new file mode 100644 index 0000000..dd89d2c --- /dev/null +++ b/tutorial/lesson-4.md @@ -0,0 +1,405 @@ +--- +title: Lesson 4 -- Tutorial +updated: 2020-06-01 +--- + + + +{%include page_header.md%}{%raw%} + + +Composite Data Types, For-Loops and Views +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +MANOOL provides a comprehensive set of composite data types, which vary in basic operations and internal data structure (and thus in asymptotic complexity of +basic operations with composite values).[^a1] + +[^a1]: A composite value in MANOOL (or object, depending on your point of view) incorporates other values (or objects, respectively). This happens irrespective + of component types and without placing restrictions on the number of components on behalf of the composite type itself. Thus, complex numbers, for + instance, are not composite values according to this definition. + +The following examples should allow you to master composite types of MANOOL and deal easily and efficiently with elaborate data sets. Pay special attention to +comments embedded in these examples about asymptotic complexity of certain expressions (such as `/*O(1)*/` or `/*O(log n)*/`).[^a2] + +[^a2]: Complete control over time complexity of operations with composite values may be the whole point of using MANOOL in the first place. + +Normally, you can iterate over elements of a composite value in a straightforward way by using a `for`-loop:[^a3] + + { {extern "manool.org.18/std/0.6/all"} in + : for { E = {array of "Red" "Orange" "Yellow" "Green" "Blue" "Indigo" "Violet"}$ } do + Out.WriteLine[E] + } + + (here we construct an _array_ out of elements by using the expression `{array of ...}` and declare the variable `E` as a `for`-loop variable). + +[^a3]: Components of an iterable composite value are also referred to as _elements_ of that value. + +Output: + + Red + Orange + Yellow + Green + Blue + Indigo + Violet + +You can apply the operation `Size` to any iterable composite value to obtain the number of elements in the argument. Actually, you always iterate over elements +of a _view_ (some composite values are views onto their own elements).[^a4] + +[^a4]: `{for {E = C} in ...}` is actually equivalent to `{for {E = C.Elems[]} in ...}`, and `Elems` for a view shall return that view again. Other operations + exist that return views, like `Keys`, which returns a view onto access keys for a composite value. + + + +#### Concatenation and slicing of arrays and sequences, move operations + +You can concatenate arrays and get subarrays (_slices_ in more general terms), even in reverse order if you like, by indexing (subscripting) using ranges:[^a5] + + { {extern "manool.org.18/std/0.6/all"} in + : var { A = {array of "Red" "Orange" "Yellow" "Green" "Blue" "Indigo" "Violet"}$ } in + : for { E = A[Range[2; Size[A]]] + A[RevRange[Size[A] - 1]] } do + Out.WriteLine[E] + } + +[^a5]: You can also access individual array elements (by index), which is demonstrated later. + +Compared to arrays, sequences (another composite data type) may be more efficient for concatenation and removing a few elements from either end. In turn, arrays +are more efficient than sequences for accessing individual elements in the middle. Otherwise, they are similar: + + { {extern "manool.org.18/std/0.6/all"} in + : var { S = {sequence of "Red" "Orange" "Yellow" "Green" "Blue" "Indigo" "Violet"} } in + : var { R = S[RevRange[Size[S]]] } in + : for { E = S![Range[2; Size[S]]] + R![Range[1; Size[R]]] /*O(1)*/ } do + Out.WriteLine[E] + } + +The constructs `S!` and `R!` above refer to _move_ operations. By using a move operation, you roughly tell the MANOOL translator: "I declare that I won't need +the value stored in this location anymore. Please feel free to reuse any associated resources as you wish.".[^a6] + +[^a6]: If you in fact hold your promise, such move operation is semantically a non-op, but it may be crucial for efficiency. Arguably, relying on move + operations instead of referential semantics constitutes a better software engineering practice. + +Output of both examples: + + Yellow + Green + Blue + Indigo + Violet + Indigo + Blue + Green + Yellow + Orange + Red + + + +### Sets ############################################################################################################### + +You can also easily operate with _sets_ and even perform set-theoretic operations using infix operators: + + { {extern "manool.org.18/std/0.6/all"} in + : for { E = {set of "Red" "Green" "Blue" "Indigo" "Violet"}$ + {set of "Red" "Yellow" "Orange" "Green" "Blue"}$ } do + Out.WriteLine[E] + } + +Output: + + Blue + Green + Indigo + Orange + Red + Violet + Yellow + +Note that the same value cannot occur in a set more than once, and here you cannot have arbitrary control over element ordering anymore (instead, the order of +elements in a set is always implicitly induced by the operation `Order`). + + + +### Partial updates and iteration over lazily evaluated slices ######################################################### + +Small, step-wise modifications of composites can be constructed in constant or logarithmic time; slices can be evaluated on-demand (by using first `Elems` to +construct a _view_): + + { {extern "manool.org.18/std/0.6/all"} in + : var { A = {array of "Red" "Orange" "Yellow" "Green" "Blue" "Indigo"} } in + /* A = {array of "Violet"}$ + A! /*O(n)*/ */ -- may be slow in case of arrays + A = A! | "Violet" | "Red" /*amortized O(1)*/ -- append violet and red + A[5] = "Dark Blue" /*O(1)*/ -- replace an element; shorthand for: + /* A = A!.Repl[5; "Dark Blue"] /*O(1)*/ */ + : do Out.WriteLine[A[Size[A] - 1] /*O(1)*/] after -- output the last elem + : for { E = A.Elems[Range[Size[A] - 1]] /*O(1)*/ } do -- output all but the last elem + Out.Write[E; ", "] + } + +Output: + + Red, Orange, Yellow, Green, Blue, Dark Blue, Violet, Red + +And now using sequences: + + { {extern "manool.org.18/std/0.6/all"} in + : var { S = {sequence of "Red" "Orange" "Yellow" "Green" "Blue" "Indigo"} } in + S = {sequence of "Violet"}$ + S! | "Violet" /*O(1)*/ -- prepend and append violet + /* A[3] = "Amarillo" /*O(n)*/ */ -- may be slow in case of sequences + : do Out.WriteLine[S[Size[S] - 1] /*O(1)*/] after -- output the last elem + : for { E = S.Elems[Range[Size[S] - 1]] /*O(1)*/ } do -- output all but the last elem + Out.Write[E; ", "] + } + +Output: + + Violet, Red, Orange, Yellow, Green, Blue, Indigo, Violet + +And sets: + + { {extern "manool.org.18/std/0.6/all"} in + : var { S = {set of "Red" "Orange" "Yellow" "Green" "Blue" "Indigo"} } in + S = S! | "Violet" | "Red" /*O(log n)*/ -- include violet and red (if not present) + S["Orange"] = False /*O(log n)*/ -- exclude orange (if present); shorthand for: + /* S = S!.Repl["Orange"; False] /*O(log n)*/ */ + : do Out.WriteLine[S.Elems[][Size[S] - 1] /*O(1)*/] after -- output the last elem + : for { E = S.Elems[Range[Size[S] - 1]] /*O(1)*/ } do -- output all but the last elem + Out.Write[E; ", "] + } + +Output: + + Blue, Green, Indigo, Red, Violet, Yellow + + +### Nested composites ################################################################################################## + +A composite value may contain other composite values as components/elements. Let's construct a funny table or matrix 9 x 7 of color names out of nested arrays: + + { {extern "manool.org.18/std/0.6/all"} in + : var { Tab = {array 9 of: array 7}$ } in -- 9 x 7 matrix filled with Nil value + Tab[0] = {array of "Red" "Orange" "Yellow" "Green" "Blue" "Indigo" "Violet"}$ + { for { I = Range[1; Size[Tab]] } do -- Fill out the table + : for { J = Range[Size[Tab[0]]] } do + Tab[I; J] = Tab[I - 1; (J - 1).Mod[7]] /*amortized O(1)*/ -- shorthand for: + /* Tab[I][J] = Tab[I - 1][(J - 1).Mod[7]] /*amortized O(1)*/ */ -- (but faster) + } + : for { I = Tab.Keys[] } do -- Print the table + : for { J = Tab[I].Keys[] } do + Out.Write[Tab[I; J] /*O(1)*/] -- shorthand for: + /* Out.Write[Tab[I][J]] */ -- (but faster) + : if J < Tab[I].Size[] - 1 then + Out.Write[" "] + else + Out.WriteLine[] + } + + (note that in MANOOL instead of writing `T[I][J]` you can write just `T[I; J]`, which is actually recommended due to performance reasons). + +Output: + + Red Orange Yellow Green Blue Indigo Violet + Violet Red Orange Yellow Green Blue Indigo + Indigo Violet Red Orange Yellow Green Blue + Blue Indigo Violet Red Orange Yellow Green + Green Blue Indigo Violet Red Orange Yellow + Yellow Green Blue Indigo Violet Red Orange + Orange Yellow Green Blue Indigo Violet Red + Red Orange Yellow Green Blue Indigo Violet + Violet Red Orange Yellow Green Blue Indigo + + +### Records ############################################################################################################ + +Records are composite values whose individual components are always identified by keys of type Symbol. A record component is accessible in constant time, but +there are more limitations as to how you can manipulate records (compared to maps). + +Let's use records to construct a table of set operations and their descriptions and then iterate over that table: + + { {extern "manool.org.18/std/0.6/all"} in + : for + { E = + { array of + {record of Description = "Set A"; Op = {proc {A; _} as A}} + {record of Description = "Set B"; Op = {proc {_; B} as B}} + {record of Op = (+); Description = "Union"} + {record of Op = (*); Description = "Intersection"} + {record of Op = (-); Description = "Difference"} + {record of Op = (/); Description = "Symmetric difference"} + }$ + } + do + : var + { S = + {set of "Red" "Green" "Blue" "Indigo" "Violet"}$.(E[Op] /*O(1)*/)[{set of "Red" "Yellow" "Orange" "Green" "Blue"}$] + } + in + Out.WriteLine["===== " E[Description] /*O(1)*/ ":"] + : do Out.WriteLine[S.Elems[][Size[S] - 1] /*O(1)*/] after -- last elem + : for { E = S.Elems[Range[Size[S] - 1]] /*O(1)*/ } do -- all but the last elem + Out.Write[E; ", "] + } + +Output: + + ===== Set A: + Blue, Green, Indigo, Red, Violet + ===== Set B: + Blue, Green, Orange, Red, Yellow + ===== Union: + Blue, Green, Indigo, Orange, Red, Violet, Yellow + ===== Intersection: + Blue, Green, Red + ===== Difference: + Indigo, Violet + ===== Symmetric difference: + Indigo, Orange, Violet, Yellow + + +### Maps ############################################################################################################### + +Unlike records maps are composite values whose individual elements can be addressed using keys of any type. + +Let's construct a dictionary to translate color names from English to Spanish and then another one to translate from English to Portuguese:[^a7] + + { {extern "manool.org.18/std/0.6/all"} in + : var + { Es = + { map of + "Red" = "Rojo" + "Orange" = "Naranja" + "Yellow" = "Amarillo" + "Green" = "Verde" + "Blue" = "Azul" + "Indigo" = "Indigo" + "Violet" = "Violeta" + } + } + in + : var { Pt = Es /*O(1)*/ } in -- make a "virtual" copy (sharing map resources) + Pt["Red"] = "Vermelho" /*O(n)*/ -- copy-on-write (automatically unsharing map resources here) + Pt["Orange"] = "Laranja" /*O(log n)*/ -- reading and updating of map elements always takes logarithmic time + Pt["Yellow"] = "Amarelo" /*O(log n)*/ + : let + { WriteMap = + { proc { S; M } as + : for { K = M.Keys[]; E = M.Elems[] } do -- parallel iteration + S.WriteLine[K " -> " E] + } + } + in + Out.WriteLine["=== In Spanish ==="]; Out.WriteMap[Es] + Out.WriteLine["=== In Portuguese ==="]; Out.WriteMap[Pt] + } + + (in MANOOL you can iterate over more than one view using more than one loop variable in each step, which is demonstrated above). + +[^a7]: This example also demonstrates the non-referential semantics in action. + +Output: + + === In Spanish === + Blue -> Azul + Green -> Verde + Indigo -> Indigo + Orange -> Naranja + Red -> Rojo + Violet -> Violeta + Yellow -> Amarillo + === In Portuguese === + Blue -> Azul + Green -> Verde + Indigo -> Indigo + Orange -> Laranja + Red -> Vermelho + Violet -> Violeta + Yellow -> Amarelo + + +* * * * * +**[Continue](lesson-5 "Next Lesson")** + + +{%endraw%}{%include page_footer.md%} diff --git a/tutorial/lesson-5.md b/tutorial/lesson-5.md new file mode 100644 index 0000000..58639f2 --- /dev/null +++ b/tutorial/lesson-5.md @@ -0,0 +1,208 @@ +--- +title: Lesson 5 -- Tutorial +updated: 2020-06-01 +--- + + + +{%include page_header.md%}{%raw%} + + +Rational Numbers ADT +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + +In this section we'll discuss a rather long piece of code -- taking as an example rational numbers, we'll see how to define your own data types in MANOOL. + +This involves studying several orthogonal mechanisms and constructs: + * modules, + * uninterned (unique) symbols, + * concrete object constructors (along with `utils` modules), and + * exception signaling. + + Some auxiliary constructs will be exposed (and explained) too: + * `{do ... where ...}`, + * `{scope {...} in ...}`, + * `(...)%`, + * `{unless ... signal ... else ...}`, and + * `{ensure ... in ...}`. + + + +The following example constitutes a working program with a complete "rational number" data type definition and some test code (for ease of reference, line +numbers are added as valid comments to the listing): + + /*001*/ { {extern "manool.org.18/std/0.6/all"} in + /*002*/ : do + /*003*/ { Rationals in -- Test code + /*004*/ : var { A; B } in + /*005*/ A = Rat[1; 2]$ -- one half rational constant + /*006*/ B = Rat[3; 4]$ -- three fourth rational constant + /*007*/ Out.WriteLine["A = " A] -- displaying rationals + /*008*/ Out.WriteLine["B = " B] + /*009*/ : for { Op = {array of (+) (-) (*) (/)} } do + /*010*/ Out.WriteLine["A " Op " B = " A.Op[B]] -- rational arithmetic + /*011*/ } + /*012*/ where + /*013*/ Rationals = + /*014*/ { scope { extern } in + /*015*/ : {extern "manool.org.18/std/0.6/all"} in + /*016*/ : let { _Num; _Den } in + /*017*/ : let { IsRat = {{object utils _Num; _Den} in IsInst} } in -- classification + /*018*/ : let + /*019*/ { Gcd = -- Greatest Common Divisor of {Num, Den} - Snippet of code + /*020*/ { if Num == 0 then 1 else + /*021*/ : var { A = Abs[Num]; B = Den } in + /*022*/ : do A after + /*023*/ : while A <> B do: if A > B then A = A - B else B = B - A -- Euclidean algorithm + /*024*/ }' + /*025*/ } + /*026*/ in + /*027*/ : let rec + /*028*/ { Rat = -- Construction + /*029*/ { proc { Num; Den } as + /*030*/ : var { Gcd = Gcd% } in + /*031*/ : object { _Num = Num / Gcd; _Den = Den / Gcd } with + /*032*/ -- Extractors + /*033*/ Num = {proc {A} as A[_Num]@} + /*034*/ Den = {proc {A} as A[_Den]@} + /*035*/ -- Arithmetic operations + /*036*/ (+) = + /*037*/ { proc { A; B } as + /*038*/ : unless IsRat[B] signal TypeMismatch else + /*039*/ Rat[Num[A] * Den[B] + Num[B] * Den[A]; Den[A] * Den[B]] + /*040*/ } + /*041*/ (-) = + /*042*/ { proc { A; B } as + /*043*/ : unless IsRat[B] signal TypeMismatch else + /*044*/ Rat[Num[A] * Den[B] - Num[B] * Den[A]; Den[A] * Den[B]] + /*045*/ } + /*046*/ (*) = + /*047*/ { proc { A; B } as + /*048*/ : unless IsRat[B] signal TypeMismatch else + /*049*/ Rat[Num[A] * Num[B]; Den[A] * Den[B]] + /*050*/ } + /*051*/ (/) = + /*052*/ { proc { A; B } as + /*053*/ : unless IsRat[B] signal TypeMismatch else + /*054*/ : unless Num[B] <> 0 signal DivisionByZero else + /*055*/ : if Num[B] > 0 then + /*056*/ Rat[ Num[A] * Den[B]; Den[A] * Num[B]] + /*057*/ else + /*058*/ Rat[~Num[A] * Den[B]; Den[A] * ~Num[B]] + /*059*/ } + /*060*/ (~) = Neg -- unary minus (arithmetic negation) + /*061*/ Neg = + /*062*/ { proc { A } as + /*063*/ Rat[~Num[A]; Den[A]] + /*064*/ } + /*065*/ -- Comparison + /*066*/ (==) = + /*067*/ { proc { A; B } as + /*068*/ IsRat[B] & (Num[A] == Num[B]) & (Den[A] == Den[B]) + /*069*/ } + /*070*/ (<>) = + /*071*/ { proc { A; B } as + /*072*/ ~IsRat[B] | (Num[A] <> Num[B]) | (Den[A] <> Den[B]) + /*073*/ } + /*074*/ (<) = + /*075*/ { proc { A; B } as + /*076*/ : unless IsRat[B] signal TypeMismatch else + /*077*/ Num[A] * Den[B] < Num[B] * Den[A] + /*078*/ } + /*079*/ (<=) = + /*080*/ { proc { A; B } as + /*081*/ : unless IsRat[B] signal TypeMismatch else + /*082*/ Num[A] * Den[B] <= Num[B] * Den[A] + /*083*/ } + /*084*/ (>) = + /*085*/ { proc { A; B } as + /*086*/ : unless IsRat[B] signal TypeMismatch else + /*087*/ Num[A] * Den[B] > Num[B] * Den[A] + /*088*/ } + /*089*/ (>=) = + /*090*/ { proc { A; B } as + /*091*/ : unless IsRat[B] signal TypeMismatch else + /*092*/ Num[A] * Den[B] >= Num[B] * Den[A] + /*093*/ } + /*094*/ Order = + /*095*/ { proc { A; B } as + /*096*/ : unless IsRat[B] signal TypeMismatch else + /*097*/ Order[Num[A] * Den[B]; Num[B] * Den[A]] + /*098*/ } + /*099*/ -- Conversions and cloning + /*100*/ Str = {proc {A} as Str[Num[A]] + "/" + Str[Den[A]]} + /*101*/ Int = {proc {A} as Num[A] / Den[A]} + /*102*/ Clone = {proc {A} as Rat[Num[A]; Den[A]]} + /*103*/ } -- proc + /*104*/ } + /*105*/ in + /*106*/ : ensure Rat[1; 2] + Rat[ 3; 4] == Rat[ 5; 4] in -- unit tests + /*107*/ : ensure Rat[1; 2] - Rat[ 3; 4] == Rat[~1; 4] in + /*108*/ : ensure Rat[1; 2] * Rat[ 3; 4] == Rat[ 3; 8] in + /*109*/ : ensure Rat[1; 2] / Rat[ 3; 4] == Rat[ 2; 3] in + /*110*/ : ensure Rat[1; 2] / Rat[~3; 4] == Rat[~2; 3] in + /*111*/ : ensure Rat[1; 2] < Rat[ 3; 4] in + /*112*/ : export + /*113*/ Rat = -- Construction + /*114*/ { proc { Num; Den } as + /*115*/ : unless IsI48[Num] & IsI48[Den] signal TypeMismatch else + /*116*/ : unless Den <> 0 signal {if Num <> 0 then DivisionByZero else Undefined} else + /*117*/ : if Den > 0 then Rat[Num; Den] else Rat[~Num; ~Den] + /*118*/ } + /*119*/ IsRat = IsRat -- classification + /*120*/ } -- scope + /*121*/ } + +Output: + + A = 1/2 + B = 3/4 + A + B = 5/4 + A - B = -1/4 + A * B = 3/8 + A / B = 2/3 + +--- + +**Caution!!! Work in progress!!!** + +--- + + + + +{%endraw%}{%include page_footer.md%}