end
# Tried in order at each token start; first match wins. Because we are always
- # at a token boundary, no lookbehinds are needed.
- LITERALS = {
- string: /"[^"]*"|`[^`]*`|'[^']*'/,
- url: %r{[a-z][a-z0-9+.-]*://\S+}i,
- env: /\$\{?[a-z_]\w*\}?/i,
- glob: %r@[\w.~/-]*(?:\*|\[[^\]\s]+\]|\{[^}\s]+\})[\w.~/*?\[\]{},-]*@,
- path: %r@(?:(?:~|\.{1,2})?(?:/[^\s/]+)+/?|[\w.-]+(?:/[\w.-]+)+/?|~(?=\s|\z))@,
- flag: /--?[a-z][\w-]*(?:=\S+)?/i,
- number: /\d+(?:\.\d+)?(?:kb|mb|gb|tb|ms|min|[kmgshd%])?(?![\w]|\.\w)/i,
- file: %r{(?:\.[a-z][\w.-]*|[\w-]+(?:\.[\w-]+)*\.[a-z0-9]{1,5})(?![\w/])}i,
- }.freeze
+ # at a token boundary, no lookbehinds are needed. Set via Lexer.configure.
+ class << self
+ attr_accessor :literals, :connectives
+ end
+
+ def self.configure(ruleset)
+ self.literals = ruleset.literal_patterns
+ self.connectives = ruleset.connective_patterns
+ end
# Sentence punctuation that greedy patterns may swallow ("rm ~/old.log.")
TRAILING = /[.,;:!?]+\z/
end
def self.read_literal(sc)
- LITERALS.each do |kind, re|
+ literals.each do |kind, re|
text = sc.scan(re) or next # Match or skip to the next
if kind == :string
KNOWN_ACTION_WORDS = Set.new(%w[run]).freeze
- ConnectivePatterns = [
- ["after", "that"],
- ["and", "then"],
- ["then"],
- ["next"],
- ]
-
# Length in tokens of a connective starting at i, or 0 if none matches.
def self.connective_length(tokens, i)
length = 0
t, n = tokens[i], tokens[i + 1]
- ConnectivePatterns.each do |pattern|
+ connectives.each do |pattern|
slice = tokens[i..(i+pattern.length-1)].map{|e| (e ? e.text : "") }
if slice == pattern
return pattern.length
module Ruleset
class RulesModule
def types(hash)
+ @types = hash
end
def connectives(list)
+ @connectives = list
end
+
+ def literal_patterns = @types
+ def connective_patterns = @connectives
end
def self.create(name, &block)
end
# Create default ruleset
-Ruleset.create("builtins") do
- types {
+builtins = Ruleset.create("builtins") do
+ types(
string: /"[^"]*"|`[^`]*`|'[^']*'/,
url: %r{[a-z][a-z0-9+.-]*://\S+}i,
env: /\$\{?[a-z_]\w*\}?/i,
flag: /--?[a-z][\w-]*(?:=\S+)?/i,
number: /\d+(?:\.\d+)?(?:kb|mb|gb|tb|ms|min|[kmgshd%])?(?![\w]|\.\w)/i,
file: %r{(?:\.[a-z][\w.-]*|[\w-]+(?:\.[\w-]+)*\.[a-z0-9]{1,5})(?![\w/])}i,
- }
+ )
connectives [
["after", "that"],
["then"],
["next"],
]
-
end
+Lexer.configure(builtins)
+
# Start the REPL now
loop do
print ":> "