skeem 0.2.16 → 0.2.20
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +283 -11
- data/.travis.yml +27 -0
- data/CHANGELOG.md +30 -1
- data/README.md +1 -1
- data/bin/skeem +4 -4
- data/lib/skeem/datum_dsl.rb +36 -35
- data/lib/skeem/grammar.rb +38 -77
- data/lib/skeem/interpreter.rb +8 -4
- data/lib/skeem/primitive/primitive_builder.rb +7 -15
- data/lib/skeem/primitive/primitive_procedure.rb +11 -5
- data/lib/skeem/runtime.rb +8 -10
- data/lib/skeem/s_expr_builder.rb +18 -57
- data/lib/skeem/s_expr_nodes.rb +46 -45
- data/lib/skeem/skeem_exception.rb +1 -0
- data/lib/skeem/skm_binding.rb +4 -5
- data/lib/skeem/skm_compound_datum.rb +2 -3
- data/lib/skeem/skm_element.rb +1 -1
- data/lib/skeem/skm_pair.rb +5 -2
- data/lib/skeem/skm_procedure_exec.rb +3 -0
- data/lib/skeem/skm_simple_datum.rb +12 -10
- data/lib/skeem/skm_unary_expression.rb +25 -26
- data/lib/skeem/tokenizer.rb +14 -7
- data/lib/skeem/version.rb +1 -1
- data/skeem.gemspec +5 -5
- data/spec/skeem/element_visitor_spec.rb +3 -1
- data/spec/skeem/interpreter_spec.rb +3 -1
- data/spec/skeem/lambda_spec.rb +4 -4
- data/spec/skeem/parser_spec.rb +2 -0
- data/spec/skeem/primitive/primitive_builder_spec.rb +5 -5
- data/spec/skeem/primitive/primitive_procedure_spec.rb +1 -1
- data/spec/skeem/runtime_spec.rb +2 -2
- data/spec/skeem/skm_compound_datum_spec.rb +1 -1
- data/spec/skeem/skm_pair_spec.rb +5 -5
- data/spec/skeem/tokenizer_spec.rb +4 -2
- data/spec/spec_helper.rb +13 -10
- metadata +10 -9
data/lib/skeem/tokenizer.rb
CHANGED
@@ -16,8 +16,13 @@ module Skeem
|
|
16
16
|
# Delimiters: parentheses '(', ')'
|
17
17
|
# Separators: comma
|
18
18
|
class Tokenizer
|
19
|
+
# @return [StringScanner]
|
19
20
|
attr_reader(:scanner)
|
21
|
+
|
22
|
+
# @return [Integer] Current line number
|
20
23
|
attr_reader(:lineno)
|
24
|
+
|
25
|
+
# @return [Integer] Offset of start of current line
|
21
26
|
attr_reader(:line_start)
|
22
27
|
|
23
28
|
@@lexeme2name = {
|
@@ -53,7 +58,7 @@ module Skeem
|
|
53
58
|
SYNTAX-RULES
|
54
59
|
UNQUOTE
|
55
60
|
UNQUOTE-SPLICING
|
56
|
-
].map { |x| [x, x] }
|
61
|
+
].map { |x| [x, x.sub(/\*$/, '_STAR')] }.to_h
|
57
62
|
|
58
63
|
class ScanError < StandardError; end
|
59
64
|
|
@@ -84,6 +89,8 @@ module Skeem
|
|
84
89
|
|
85
90
|
private
|
86
91
|
|
92
|
+
# rubocop: disable Lint/DuplicateBranch
|
93
|
+
|
87
94
|
def _next_token
|
88
95
|
skip_intertoken_spaces
|
89
96
|
curr_ch = scanner.peek(1)
|
@@ -109,7 +116,7 @@ module Skeem
|
|
109
116
|
elsif (lexeme = scanner.scan(/[+-]?[0-9]+(?:\.[0-9]*)?(?:(?:e|E)[+-]?[0-9]+)?/))
|
110
117
|
# Order dependency: must be tested after INTEGER case
|
111
118
|
token = build_token('REAL', lexeme)
|
112
|
-
elsif (lexeme = scanner.scan(/#(?:(?:true)|(?:false)|(?:u8)|[
|
119
|
+
elsif (lexeme = scanner.scan(/#(?:(?:true)|(?:false)|(?:u8)|[\\(tfeiodx]|(?:\d+[=#]))/))
|
113
120
|
token = cardinal_token(lexeme)
|
114
121
|
elsif (lexeme = scanner.scan(/"(?:\\"|[^"])*"/)) # Double quotes literal?
|
115
122
|
token = build_token('STRING_LIT', lexeme)
|
@@ -119,7 +126,7 @@ module Skeem
|
|
119
126
|
token = build_token(tok_type, lexeme)
|
120
127
|
elsif (lexeme = scanner.scan(/\|(?:[^|])*\|/)) # Vertical bar delimited
|
121
128
|
token = build_token('IDENTIFIER', lexeme)
|
122
|
-
elsif (lexeme = scanner.scan(/([
|
129
|
+
elsif (lexeme = scanner.scan(/([+\-])((?=\s|[|()";])|$)/))
|
123
130
|
# # R7RS peculiar identifiers case 1: isolated plus and minus as identifiers
|
124
131
|
token = build_token('IDENTIFIER', lexeme)
|
125
132
|
elsif (lexeme = scanner.scan(/[+-][a-zA-Z!$%&*\/:<=>?@^_~+-@][a-zA-Z0-9!$%&*+-.\/:<=>?@^_~+-]*/))
|
@@ -138,6 +145,8 @@ module Skeem
|
|
138
145
|
return token
|
139
146
|
end
|
140
147
|
|
148
|
+
# rubocop: enable Lint/DuplicateBranch
|
149
|
+
|
141
150
|
=begin
|
142
151
|
#u8( This introduces a bytevector constant (section 6.9).
|
143
152
|
Bytevector constants are terminated by ) .
|
@@ -162,11 +171,10 @@ other literal data (section 2.4).
|
|
162
171
|
if (lexeme = scanner.scan(/#\\/))
|
163
172
|
if (lexeme = scanner.scan(/(?:alarm|backspace|delete|escape|newline|null|return|space|tab)/))
|
164
173
|
token = build_token('CHAR', lexeme, :name)
|
165
|
-
elsif (lexeme = scanner.scan(/[^x]/))
|
166
|
-
token = build_token('CHAR', lexeme, :escaped)
|
167
174
|
elsif (lexeme = scanner.scan(/x[0-9a-fA-F]+/))
|
168
175
|
token = build_token('CHAR', lexeme, :hex_value)
|
169
|
-
|
176
|
+
else
|
177
|
+
lexeme = scanner.getch
|
170
178
|
token = build_token('CHAR', lexeme, :escaped)
|
171
179
|
end
|
172
180
|
end
|
@@ -352,7 +360,6 @@ other literal data (section 2.4).
|
|
352
360
|
end
|
353
361
|
|
354
362
|
def skip_block_comment
|
355
|
-
# require 'debug'
|
356
363
|
scanner.skip(/#\|/)
|
357
364
|
nesting_level = 1
|
358
365
|
loop do
|
data/lib/skeem/version.rb
CHANGED
data/skeem.gemspec
CHANGED
@@ -10,6 +10,7 @@ module PkgExtending
|
|
10
10
|
file_list = Dir[
|
11
11
|
'.rubocop.yml',
|
12
12
|
'.rspec',
|
13
|
+
'.travis.yml',
|
13
14
|
'.yardopts',
|
14
15
|
'appveyor.yml',
|
15
16
|
'Gemfile',
|
@@ -23,7 +24,8 @@ module PkgExtending
|
|
23
24
|
'lib/**/*.rb',
|
24
25
|
'lib/**/*.skm',
|
25
26
|
'spec/**/*.rb',
|
26
|
-
'spec/**/*.skm'
|
27
|
+
'spec/**/*.skm',
|
28
|
+
'spec/**/*.yml'
|
27
29
|
]
|
28
30
|
aPackage.files = file_list
|
29
31
|
aPackage.test_files = Dir['spec/**/*_spec.rb']
|
@@ -52,7 +54,7 @@ DESCR
|
|
52
54
|
SUMMARY
|
53
55
|
spec.homepage = 'https://github.com/famished-tiger/Skeem'
|
54
56
|
spec.license = 'MIT'
|
55
|
-
spec.required_ruby_version = '>= 2.
|
57
|
+
spec.required_ruby_version = '>= 2.5.0'
|
56
58
|
|
57
59
|
spec.bindir = 'bin'
|
58
60
|
spec.executables << 'skeem'
|
@@ -60,12 +62,10 @@ SUMMARY
|
|
60
62
|
PkgExtending.pkg_files(spec)
|
61
63
|
PkgExtending.pkg_documentation(spec)
|
62
64
|
# Runtime dependencies
|
63
|
-
spec.add_dependency 'rley', '~> 0.
|
65
|
+
spec.add_dependency 'rley', '~> 0.8.08'
|
64
66
|
|
65
67
|
# Development dependencies
|
66
68
|
spec.add_development_dependency 'bundler', '~> 2.0'
|
67
69
|
spec.add_development_dependency 'rake', '~> 12.0'
|
68
70
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
69
|
-
|
70
|
-
|
71
71
|
end
|
@@ -16,7 +16,7 @@ module Skeem
|
|
16
16
|
end
|
17
17
|
|
18
18
|
it 'could be initialized with a block argument' do
|
19
|
-
expect { Interpreter.new
|
19
|
+
expect { Interpreter.new(&:runtime) }.not_to raise_error
|
20
20
|
end
|
21
21
|
|
22
22
|
it 'should have a parser' do
|
@@ -67,6 +67,7 @@ module Skeem
|
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
70
|
+
# rubocop: disable Style/ExponentialNotation
|
70
71
|
it 'should evaluate isolated real numbers' do
|
71
72
|
samples = [
|
72
73
|
['0.0', 0.0],
|
@@ -80,6 +81,7 @@ module Skeem
|
|
80
81
|
expect(result).to eq(predicted)
|
81
82
|
end
|
82
83
|
end
|
84
|
+
# rubocop: enable Style/ExponentialNotation
|
83
85
|
|
84
86
|
it 'should evaluate isolated strings' do
|
85
87
|
samples = [
|
data/spec/skeem/lambda_spec.rb
CHANGED
@@ -31,7 +31,7 @@ SKEEM
|
|
31
31
|
|
32
32
|
context 'Defining compound procedures:' do
|
33
33
|
it 'should accept the definition of simple procedure with arity 1' do
|
34
|
-
source = definition_set
|
34
|
+
source = "#{definition_set}\nsquare"
|
35
35
|
result = subject.run(source)
|
36
36
|
|
37
37
|
square = result.last
|
@@ -41,7 +41,7 @@ SKEEM
|
|
41
41
|
end
|
42
42
|
|
43
43
|
it 'should accept the definition of simple procedure with arity 2' do
|
44
|
-
source = definition_set
|
44
|
+
source = "#{definition_set}\nsum-of-squares"
|
45
45
|
result = subject.run(source)
|
46
46
|
|
47
47
|
square = result.last
|
@@ -65,14 +65,14 @@ SKEEM
|
|
65
65
|
end
|
66
66
|
|
67
67
|
it 'should support the call to a simple procedure with arity 2' do
|
68
|
-
source = definition_set
|
68
|
+
source = "#{definition_set}\n(sum-of-squares 3 4)"
|
69
69
|
result = subject.run(source)
|
70
70
|
|
71
71
|
expect(result.last).to eq(25)
|
72
72
|
end
|
73
73
|
|
74
74
|
it 'should support the call to a nested lambda procedure' do
|
75
|
-
source = definition_set
|
75
|
+
source = "#{definition_set}\n(f 5)"
|
76
76
|
result = subject.run(source)
|
77
77
|
|
78
78
|
expect(result.last).to eq(136)
|
data/spec/skeem/parser_spec.rb
CHANGED
@@ -45,6 +45,7 @@ module Skeem
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
+
# rubocop: disable Style/ExponentialNotation
|
48
49
|
it 'should parse isolated real numbers' do
|
49
50
|
samples = [
|
50
51
|
['0.0', 0.0],
|
@@ -59,6 +60,7 @@ module Skeem
|
|
59
60
|
expect(ptree.root.value).to eq(predicted)
|
60
61
|
end
|
61
62
|
end
|
63
|
+
# rubocop: enable Style/ExponentialNotation
|
62
64
|
|
63
65
|
it 'should parse isolated strings' do
|
64
66
|
samples = [
|
@@ -654,8 +654,8 @@ SKEEM
|
|
654
654
|
["(append '(a b) '(c d))", array2list_ids(%w[a b c d])],
|
655
655
|
["(append '(a b) '(c) 'd)", array2list_ids(%w[a b c d])],
|
656
656
|
["(append '(a (b)) '((c)))", [SkmIdentifier.create('a'),
|
657
|
-
|
658
|
-
|
657
|
+
SkmPair.create_from_a(array2list_ids(['b'])),
|
658
|
+
SkmPair.create_from_a(array2list_ids(['c']))]],
|
659
659
|
["(append '() 'a)", SkmIdentifier.create('a')]
|
660
660
|
]
|
661
661
|
compare_to_predicted(checks) do |result, expectation|
|
@@ -919,8 +919,8 @@ SKEEM
|
|
919
919
|
result = subject.run(source)
|
920
920
|
expect(result).to be_kind_of(SkmVector)
|
921
921
|
expectation = [SkmInteger.create(0),
|
922
|
-
|
923
|
-
|
922
|
+
SkmPair.new(SkmString.create('Sue'), SkmPair.new(SkmString.create('Sue'), SkmEmptyList.instance)),
|
923
|
+
SkmString.create('Anna')]
|
924
924
|
expect(result).to eq(expectation)
|
925
925
|
|
926
926
|
source = <<-SKEEM
|
@@ -1010,7 +1010,7 @@ SKEEM
|
|
1010
1010
|
err = StandardError
|
1011
1011
|
msg1 = 'Error: assertion failed on line 3, column 4'
|
1012
1012
|
msg2 = 'with <Skeem::SkmBoolean: false>'
|
1013
|
-
expect { subject.run(source) }.to raise_error(err, msg1
|
1013
|
+
expect { subject.run(source) }.to raise_error(err, "#{msg1}, #{msg2}")
|
1014
1014
|
end
|
1015
1015
|
end # context
|
1016
1016
|
end # describe
|
@@ -149,7 +149,7 @@ module Skeem
|
|
149
149
|
expect(pproc.call(rtime, no_arg)).to eq(0)
|
150
150
|
|
151
151
|
many = [SkmString.create('foo'), SkmString.create('bar'),
|
152
|
-
|
152
|
+
SkmString.create('quux')]
|
153
153
|
expect(pproc.call(rtime, many)).to eq(3)
|
154
154
|
end
|
155
155
|
end # context
|
data/spec/skeem/runtime_spec.rb
CHANGED
@@ -96,11 +96,11 @@ module Skeem
|
|
96
96
|
|
97
97
|
it 'should push a call to the stack call' do
|
98
98
|
expect { subject.push_call(sample_call) }.not_to raise_error
|
99
|
-
expect(subject.call_stack.size).
|
99
|
+
expect(subject.call_stack.size).to eq(1)
|
100
100
|
expect(subject.caller).to eq(sample_call)
|
101
101
|
|
102
102
|
subject.push_call(sample_call.clone)
|
103
|
-
expect(subject.call_stack.size).
|
103
|
+
expect(subject.call_stack.size).to eq(2)
|
104
104
|
end
|
105
105
|
|
106
106
|
it 'should pop a call from the call stack' do
|
@@ -50,7 +50,7 @@ module Skeem
|
|
50
50
|
it 'should return its text representation' do
|
51
51
|
txt1 = '<Skeem::SkmCompoundDatum: <Skeem::SkmInteger: 1>,'
|
52
52
|
txt2 = '<Skeem::SkmInteger: 2>, <Skeem::SkmInteger: 3>>'
|
53
|
-
expect(subject.inspect).to eq(txt1
|
53
|
+
expect(subject.inspect).to eq("#{txt1} #{txt2}")
|
54
54
|
end
|
55
55
|
end # context
|
56
56
|
|
data/spec/skeem/skm_pair_spec.rb
CHANGED
@@ -37,7 +37,7 @@ module Skeem
|
|
37
37
|
|
38
38
|
context 'Provided services:' do
|
39
39
|
let(:runtime) { Runtime.new(SkmFrame.new) }
|
40
|
-
let(:
|
40
|
+
let(:list_length2) { SkmPair.new(integer(10), subject) }
|
41
41
|
let(:quirk_element) { double('three') }
|
42
42
|
let(:quirk_members) { [integer(10), quirk_element] }
|
43
43
|
|
@@ -71,7 +71,7 @@ module Skeem
|
|
71
71
|
expect(subject.length).to eq(1)
|
72
72
|
|
73
73
|
# Use a list of length 2
|
74
|
-
expect(
|
74
|
+
expect(list_length2.length).to eq(2)
|
75
75
|
end
|
76
76
|
|
77
77
|
it 'should respond false to `eqv?` message' do
|
@@ -113,7 +113,7 @@ module Skeem
|
|
113
113
|
expect(subject.to_a).to eq([sample_car])
|
114
114
|
|
115
115
|
# Use a list of length 2
|
116
|
-
expect(
|
116
|
+
expect(list_length2.to_a).to eq([integer(10), sample_car])
|
117
117
|
end
|
118
118
|
|
119
119
|
it 'should return the last pair of a proper list' do
|
@@ -133,7 +133,7 @@ module Skeem
|
|
133
133
|
|
134
134
|
it 'should return the last element of a list' do
|
135
135
|
expect(subject.last).to eq(sample_car)
|
136
|
-
expect(
|
136
|
+
expect(list_length2.last).to eq(sample_car)
|
137
137
|
end
|
138
138
|
|
139
139
|
it 'should append a new element to a list' do
|
@@ -227,7 +227,7 @@ module Skeem
|
|
227
227
|
expect(subject.inspect).to eq(predicted)
|
228
228
|
|
229
229
|
predicted = '<Skeem::SkmPair: <Skeem::SkmInteger: 10>, <Skeem::SkmInteger: 3>>'
|
230
|
-
expect(
|
230
|
+
expect(list_length2.inspect).to eq(predicted)
|
231
231
|
end
|
232
232
|
end # context
|
233
233
|
end # describe
|
@@ -47,8 +47,8 @@ module Skeem
|
|
47
47
|
tokens.each { |token| expect(token).to be_kind_of(Rley::Lexical::Token) }
|
48
48
|
terminals = tokens.map(&:terminal)
|
49
49
|
prediction = %w[LPAREN RPAREN APOSTROPHE
|
50
|
-
|
51
|
-
|
50
|
+
GRAVE_ACCENT PERIOD
|
51
|
+
COMMA COMMA_AT_SIGN ]
|
52
52
|
expect(terminals).to eq(prediction)
|
53
53
|
end
|
54
54
|
end # context
|
@@ -129,6 +129,7 @@ module Skeem
|
|
129
129
|
end # context
|
130
130
|
|
131
131
|
context 'Real number recognition:' do
|
132
|
+
# rubocop: disable Style/ExponentialNotation
|
132
133
|
it 'should tokenize real numbers' do
|
133
134
|
tests = [
|
134
135
|
# couple [raw input, expected]
|
@@ -140,6 +141,7 @@ module Skeem
|
|
140
141
|
|
141
142
|
check_tokens(tests, 'REAL')
|
142
143
|
end
|
144
|
+
# rubocop: enable Style/ExponentialNotation
|
143
145
|
end # context
|
144
146
|
|
145
147
|
context 'Character literal recognition:' do
|
data/spec/spec_helper.rb
CHANGED
@@ -17,26 +17,29 @@ RSpec.configure do |config|
|
|
17
17
|
config.full_backtrace = true
|
18
18
|
end
|
19
19
|
|
20
|
+
|
20
21
|
module InterpreterSpec
|
21
22
|
def expect_expr(aSkeemExpr)
|
22
23
|
result = subject.run(aSkeemExpr)
|
23
24
|
expect(result)
|
24
25
|
end
|
25
26
|
|
27
|
+
# rubocop: disable Lint/RescueException
|
28
|
+
|
26
29
|
# This method assumes that 'subject' is a Skeem::Interpreter instance.
|
27
30
|
def compare_to_predicted(arrActualsPredictions)
|
28
31
|
arrActualsPredictions.each_with_index do |(source, predicted), index|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
expect(result).to eq(predicted)
|
35
|
-
end
|
36
|
-
rescue Exception => e
|
37
|
-
$stderr.puts "Row #{index + 1} failed."
|
38
|
-
throw e
|
32
|
+
result = subject.run(source)
|
33
|
+
if block_given?
|
34
|
+
yield result, predicted
|
35
|
+
else
|
36
|
+
expect(result).to eq(predicted)
|
39
37
|
end
|
38
|
+
|
39
|
+
rescue Exception => e
|
40
|
+
$stderr.puts "Row #{index + 1} failed."
|
41
|
+
throw e
|
40
42
|
end
|
41
43
|
end
|
44
|
+
# rubocop: enable Lint/RescueException
|
42
45
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: skeem
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.20
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dimitri Geshef
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-11-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rley
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 0.8.08
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 0.8.08
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -77,6 +77,7 @@ extra_rdoc_files:
|
|
77
77
|
files:
|
78
78
|
- ".rspec"
|
79
79
|
- ".rubocop.yml"
|
80
|
+
- ".travis.yml"
|
80
81
|
- ".yardopts"
|
81
82
|
- CHANGELOG.md
|
82
83
|
- Gemfile
|
@@ -139,7 +140,7 @@ homepage: https://github.com/famished-tiger/Skeem
|
|
139
140
|
licenses:
|
140
141
|
- MIT
|
141
142
|
metadata: {}
|
142
|
-
post_install_message:
|
143
|
+
post_install_message:
|
143
144
|
rdoc_options:
|
144
145
|
- --charset=UTF-8 --exclude="examples|spec"
|
145
146
|
require_paths:
|
@@ -148,15 +149,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
148
149
|
requirements:
|
149
150
|
- - ">="
|
150
151
|
- !ruby/object:Gem::Version
|
151
|
-
version: 2.
|
152
|
+
version: 2.5.0
|
152
153
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
153
154
|
requirements:
|
154
155
|
- - ">="
|
155
156
|
- !ruby/object:Gem::Version
|
156
157
|
version: '0'
|
157
158
|
requirements: []
|
158
|
-
rubygems_version: 3.
|
159
|
-
signing_key:
|
159
|
+
rubygems_version: 3.1.4
|
160
|
+
signing_key:
|
160
161
|
specification_version: 4
|
161
162
|
summary: Skeem is an interpreter of a subset of the Scheme programming language. Scheme
|
162
163
|
is a descendent of the Lisp language.
|