ascode 0.4.2 → 0.5.0
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/README.md +5 -6
- data/lib/ascode/parser/condition_block.rb +67 -0
- data/lib/ascode/parser/function.rb +47 -0
- data/lib/ascode/{functions.md → parser/functions.md} +1 -1
- data/lib/ascode/parser/literal.rb +46 -0
- data/lib/ascode/parser/main.rb +67 -0
- data/lib/ascode.rb +2 -2
- metadata +7 -5
- data/lib/ascode/functions.rb +0 -33
- data/lib/ascode/parser.rb +0 -148
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d7d02a2015c6ea648b76aedd3144765b8f130376016b865026aaae8f15a1fb38
|
4
|
+
data.tar.gz: fbc32bb909699a1ed819612b0f7e190d1d1c794bffffe7f5b653bffd1083fd23
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 34f50589e7fb8dbee4c2c766611fc5ebd91c19fc7fd6c6eb82e80de0b5034d449fb50a6bb08efb4422d2b8bf707a03a0f46b173574bc35f5fca50fa10bdd79ad
|
7
|
+
data.tar.gz: b68e5200f7f82852ac04d74d85ed59a5ab40b0feadeec83048f6c411c406ae8062c8df2f2044717a2d3ffec1bd759786e421b19a3ec1a0796288594dee71da3a
|
data/README.md
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
# ascode
|
2
2
|
[](https://rubygems.org/gems/ascode) [](https://rubygems.org/gems/ascode)
|
3
3
|
|
4
|
-
> Esoteric golfing language
|
5
|
-
|
4
|
+
> **Esoteric golfing language**
|
6
5
|
> In active development, so behavior may be changed without prior notice.
|
7
6
|
|
8
7
|
- [Getting Started](#getting-started)
|
@@ -18,8 +17,8 @@ gem install ascode
|
|
18
17
|
|
19
18
|
###### Run
|
20
19
|
|
21
|
-
Function
|
22
|
-
`](https://github.com/sudoio/ascode/blob/master/lib/ascode/functions.md).
|
20
|
+
Function reference is available in [`lib/ascode/parser/functions.md
|
21
|
+
`](https://github.com/sudoio/ascode/blob/master/lib/ascode/parser/functions.md).
|
23
22
|
|
24
23
|
At this moment, the only way to play with `ascode` is `irb`.
|
25
24
|
|
@@ -37,5 +36,5 @@ irb(main):003:0> Ascode.run "1.1*"
|
|
37
36
|
|
38
37
|
### Features
|
39
38
|
- Implicit input/output
|
40
|
-
- If you did not output anything,
|
41
|
-
- During `pop`, if the value is not in the stack, it will be grabbed via
|
39
|
+
- If you did not output anything, `O` (`output` function) will be called at the end of the program.
|
40
|
+
- During `pop`, if the value is not in the stack, it will be grabbed via `I` (`input` function).
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Ascode
|
2
|
+
module Parser
|
3
|
+
class ConditionBlock
|
4
|
+
attr_reader :block
|
5
|
+
|
6
|
+
def initialize(code, block_begin)
|
7
|
+
@code = code
|
8
|
+
@begin = block_begin + 1
|
9
|
+
|
10
|
+
@true = ""
|
11
|
+
@false = ""
|
12
|
+
end
|
13
|
+
|
14
|
+
def parse
|
15
|
+
@block = @code[@begin..-1]
|
16
|
+
|
17
|
+
@end = find "]"
|
18
|
+
raise "Unmatched '[' detected." if @end == -1
|
19
|
+
|
20
|
+
@block = @block[0..(@end - 1)]
|
21
|
+
|
22
|
+
@split_pos = find ":"
|
23
|
+
split
|
24
|
+
|
25
|
+
ast_action
|
26
|
+
end
|
27
|
+
|
28
|
+
def find(what)
|
29
|
+
# TODO: Ignore '[', ':', ']' when in string
|
30
|
+
level = 0
|
31
|
+
@block.split("").to_enum.each_with_index do |char, index|
|
32
|
+
return index if char == what && level.zero?
|
33
|
+
level += 1 if char == "["
|
34
|
+
level -= 1 if char == "]"
|
35
|
+
end
|
36
|
+
|
37
|
+
-1
|
38
|
+
end
|
39
|
+
|
40
|
+
def split
|
41
|
+
if @split_pos == -1
|
42
|
+
@true = @block
|
43
|
+
elsif @split_pos.zero?
|
44
|
+
@false = @block
|
45
|
+
else
|
46
|
+
@true = @block[0..(@split_pos - 1)]
|
47
|
+
@false = @block[(@split_pos + 1)..-1]
|
48
|
+
end
|
49
|
+
|
50
|
+
@true = parse_part(@true)
|
51
|
+
@false = parse_part(@false)
|
52
|
+
end
|
53
|
+
|
54
|
+
def parse_part(part)
|
55
|
+
(Main.new part, false).parse
|
56
|
+
end
|
57
|
+
|
58
|
+
def ast_action
|
59
|
+
@ast = {
|
60
|
+
action: "condition",
|
61
|
+
true_block: @true,
|
62
|
+
false_block: @false
|
63
|
+
}
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Ascode
|
2
|
+
module Parser
|
3
|
+
class Function
|
4
|
+
attr_reader :csv_data
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@csv_data = []
|
8
|
+
|
9
|
+
filename = File.join(File.dirname(__FILE__), "functions.md")
|
10
|
+
File.open(filename, "r") do |file|
|
11
|
+
read_csv file
|
12
|
+
end
|
13
|
+
|
14
|
+
@csv_data.delete_at 0
|
15
|
+
@csv_data.delete_at 0
|
16
|
+
end
|
17
|
+
|
18
|
+
def parse(char)
|
19
|
+
function = @csv_data.to_enum.find do |value|
|
20
|
+
value[:short] == char
|
21
|
+
end
|
22
|
+
|
23
|
+
raise "Unexpected function: '#{char}'" unless function
|
24
|
+
|
25
|
+
{
|
26
|
+
action: function[:long]
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
def read_csv(file)
|
31
|
+
until (line = file.gets).nil?
|
32
|
+
src = line.strip.split("|")
|
33
|
+
|
34
|
+
@csv_data.push(
|
35
|
+
short: clear_quotes(src[0]),
|
36
|
+
long: clear_quotes(src[1])
|
37
|
+
)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def clear_quotes(str)
|
42
|
+
return str unless str
|
43
|
+
str.delete("`")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -4,7 +4,7 @@ Character|Name|`pop`s|`push`es|Description
|
|
4
4
|
`O`|`output`|`a`||`a` -> `stdout`
|
5
5
|
`P`|`pop`|`a`||
|
6
6
|
`[`|`condition_begin`|`a`|| If `a` = `0`, `nil` or `false`, then part after `~` is executed
|
7
|
-
|
7
|
+
`:`|`condition_else`|||
|
8
8
|
`]`|`condition_end`|||
|
9
9
|
`+`|`plus`|`a`, `b`|`c`|`c` = `a` + `b`
|
10
10
|
`-`|`minus`|`a`, `b`|`c`|`c` = `a` - `b`
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require_relative "../converter"
|
2
|
+
|
3
|
+
module Ascode
|
4
|
+
module Parser
|
5
|
+
class Literal
|
6
|
+
attr_reader :literal
|
7
|
+
|
8
|
+
def initialize(code, literal_begin)
|
9
|
+
@code = code
|
10
|
+
@begin = literal_begin
|
11
|
+
end
|
12
|
+
|
13
|
+
def parse
|
14
|
+
@quoted = @code[@begin] == '"'
|
15
|
+
@begin += 1 if @quoted
|
16
|
+
@literal = @code[@begin..-1]
|
17
|
+
|
18
|
+
@end = find_end
|
19
|
+
@literal = @literal[0..(@end - 1)]
|
20
|
+
|
21
|
+
@literal = Converter.convert @literal
|
22
|
+
ast
|
23
|
+
end
|
24
|
+
|
25
|
+
def find_end
|
26
|
+
escape = false
|
27
|
+
@literal.split("").each_with_index do |char, index|
|
28
|
+
if @quoted
|
29
|
+
return index if char == '"' && !escape
|
30
|
+
escape = char == "\\"
|
31
|
+
else
|
32
|
+
return index unless char =~ /[0-9.]/
|
33
|
+
end
|
34
|
+
end
|
35
|
+
-1
|
36
|
+
end
|
37
|
+
|
38
|
+
def ast
|
39
|
+
@ast = {
|
40
|
+
action: "push",
|
41
|
+
what: @literal
|
42
|
+
}
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require_relative "condition_block"
|
2
|
+
require_relative "function"
|
3
|
+
require_relative "literal"
|
4
|
+
|
5
|
+
module Ascode
|
6
|
+
module Parser
|
7
|
+
class Main
|
8
|
+
def initialize(code, impl_o = true)
|
9
|
+
@function = Function.new
|
10
|
+
@code = code
|
11
|
+
|
12
|
+
@do_implicit_output = impl_o
|
13
|
+
end
|
14
|
+
|
15
|
+
def parse
|
16
|
+
@ast = []
|
17
|
+
@skip_chars = 0
|
18
|
+
@implicit_output = @do_implicit_output
|
19
|
+
|
20
|
+
@code.split("").to_enum.each_with_index do |char, index|
|
21
|
+
if @skip_chars > 0
|
22
|
+
@skip_chars -= 1
|
23
|
+
next
|
24
|
+
end
|
25
|
+
|
26
|
+
character char, index
|
27
|
+
end
|
28
|
+
|
29
|
+
@ast.push(action: "output") if @implicit_output
|
30
|
+
|
31
|
+
@ast
|
32
|
+
end
|
33
|
+
|
34
|
+
def character(char, index)
|
35
|
+
if char == "\""
|
36
|
+
literal(index, false)
|
37
|
+
elsif char =~ /[0-9.]/
|
38
|
+
literal(index, true)
|
39
|
+
elsif char == "["
|
40
|
+
block index
|
41
|
+
else
|
42
|
+
function char
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def literal(index, num)
|
47
|
+
add = num ? 0 : 1
|
48
|
+
|
49
|
+
literal = Literal.new @code, index
|
50
|
+
@ast.push literal.parse
|
51
|
+
@skip_chars = literal.literal.to_s.length + add
|
52
|
+
end
|
53
|
+
|
54
|
+
def block(index)
|
55
|
+
block = ConditionBlock.new @code, index
|
56
|
+
@ast.push block.parse
|
57
|
+
@skip_chars = block.block.length
|
58
|
+
end
|
59
|
+
|
60
|
+
def function(char)
|
61
|
+
function = @function.parse char
|
62
|
+
@implicit_output = false if function[:action] == "output"
|
63
|
+
@ast.push function
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/ascode.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
require_relative "ascode/interpreter"
|
2
|
-
require_relative "ascode/parser"
|
2
|
+
require_relative "ascode/parser/main"
|
3
3
|
|
4
4
|
module Ascode
|
5
5
|
def self.run(code)
|
6
|
-
parser = Parser.new code
|
6
|
+
parser = Parser::Main.new code
|
7
7
|
ast = parser.parse
|
8
8
|
|
9
9
|
interpreter = Interpreter.new ast
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ascode
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Artem Varaksa
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-01-
|
11
|
+
date: 2018-01-17 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Esoteric golfing language. In active development, so behavior may be
|
14
14
|
changed without prior notice.
|
@@ -21,10 +21,12 @@ files:
|
|
21
21
|
- README.md
|
22
22
|
- lib/ascode.rb
|
23
23
|
- lib/ascode/converter.rb
|
24
|
-
- lib/ascode/functions.md
|
25
|
-
- lib/ascode/functions.rb
|
26
24
|
- lib/ascode/interpreter.rb
|
27
|
-
- lib/ascode/parser.rb
|
25
|
+
- lib/ascode/parser/condition_block.rb
|
26
|
+
- lib/ascode/parser/function.rb
|
27
|
+
- lib/ascode/parser/functions.md
|
28
|
+
- lib/ascode/parser/literal.rb
|
29
|
+
- lib/ascode/parser/main.rb
|
28
30
|
homepage: https://github.com/sudoio/ascode
|
29
31
|
licenses:
|
30
32
|
- MIT
|
data/lib/ascode/functions.rb
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
module Ascode
|
2
|
-
class Functions
|
3
|
-
attr_reader :csv_data
|
4
|
-
|
5
|
-
def initialize
|
6
|
-
@csv_data = []
|
7
|
-
|
8
|
-
filename = File.join(File.dirname(__FILE__), "functions.md")
|
9
|
-
File.open(filename, "r") do |file|
|
10
|
-
read_csv file
|
11
|
-
end
|
12
|
-
|
13
|
-
@csv_data.delete_at 0
|
14
|
-
@csv_data.delete_at 0
|
15
|
-
end
|
16
|
-
|
17
|
-
def read_csv(file)
|
18
|
-
until (line = file.gets).nil?
|
19
|
-
src = line.strip.split("|")
|
20
|
-
|
21
|
-
@csv_data.push(
|
22
|
-
short: clear_quotes(src[0]),
|
23
|
-
long: clear_quotes(src[1])
|
24
|
-
)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def clear_quotes(str)
|
29
|
-
return str unless str
|
30
|
-
str.delete("`")
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
data/lib/ascode/parser.rb
DELETED
@@ -1,148 +0,0 @@
|
|
1
|
-
require_relative "functions"
|
2
|
-
require_relative "converter"
|
3
|
-
|
4
|
-
module Ascode
|
5
|
-
class Parser
|
6
|
-
def initialize(code, impl_o = true)
|
7
|
-
@fun = Functions.new
|
8
|
-
@code = code
|
9
|
-
|
10
|
-
@do_implicit_output = impl_o
|
11
|
-
end
|
12
|
-
|
13
|
-
def parse
|
14
|
-
@ast = []
|
15
|
-
@push_buffer = false
|
16
|
-
@skip_chars = 0
|
17
|
-
@is_quoted = false
|
18
|
-
@escape = false
|
19
|
-
@implicit_output = @do_implicit_output
|
20
|
-
|
21
|
-
@code.split("").to_enum.each_with_index do |char, index|
|
22
|
-
if @skip_chars > 0
|
23
|
-
@skip_chars -= 1
|
24
|
-
next
|
25
|
-
else
|
26
|
-
parse_character char, index
|
27
|
-
end
|
28
|
-
end
|
29
|
-
ast_add_buffer
|
30
|
-
|
31
|
-
ast_add_action "output" if @implicit_output
|
32
|
-
|
33
|
-
@ast
|
34
|
-
end
|
35
|
-
|
36
|
-
def parse_condition_block(begin_index)
|
37
|
-
block_code = @code[(begin_index + 1)..-1]
|
38
|
-
|
39
|
-
end_index = (find_block_end block_code) + begin_index
|
40
|
-
raise "Unmatched '[' at position #{begin_index}" if end_index == -1
|
41
|
-
|
42
|
-
block_code = @code[(begin_index + 1)..(end_index + 1)]
|
43
|
-
|
44
|
-
split_pos = find_block_split(block_code)
|
45
|
-
|
46
|
-
if split_pos == -1
|
47
|
-
true_block_ast = (Parser.new block_code).parse
|
48
|
-
false_block_ast = []
|
49
|
-
elsif split_pos.zero?
|
50
|
-
true_block_ast = []
|
51
|
-
false_block_ast = (Parser.new block_code[1..-1]).parse
|
52
|
-
else
|
53
|
-
true_block_ast = (Parser.new block_code[0..(split_pos - 1)], false).parse
|
54
|
-
false_block_ast = (Parser.new block_code[(split_pos + 1)..-1], false).parse
|
55
|
-
end
|
56
|
-
|
57
|
-
@ast.push(action: "condition", true_block: true_block_ast, false_block: false_block_ast )
|
58
|
-
|
59
|
-
end_index + 2
|
60
|
-
end
|
61
|
-
|
62
|
-
def find_block_end(block)
|
63
|
-
end_index = -1
|
64
|
-
block.split("").to_enum.with_index.reverse_each do |char, index|
|
65
|
-
if char == "]"
|
66
|
-
end_index = index
|
67
|
-
break
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
end_index - 1
|
72
|
-
end
|
73
|
-
|
74
|
-
def find_block_split(block)
|
75
|
-
level = 0
|
76
|
-
split_pos = -1
|
77
|
-
block.split("").to_enum.each_with_index do |char, index|
|
78
|
-
if char == "["
|
79
|
-
level += 1
|
80
|
-
elsif char == "]"
|
81
|
-
level -= 1
|
82
|
-
elsif char == "~" && level.zero?
|
83
|
-
split_pos = index
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
split_pos
|
88
|
-
end
|
89
|
-
|
90
|
-
def parse_character(char, index)
|
91
|
-
if char == "\\"
|
92
|
-
@escape = true
|
93
|
-
return
|
94
|
-
end
|
95
|
-
|
96
|
-
if char == "\"" && !@escape
|
97
|
-
ast_add_buffer if @is_quoted
|
98
|
-
@is_quoted = !@is_quoted
|
99
|
-
elsif @is_quoted || char =~ /[0-9.]/
|
100
|
-
buffer_push char
|
101
|
-
elsif char == "["
|
102
|
-
end_index = parse_condition_block index
|
103
|
-
@skip_chars = end_index - index
|
104
|
-
elsif char == " "
|
105
|
-
ast_add_buffer
|
106
|
-
else
|
107
|
-
ast_add_buffer
|
108
|
-
parse_function char
|
109
|
-
end
|
110
|
-
|
111
|
-
@escape = false
|
112
|
-
end
|
113
|
-
|
114
|
-
def parse_function(char)
|
115
|
-
function = @fun.csv_data.to_enum.find do |value|
|
116
|
-
value[:short] == char
|
117
|
-
end
|
118
|
-
|
119
|
-
raise "Unexpected identifier: '#{char}'" unless function
|
120
|
-
ast_add_action function[:long]
|
121
|
-
end
|
122
|
-
|
123
|
-
def buffer_push(char)
|
124
|
-
if @push_buffer
|
125
|
-
@push_buffer += char
|
126
|
-
else
|
127
|
-
@push_buffer = char
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
def ast_add_buffer
|
132
|
-
return unless @push_buffer
|
133
|
-
|
134
|
-
@push_buffer = Converter.convert @push_buffer
|
135
|
-
|
136
|
-
ast_add_action "push", @push_buffer
|
137
|
-
@push_buffer = false
|
138
|
-
end
|
139
|
-
|
140
|
-
def ast_add_action(name, what = nil)
|
141
|
-
@implicit_output = false if name == "output"
|
142
|
-
|
143
|
-
data = { action: name }
|
144
|
-
data[:what] = what if what
|
145
|
-
@ast.push data
|
146
|
-
end
|
147
|
-
end
|
148
|
-
end
|