fusion-lang 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/.mutant.yml +24 -0
  3. data/.simplecov +11 -0
  4. data/CHANGELOG.md +42 -0
  5. data/README.md +2 -1
  6. data/Rakefile +8 -0
  7. data/docs/lang/design.md +54 -5
  8. data/docs/lang/implementation.md +41 -0
  9. data/docs/user/how-to-guides.md +90 -5
  10. data/docs/user/reference.md +48 -37
  11. data/docs/user/tutorial.md +6 -5
  12. data/examples/double.fsn +4 -1
  13. data/examples/factorial.fsn +6 -3
  14. data/examples/gcd.fsn +9 -0
  15. data/examples/matrix/OP.fsn +2 -0
  16. data/examples/matrix/average.fsn +2 -0
  17. data/examples/matrix/solve.fsn +2 -0
  18. data/exe/fusion +2 -2
  19. data/lib/fusion/ast.rb +76 -28
  20. data/lib/fusion/atom.rb +1 -1
  21. data/lib/fusion/cli/decoder.rb +6 -6
  22. data/lib/fusion/cli/encoder.rb +2 -2
  23. data/lib/fusion/cli/options.rb +11 -8
  24. data/lib/fusion/cli/parser.rb +1 -1
  25. data/lib/fusion/cli/repl.rb +6 -6
  26. data/lib/fusion/cli/serializer.rb +6 -7
  27. data/lib/fusion/cli.rb +1 -1
  28. data/lib/fusion/interpreter/builtins.rb +51 -22
  29. data/lib/fusion/interpreter/thunk.rb +2 -2
  30. data/lib/fusion/interpreter.rb +31 -23
  31. data/lib/fusion/lexer.rb +62 -40
  32. data/lib/fusion/parser.rb +72 -35
  33. data/lib/fusion/version.rb +3 -1
  34. data/lib/fusion.rb +0 -1
  35. data/stdlib/all.fsn +2 -2
  36. data/stdlib/any.fsn +3 -3
  37. data/stdlib/compact.fsn +2 -3
  38. data/stdlib/concat.fsn +4 -3
  39. data/stdlib/entries.fsn +6 -0
  40. data/stdlib/filter.fsn +3 -3
  41. data/stdlib/map.fsn +3 -2
  42. data/stdlib/matrix/Matrix.fsn +8 -0
  43. data/stdlib/matrix/OP.fsn +18 -0
  44. data/stdlib/matrix/add.fsn +7 -0
  45. data/stdlib/matrix/column.fsn +6 -0
  46. data/stdlib/matrix/determinant.fsn +16 -0
  47. data/stdlib/matrix/dimensions.fsn +5 -0
  48. data/stdlib/matrix/identity.fsn +6 -0
  49. data/stdlib/matrix/invert.fsn +26 -0
  50. data/stdlib/matrix/minor.fsn +15 -0
  51. data/stdlib/matrix/multiply.fsn +10 -0
  52. data/stdlib/matrix/negate.fsn +5 -0
  53. data/stdlib/matrix/product.fsn +11 -0
  54. data/stdlib/matrix/rotate.fsn +10 -0
  55. data/stdlib/matrix/row.fsn +6 -0
  56. data/stdlib/matrix/scale.fsn +6 -0
  57. data/stdlib/matrix/subtract.fsn +7 -0
  58. data/stdlib/matrix/sum.fsn +14 -0
  59. data/stdlib/matrix/transpose.fsn +5 -0
  60. data/stdlib/range.fsn +2 -2
  61. data/stdlib/safe.fsn +7 -0
  62. data/stdlib/sanitize.fsn +2 -3
  63. data/stdlib/toObject.fsn +7 -0
  64. data/stdlib/vector/Vector.fsn +7 -0
  65. data/stdlib/vector/add.fsn +6 -0
  66. data/stdlib/vector/cross.fsn +6 -0
  67. data/stdlib/vector/dot.fsn +6 -0
  68. data/stdlib/vector/norm.fsn +6 -0
  69. data/stdlib/vector/scale.fsn +5 -0
  70. data/stdlib/vector/subtract.fsn +6 -0
  71. data/stdlib/zip.fsn +6 -0
  72. metadata +37 -5
  73. data/stdlib/gt.fsn +0 -9
  74. data/stdlib/gte.fsn +0 -9
  75. data/stdlib/lt.fsn +0 -9
  76. data/stdlib/lte.fsn +0 -9
@@ -0,0 +1,6 @@
1
+ # Destructure an object into an array of its [key, value] entries, in insertion
2
+ # order. The inverse of @toObject.
3
+ (
4
+ obj ? @Object => obj | @keys |: (k => [k, obj[k]]),
5
+ x => !{"kind": "argument_error", "origin": "stdlib", "operation": "@entries", "status": 0, "input": x, "expected": ["_ ? @Object"]}
6
+ )
data/stdlib/filter.fsn CHANGED
@@ -6,7 +6,7 @@
6
6
  _ ? @falsey => {"f": f, "c": rest} | @,
7
7
  _ => [x, ...({"f": f, "c": rest} | @)],
8
8
  ),
9
- {"f": f ? @Function, "c": obj ? @Object} =>
10
- {"f": (e => e | ([_, v] => v | f)), "c": {"f": (k => [k, obj[k]]), "c": obj | @keys} | @map} | @ | @toObject,
11
- x => !{"kind": "argument_error", "origin": "stdlib", "operation": "@filter", "status": 0, "input": x, "expected": ["{\"f\": _ ? @Function, \"c\": _ ? @Array}", "{\"f\": _ ? @Function, \"c\": _ ? @Object}"]}
9
+ # Reduce to array case
10
+ {"f": f ? @Function, "c": obj ? @Object} => {"f": ([_, v] => v | f), "c": obj | @entries} | @ | @toObject,
11
+ x => !{"kind": "argument_error", "origin": "stdlib", "operation": "@filter", "status": 0, "input": x, "expected": ["{\"f\": _ ? @Function, \"c\": _ ? @Collection}"]}
12
12
  )
data/stdlib/map.fsn CHANGED
@@ -3,6 +3,7 @@
3
3
  (
4
4
  {"f": _ ? @Function, "c": []} => [],
5
5
  {"f": f ? @Function, "c": [x, ...rest]} => [x | f, ...({"f": f, "c": rest} | @)],
6
- {"f": f ? @Function, "c": obj ? @Object} => {"f": (k => [k, obj[k] | f]), "c": obj | @keys} | @ | @toObject,
7
- x => !{"kind": "argument_error", "origin": "stdlib", "operation": "@map", "status": 0, "input": x, "expected": ["{\"f\": _ ? @Function, \"c\": _ ? @Array}", "{\"f\": _ ? @Function, \"c\": _ ? @Object}"]}
6
+ # Reduce to array case
7
+ {"f": f ? @Function, "c": obj ? @Object} => {"f": ([k, v] => [k, v | f]), "c": obj | @entries} | @ | @toObject,
8
+ x => !{"kind": "argument_error", "origin": "stdlib", "operation": "@map", "status": 0, "input": x, "expected": ["{\"f\": _ ? @Function, \"c\": _ ? @Collection}"]}
8
9
  )
@@ -0,0 +1,8 @@
1
+ # True for a matrix: a non-empty array of equally sized vectors (@vector/Vector).
2
+ # A predicate: false for anything else, never an error.
3
+ (rows => [
4
+ rows | @Array,
5
+ rows | @size > 0,
6
+ {"c": rows, "f": @vector/Vector} | @all,
7
+ rows |: @size | @OP.equal,
8
+ ] | @OP.and | @safe)
@@ -0,0 +1,18 @@
1
+ # The operators reskinned for matrices. Point a directory's OP.fsn at
2
+ # `@matrix/OP` to make the arithmetic sugar operate on matrices: `a + b`
3
+ # (elementwise sum), `-a` / `a - b` (elementwise negation), `a * b` (matrix
4
+ # product), `/a` (inverse), `a / b` (multiply by the inverse). `//` and `%`
5
+ # have no matrix meaning and always raise. The spread keeps every other member
6
+ # (equal, compare and its readers, the boolean operators) at its builtin
7
+ # meaning — this module's own files rely on that: they use the comparison and
8
+ # boolean sugar and plain `@OP` references freely, and reserve `@@OP` for the
9
+ # scalar versions of the arithmetic members this file overrides.
10
+ {
11
+ ...@@,
12
+ "sum": @sum,
13
+ "negate": @negate,
14
+ "product": @product,
15
+ "invert": @invert,
16
+ "quotient": (x => !{"kind": "math_error", "origin": "stdlib", "operation": "@matrix/OP.quotient", "status": 0, "input": x, "message": "integer division is not defined for matrices"}),
17
+ "modulo": (x => !{"kind": "math_error", "origin": "stdlib", "operation": "@matrix/OP.modulo", "status": 0, "input": x, "message": "modulo is not defined for matrices"}),
18
+ }
@@ -0,0 +1,7 @@
1
+ # Add two equally sized matrices elementwise: zip the rows and add each pair
2
+ # through @vector/add. Input: [a, b].
3
+ (
4
+ [a, b] ? ([x ? @Matrix, y ? @Matrix] => [x, y] |: @dimensions | @OP.equal) =>
5
+ [a, b] | @zip |: @vector/add,
6
+ x => !{"kind": "argument_error", "origin": "stdlib", "operation": "@matrix/add", "status": 0, "input": x, "expected": ["_ ? ([a ? @matrix/Matrix, b ? @matrix/Matrix] => [a, b] |: @matrix/dimensions | @OP.equal)"]}
7
+ )
@@ -0,0 +1,6 @@
1
+ # Extract the i-th (0-based) column of a matrix as a vector. Input: [matrix, index].
2
+ (
3
+ [m, i] ? ([x ? @Matrix, n ? @Integer] => n >= 0 && n < (x | @dimensions).columns) =>
4
+ m |: (row => row[i]),
5
+ x => !{"kind": "argument_error", "origin": "stdlib", "operation": "@matrix/column", "status": 0, "input": x, "expected": ["_ ? ([m ? @matrix/Matrix, i ? @Integer] => i >= 0 && i < (m | @matrix/dimensions).columns)"]}
6
+ )
@@ -0,0 +1,16 @@
1
+ # Determinant of a square matrix, by Laplace expansion along the first row: the
2
+ # dot product of the first row with its cofactors (signed minor determinants).
3
+ (
4
+ m ? (sq ? @Matrix => sq | @dimensions | (d => d.rows == d.columns)) =>
5
+ m | (
6
+ [[a]] => a,
7
+ [firstRow, ...] =>
8
+ [
9
+ firstRow,
10
+ (m | @dimensions).columns | @range |: (j =>
11
+ [[-1, j] | @math.pow, [m, 0, j] | @minor | @] | @@OP.product
12
+ ),
13
+ ] | @vector/dot
14
+ ),
15
+ x => !{"kind": "argument_error", "origin": "stdlib", "operation": "@matrix/determinant", "status": 0, "input": x, "expected": ["_ ? (sq ? @matrix/Matrix => sq | @matrix/dimensions | (d => d.rows == d.columns))"]}
16
+ )
@@ -0,0 +1,5 @@
1
+ # The dimensions of a matrix, as {"rows": _, "columns": _}.
2
+ (
3
+ m ? @Matrix => {"rows": m | @size, "columns": m[0] | @size},
4
+ x => !{"kind": "argument_error", "origin": "stdlib", "operation": "@matrix/dimensions", "status": 0, "input": x, "expected": ["_ ? @matrix/Matrix"]}
5
+ )
@@ -0,0 +1,6 @@
1
+ # The n-by-n identity matrix for a positive integer n.
2
+ (
3
+ n ? (i ? @Integer => i > 0) =>
4
+ n | @range |: (r => n | @range |: (c => (r == c) | (true => 1, false => 0))),
5
+ x => !{"kind": "argument_error", "origin": "stdlib", "operation": "@matrix/identity", "status": 0, "input": x, "expected": ["_ ? (n ? @Integer => n > 0)"]}
6
+ )
@@ -0,0 +1,26 @@
1
+ # Inverse of a square matrix with a non-zero determinant: the adjugate scaled
2
+ # by 1/determinant. Adjugate entry (i, j) is (-1)^(i+j) * determinant(minor(j, i)).
3
+ (
4
+ m ? (sq ? @Matrix => sq | @dimensions | (d => d.rows == d.columns)) =>
5
+ m | @determinant | (det => (det ?? 0) | (
6
+ 0 => !{"kind": "math_error", "origin": "stdlib", "operation": "@matrix/invert", "status": 0, "input": m, "message": "singular matrix (determinant 0)"},
7
+ _ => m | (
8
+ [[a]] => [[a | @@OP.invert]],
9
+ _ =>
10
+ [
11
+ det | @@OP.invert,
12
+ m | @dimensions | (d =>
13
+ d.rows | @range |: (i =>
14
+ d.columns | @range |: (j =>
15
+ [
16
+ [-1, [i, j] | @@OP.sum] | @math.pow,
17
+ [m, j, i] | @minor | @determinant,
18
+ ] | @@OP.product
19
+ )
20
+ )
21
+ ),
22
+ ] | @scale
23
+ )
24
+ )),
25
+ x => !{"kind": "argument_error", "origin": "stdlib", "operation": "@matrix/invert", "status": 0, "input": x, "expected": ["_ ? (sq ? @matrix/Matrix => sq | @matrix/dimensions | (d => d.rows == d.columns))"]}
26
+ )
@@ -0,0 +1,15 @@
1
+ # The minor of a matrix: drop row r and column c. Input: [matrix, r, c]. The
2
+ # matrix must be at least 2 by 2, so the minor is a matrix again.
3
+ (
4
+ [m, r, c] ? ([x ? @Matrix, row ? @Integer, col ? @Integer] =>
5
+ x | @dimensions | (d =>
6
+ row >= 0 && row < d.rows &&
7
+ col >= 0 && col < d.columns &&
8
+ d.rows >= 2 && d.columns >= 2)) =>
9
+ m | @dimensions | (d =>
10
+ d.rows | @range |? (k => ~(k == r)) |: (k =>
11
+ d.columns | @range |? (l => ~(l == c)) |: (l => m[k][l])
12
+ )
13
+ ),
14
+ x => !{"kind": "argument_error", "origin": "stdlib", "operation": "@matrix/minor", "status": 0, "input": x, "expected": ["_ ? ([m ? @matrix/Matrix, r ? @Integer, c ? @Integer] => m | @matrix/dimensions | (d => r >= 0 && r < d.rows && c >= 0 && c < d.columns && d.rows >= 2 && d.columns >= 2))"]}
15
+ )
@@ -0,0 +1,10 @@
1
+ # Matrix multiplication of a pair [a, b]: a's column count must equal b's row
2
+ # count. Entry (i, j) is the dot product of a's row i and b's column j — the
3
+ # columns are the rows of the transposed b, computed once.
4
+ (
5
+ [a, b] ? ([x ? @Matrix, y ? @Matrix] => (x | @dimensions).columns == (y | @dimensions).rows) =>
6
+ b | @transpose | (columns =>
7
+ a |: (row => columns |: (column => [row, column] | @vector/dot))
8
+ ),
9
+ x => !{"kind": "argument_error", "origin": "stdlib", "operation": "@matrix/multiply", "status": 0, "input": x, "expected": ["_ ? ([x ? @matrix/Matrix, y ? @matrix/Matrix] => (x | @matrix/dimensions).columns == (y | @matrix/dimensions).rows)"]}
10
+ )
@@ -0,0 +1,5 @@
1
+ # Negate a matrix elementwise. Built on @matrix/scale.
2
+ (
3
+ m ? @Matrix => [-1, m] | @scale,
4
+ x => !{"kind": "argument_error", "origin": "stdlib", "operation": "@matrix/negate", "status": 0, "input": x, "expected": ["_ ? @matrix/Matrix"]}
5
+ )
@@ -0,0 +1,11 @@
1
+ # Product of a non-empty array of matrices, folded left through
2
+ # @matrix/multiply. A dimension mismatch surfaces as @matrix/multiply's error.
3
+ (
4
+ matrices ? (ms => [
5
+ ms | @Array,
6
+ ms | @size > 0,
7
+ {"c": ms, "f": @Matrix} | @all,
8
+ ] | @OP.and | @safe) =>
9
+ matrices |+ @multiply,
10
+ x => !{"kind": "argument_error", "origin": "stdlib", "operation": "@matrix/product", "status": 0, "input": x, "expected": ["_ ? (ms => [ms | @Array, ms | @size > 0, {\"c\": ms, \"f\": @matrix/Matrix} | @all] | @OP.and | @safe)"]}
11
+ )
@@ -0,0 +1,10 @@
1
+ # Rotate a 2x2 matrix by an angle in radians: the rotation matrix
2
+ # [[cos, -sin], [sin, cos]] multiplied onto it. Input: [angle, matrix].
3
+ (
4
+ [angle ? @Number, [[a ? @Number, b ? @Number], [c ? @Number, d ? @Number]]] =>
5
+ [
6
+ [[angle | @math.cos, angle | @math.sin | @@OP.negate], [angle | @math.sin, angle | @math.cos]],
7
+ [[a, b], [c, d]],
8
+ ] | @multiply,
9
+ x => !{"kind": "argument_error", "origin": "stdlib", "operation": "@matrix/rotate", "status": 0, "input": x, "expected": ["[_ ? @Number, [[_ ? @Number, _ ? @Number], [_ ? @Number, _ ? @Number]]]"]}
10
+ )
@@ -0,0 +1,6 @@
1
+ # Extract the i-th (0-based) row of a matrix as a vector. Input: [matrix, index].
2
+ (
3
+ [m, i] ? ([x ? @Matrix, n ? @Integer] => n >= 0 && n < (x | @dimensions).rows) =>
4
+ m[i],
5
+ x => !{"kind": "argument_error", "origin": "stdlib", "operation": "@matrix/row", "status": 0, "input": x, "expected": ["_ ? ([m ? @matrix/Matrix, i ? @Integer] => i >= 0 && i < (m | @matrix/dimensions).rows)"]}
6
+ )
@@ -0,0 +1,6 @@
1
+ # Scale a matrix elementwise by a single number: each row scaled as a vector.
2
+ # Input: [number, matrix].
3
+ (
4
+ [s ? @Number, m ? @Matrix] => m |: (row => [s, row] | @vector/scale),
5
+ x => !{"kind": "argument_error", "origin": "stdlib", "operation": "@matrix/scale", "status": 0, "input": x, "expected": ["[_ ? @Number, _ ? @matrix/Matrix]"]}
6
+ )
@@ -0,0 +1,7 @@
1
+ # Subtract the second matrix from the first, elementwise: a + (-1 * b).
2
+ # Input: [a, b].
3
+ (
4
+ [a, b] ? ([x ? @Matrix, y ? @Matrix] => [x, y] |: @dimensions | @OP.equal) =>
5
+ [a, [-1, b] | @scale] | @add,
6
+ x => !{"kind": "argument_error", "origin": "stdlib", "operation": "@matrix/subtract", "status": 0, "input": x, "expected": ["_ ? ([a ? @matrix/Matrix, b ? @matrix/Matrix] => [a, b] |: @matrix/dimensions | @OP.equal)"]}
7
+ )
@@ -0,0 +1,14 @@
1
+ # Elementwise sum of a non-empty array of equally sized matrices: a reduce
2
+ # over @matrix/add. The guard is a flat conditions array ending in @safe: a
3
+ # condition that errors (e.g. @dimensions of a non-matrix) collapses the array
4
+ # into that error, @safe turns it into false, and the clause just doesn't match.
5
+ (
6
+ matrices ? (ms => [
7
+ ms | @Array,
8
+ ms | @size > 0,
9
+ {"c": ms, "f": @Matrix} | @all,
10
+ ms |: @dimensions | @OP.equal,
11
+ ] | @OP.and | @safe) =>
12
+ matrices |+ @add,
13
+ x => !{"kind": "argument_error", "origin": "stdlib", "operation": "@matrix/sum", "status": 0, "input": x, "expected": ["_ ? (ms => [ms | @Array, ms | @size > 0, {\"c\": ms, \"f\": @matrix/Matrix} | @all, ms |: @matrix/dimensions | @OP.equal] | @OP.and | @safe)"]}
14
+ )
@@ -0,0 +1,5 @@
1
+ # Transpose a matrix: its columns become its rows.
2
+ (
3
+ m ? @Matrix => (m | @dimensions).columns | @range |: (j => [m, j] | @column),
4
+ x => !{"kind": "argument_error", "origin": "stdlib", "operation": "@matrix/transpose", "status": 0, "input": x, "expected": ["_ ? @matrix/Matrix"]}
5
+ )
data/stdlib/range.fsn CHANGED
@@ -1,5 +1,5 @@
1
1
  (
2
2
  0 => [],
3
- n ? (m ? @Integer => [m, 0] | @OP.compare | @gt) => [...([n, -1] | @OP.sum | @), [n, -1] | @OP.sum],
4
- x => !{"kind": "argument_error", "origin": "stdlib", "operation": "@range", "status": 0, "input": x, "expected": ["_ ? (m ? @Integer => [m, 0] | @OP.compare | @gte)"]}
3
+ n ? (m ? @Integer => [m, 0] | @OP.compare | @OP.gt) => [...([n, -1] | @OP.sum | @), [n, -1] | @OP.sum],
4
+ x => !{"kind": "argument_error", "origin": "stdlib", "operation": "@range", "status": 0, "input": x, "expected": ["_ ? (m ? @Integer => [m, 0] | @OP.compare | @OP.gte)"]}
5
5
  )
data/stdlib/safe.fsn ADDED
@@ -0,0 +1,7 @@
1
+ # Catch any error into false; pass every regular value through unchanged.
2
+ # Appended to a guard condition (`cond | @safe`), an erroring check reads as
3
+ # "no match" instead of propagating out of the guard.
4
+ (
5
+ ! => false,
6
+ v => v
7
+ )
data/stdlib/sanitize.fsn CHANGED
@@ -5,8 +5,7 @@
5
5
  # returned unchanged.
6
6
  (
7
7
  v ? @Function => "<function>",
8
- v ? @NonFinite => [["<", v | @toString] | @concat, ">"] | @concat,
9
- v ? @Array => {"f": @sanitize, "c": v} | @map,
10
- v ? @Object => {"f": @sanitize, "c": v} | @map,
8
+ v ? @NonFinite => ["<", v | @toString, ">"] | @concat,
9
+ v ? @Collection => v |: @,
11
10
  v => v
12
11
  )
@@ -0,0 +1,7 @@
1
+ # Build an object from an array of [key, value] entries; later duplicate keys win.
2
+ # The inverse of @entries: folds the entries onto {} with the [=] setter.
3
+ (
4
+ entries ? (xs ? @Array => {"c": xs, "f": ([_ ? @String, _] => true)} | @all) =>
5
+ [{}, ...entries] |+ ([object, [key, value]] => object[key = value]),
6
+ x => !{"kind": "argument_error", "origin": "stdlib", "operation": "@toObject", "status": 0, "input": x, "expected": ["_ ? (xs ? @Array => {\"c\": xs, \"f\": ([_ ? @String, _] => true)} | @all)"]}
7
+ )
@@ -0,0 +1,7 @@
1
+ # True for a vector: a non-empty array of numbers. A predicate: false for
2
+ # anything else, never an error.
3
+ (v => [
4
+ v | @Array,
5
+ v | @size > 0,
6
+ {"c": v, "f": @Number} | @all,
7
+ ] | @OP.and | @safe)
@@ -0,0 +1,6 @@
1
+ # Add two equal-length vectors elementwise: zip and sum each pair.
2
+ (
3
+ [u, v] ? ([a ? @Vector, b ? @Vector] => [a, b] |: @size | @OP.equal) =>
4
+ [u, v] | @zip |: @OP.sum,
5
+ x => !{"kind": "argument_error", "origin": "stdlib", "operation": "@vector/add", "status": 0, "input": x, "expected": ["_ ? ([u ? @vector/Vector, v ? @vector/Vector] => [u, v] |: @size | @OP.equal)"]}
6
+ )
@@ -0,0 +1,6 @@
1
+ # Cross product of two 3-dimensional vectors.
2
+ (
3
+ [[a ? @Number, b ? @Number, c ? @Number], [d ? @Number, e ? @Number, f ? @Number]] =>
4
+ [b * f - c * e, c * d - a * f, a * e - b * d],
5
+ x => !{"kind": "argument_error", "origin": "stdlib", "operation": "@vector/cross", "status": 0, "input": x, "expected": ["[[_ ? @Number, _ ? @Number, _ ? @Number], [_ ? @Number, _ ? @Number, _ ? @Number]]"]}
6
+ )
@@ -0,0 +1,6 @@
1
+ # Dot product of two equal-length vectors: zip, multiply each pair, sum.
2
+ (
3
+ [u, v] ? ([a ? @Vector, b ? @Vector] => [a, b] |: @size | @OP.equal) =>
4
+ [u, v] | @zip |: @OP.product | @OP.sum,
5
+ x => !{"kind": "argument_error", "origin": "stdlib", "operation": "@vector/dot", "status": 0, "input": x, "expected": ["_ ? ([u ? @vector/Vector, v ? @vector/Vector] => [u, v] |: @size | @OP.equal)"]}
6
+ )
@@ -0,0 +1,6 @@
1
+ # Euclidean norm (length) of a vector: the square root of the dot product with
2
+ # itself. Always a float.
3
+ (
4
+ v ? @Vector => [v, v] | @dot | @math.sqrt,
5
+ x => !{"kind": "argument_error", "origin": "stdlib", "operation": "@vector/norm", "status": 0, "input": x, "expected": ["_ ? @vector/Vector"]}
6
+ )
@@ -0,0 +1,5 @@
1
+ # Scale a vector elementwise by a single number. Input: [number, vector].
2
+ (
3
+ [s ? @Number, v ? @Vector] => v |: (entry => s * entry),
4
+ x => !{"kind": "argument_error", "origin": "stdlib", "operation": "@vector/scale", "status": 0, "input": x, "expected": ["[_ ? @Number, _ ? @vector/Vector]"]}
5
+ )
@@ -0,0 +1,6 @@
1
+ # Subtract the second vector from the first, elementwise: u + (-1 * v).
2
+ (
3
+ [u, v] ? ([a ? @Vector, b ? @Vector] => [a, b] |: @size | @OP.equal) =>
4
+ [u, [-1, v] | @scale] | @add,
5
+ x => !{"kind": "argument_error", "origin": "stdlib", "operation": "@vector/subtract", "status": 0, "input": x, "expected": ["_ ? ([u ? @vector/Vector, v ? @vector/Vector] => [u, v] |: @size | @OP.equal)"]}
6
+ )
data/stdlib/zip.fsn ADDED
@@ -0,0 +1,6 @@
1
+ # Zip a pair of equal-length arrays into an array of [left, right] pairs.
2
+ (
3
+ [xs, ys] ? ([a ? @Array, b ? @Array] => a | @size == b | @size) =>
4
+ xs | @size | @range |: (k => [xs[k], ys[k]]),
5
+ x => !{"kind": "argument_error", "origin": "stdlib", "operation": "@zip", "status": 0, "input": x, "expected": ["_ ? ([xs ? @Array, ys ? @Array] => xs | @size == ys | @size)"]}
6
+ )
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fusion-lang
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Klaus Weidinger
@@ -17,6 +17,9 @@ executables:
17
17
  extensions: []
18
18
  extra_rdoc_files: []
19
19
  files:
20
+ - ".mutant.yml"
21
+ - ".simplecov"
22
+ - CHANGELOG.md
20
23
  - LICENSE.txt
21
24
  - README.md
22
25
  - Rakefile
@@ -33,7 +36,11 @@ files:
33
36
  - examples/factorial.fsn
34
37
  - examples/first.fsn
35
38
  - examples/fizzbuzz.fsn
39
+ - examples/gcd.fsn
36
40
  - examples/json_test.fsn
41
+ - examples/matrix/OP.fsn
42
+ - examples/matrix/average.fsn
43
+ - examples/matrix/solve.fsn
37
44
  - examples/palindrome.fsn
38
45
  - exe/fusion
39
46
  - lib/fusion.rb
@@ -65,18 +72,43 @@ files:
65
72
  - stdlib/chars.fsn
66
73
  - stdlib/compact.fsn
67
74
  - stdlib/concat.fsn
75
+ - stdlib/entries.fsn
68
76
  - stdlib/falsey.fsn
69
77
  - stdlib/filter.fsn
70
78
  - stdlib/flatten.fsn
71
- - stdlib/gt.fsn
72
- - stdlib/gte.fsn
73
- - stdlib/lt.fsn
74
- - stdlib/lte.fsn
75
79
  - stdlib/map.fsn
80
+ - stdlib/matrix/Matrix.fsn
81
+ - stdlib/matrix/OP.fsn
82
+ - stdlib/matrix/add.fsn
83
+ - stdlib/matrix/column.fsn
84
+ - stdlib/matrix/determinant.fsn
85
+ - stdlib/matrix/dimensions.fsn
86
+ - stdlib/matrix/identity.fsn
87
+ - stdlib/matrix/invert.fsn
88
+ - stdlib/matrix/minor.fsn
89
+ - stdlib/matrix/multiply.fsn
90
+ - stdlib/matrix/negate.fsn
91
+ - stdlib/matrix/product.fsn
92
+ - stdlib/matrix/rotate.fsn
93
+ - stdlib/matrix/row.fsn
94
+ - stdlib/matrix/scale.fsn
95
+ - stdlib/matrix/subtract.fsn
96
+ - stdlib/matrix/sum.fsn
97
+ - stdlib/matrix/transpose.fsn
76
98
  - stdlib/range.fsn
77
99
  - stdlib/reduce.fsn
100
+ - stdlib/safe.fsn
78
101
  - stdlib/sanitize.fsn
102
+ - stdlib/toObject.fsn
79
103
  - stdlib/truthy.fsn
104
+ - stdlib/vector/Vector.fsn
105
+ - stdlib/vector/add.fsn
106
+ - stdlib/vector/cross.fsn
107
+ - stdlib/vector/dot.fsn
108
+ - stdlib/vector/norm.fsn
109
+ - stdlib/vector/scale.fsn
110
+ - stdlib/vector/subtract.fsn
111
+ - stdlib/zip.fsn
80
112
  homepage: https://github.com/dunkelziffer/fusion
81
113
  licenses:
82
114
  - MIT
data/stdlib/gt.fsn DELETED
@@ -1,9 +0,0 @@
1
- # Strictly greater: interprets an @OP.compare result. Use as `[a, b] | @OP.compare | @gt`.
2
- # A partial order's compare may return null (incomparable); that propagates as null.
3
- (
4
- -1 => false,
5
- 0 => false,
6
- 1 => true,
7
- null => null,
8
- x => !{"kind": "argument_error", "origin": "stdlib", "operation": "@gt", "status": 0, "input": x, "expected": ["-1", "0", "1", "null"]},
9
- )
data/stdlib/gte.fsn DELETED
@@ -1,9 +0,0 @@
1
- # Greater-or-equal: interprets an @OP.compare result. Use as `[a, b] | @OP.compare | @gte`.
2
- # A partial order's compare may return null (incomparable); that propagates as null.
3
- (
4
- -1 => false,
5
- 0 => true,
6
- 1 => true,
7
- null => null,
8
- x => !{"kind": "argument_error", "origin": "stdlib", "operation": "@gte", "status": 0, "input": x, "expected": ["-1", "0", "1", "null"]},
9
- )
data/stdlib/lt.fsn DELETED
@@ -1,9 +0,0 @@
1
- # Strictly less: interprets an @OP.compare result. Use as `[a, b] | @OP.compare | @lt`.
2
- # A partial order's compare may return null (incomparable); that propagates as null.
3
- (
4
- -1 => true,
5
- 0 => false,
6
- 1 => false,
7
- null => null,
8
- x => !{"kind": "argument_error", "origin": "stdlib", "operation": "@lt", "status": 0, "input": x, "expected": ["-1", "0", "1", "null"]},
9
- )
data/stdlib/lte.fsn DELETED
@@ -1,9 +0,0 @@
1
- # Less-or-equal: interprets an @OP.compare result. Use as `[a, b] | @OP.compare | @lte`.
2
- # A partial order's compare may return null (incomparable); that propagates as null.
3
- (
4
- -1 => true,
5
- 0 => true,
6
- 1 => false,
7
- null => null,
8
- x => !{"kind": "argument_error", "origin": "stdlib", "operation": "@lte", "status": 0, "input": x, "expected": ["-1", "0", "1", "null"]},
9
- )