-- Copyright 2016 Christian Hesse
-- systemd unit file LPeg lexer.

local l = require('lexer')
local token, word_match = l.token, l.word_match
local P, R, S = lpeg.P, lpeg.R, lpeg.S

local M = {_NAME = 'systemd'}

-- Whitespace.
local ws = token(l.WHITESPACE, l.space^1)

-- Comments.
local comment = token(l.COMMENT, l.starts_line(S(';#')) * l.nonnewline^0)

-- Strings.
local sq_str = l.delimited_range("'")
local dq_str = l.delimited_range('"')
local section_word = word_match{
  'Automount',
  'BusName',
  'Install',
  'Mount',
  'Path',
  'Service',
  'Service',
  'Socket',
  'Timer',
  'Unit'
}
local string = token(l.STRING, sq_str + dq_str + '[' * section_word * ']')

-- Numbers.
local dec = l.digit^1 * ('_' * l.digit^1)^0
local oct_num = '0' * S('01234567_')^1
local integer = S('+-')^-1 * (l.hex_num + oct_num + dec)
local number = token(l.NUMBER, (l.float + integer))

-- Keywords.
local keyword = token(l.KEYWORD, word_match({
  -- boolean values
  'true',
  'false',
  'on',
  'off',
  'yes',
  'no',

  -- service types
  'forking',
  'simple',
  'oneshot',
  'dbus',
  'notify',
  'idle',

  -- special system units
  'basic.target',
  'ctrl-alt-del.target',
  'cryptsetup.target',
  'dbus.service',
  'dbus.socket',
  'default.target',
  'display-manager.service',
  'emergency.target',
  'exit.target',
  'final.target',
  'getty.target',
  'graphical.target',
  'hibernate.target',
  'hybrid-sleep.target',
  'halt.target',
  'initrd-fs.target',
  'kbrequest.target',
  'kexec.target',
  'local-fs.target',
  'multi-user.target',
  'network-online.target',
  'paths.target',
  'poweroff.target',
  'reboot.target',
  'remote-fs.target',
  'rescue.target',
  'initrd-root-fs.target',
  'runlevel2.target',
  'runlevel3.target',
  'runlevel4.target',
  'runlevel5.target',
  'shutdown.target',
  'sigpwr.target',
  'sleep.target',
  'slices.target',
  'sockets.target',
  'suspend.target',
  'swap.target',
  'sysinit.target',
  'syslog.socket',
  'system-update.target',
  'timers.target',
  'umount.target',

  -- special system units for devices
  'bluetooth.target',
  'printer.target',
  'smartcard.target',
  'sound.target',

  -- special passive system units
  'cryptsetup-pre.target',
  'local-fs-pre.target',
  'network.target',
  'network-pre.target',
  'nss-lookup.target',
  'nss-user-lookup.target',
  'remote-fs-pre.target',
  'rpcbind.target',
  'time-sync.target',

  -- specail slice units
  '-.slice',
  'system.slice',
  'user.slice',
  'machine.slice',

  -- environment variables
  'PATH',
  'LANG',
  'USER',
  'LOGNAME',
  'HOME',
  'SHELL',
  'XDG_RUNTIME_DIR',
  'XDG_SESSION_ID',
  'XDG_SEAT',
  'XDG_VTNR',
  'MAINPID',
  'MANAGERPID',
  'LISTEN_FDS',
  'LISTEN_PID',
  'LISTEN_FDNAMES',
  'NOTIFY_SOCKET',
  'WATCHDOG_PID',
  'WATCHDOG_USEC',
  'TERM'
}, '.-'))

-- Options.
local option_word = word_match{
  -- unit section options
  'Description',
  'Documentation',
  'Requires',
  'Requisite',
  'Wants',
  'BindsTo',
  'PartOf',
  'Conflicts',
  'Before',
  'After',
  'OnFailure',
  'PropagatesReloadTo',
  'ReloadPropagatedFrom',
  'JoinsNamespaceOf',
  'RequiresMountsFor',
  'OnFailureJobMode',
  'IgnoreOnIsolate',
  'StopWhenUnneeded',
  'RefuseManualStart',
  'RefuseManualStop',
  'AllowIsolate',
  'DefaultDependencies',
  'JobTimeoutSec',
  'JobTimeoutAction',
  'JobTimeoutRebootArgument',
  'StartLimitInterval',
  'StartLimitBurst',
  'StartLimitAction',
  'RebootArgument',
  'ConditionArchitecture',
  'ConditionVirtualization',
  'ConditionHost',
  'ConditionKernelCommandLine',
  'ConditionSecurity',
  'ConditionCapability',
  'ConditionACPower',
  'ConditionNeedsUpdate',
  'ConditionFirstBoot',
  'ConditionPathExists',
  'ConditionPathExistsGlob',
  'ConditionPathIsDirectory',
  'ConditionPathIsSymbolicLink',
  'ConditionPathIsMountPoint',
  'ConditionPathIsReadWrite',
  'ConditionDirectoryNotEmpty',
  'ConditionFileNotEmpty',
  'ConditionFileIsExecutable',
  'AssertArchitecture',
  'AssertVirtualization',
  'AssertHost',
  'AssertKernelCommandLine',
  'AssertSecurity',
  'AssertCapability',
  'AssertACPower',
  'AssertNeedsUpdate',
  'AssertFirstBoot',
  'AssertPathExists',
  'AssertPathExistsGlob',
  'AssertPathIsDirectory',
  'AssertPathIsSymbolicLink',
  'AssertPathIsMountPoint',
  'AssertPathIsReadWrite',
  'AssertDirectoryNotEmpty',
  'AssertFileNotEmpty',
  'AssertFileIsExecutable',
  'SourcePath',

  -- install section options
  'Alias',
  'WantedBy',
  'RequiredBy',
  'Also',
  'DefaultInstance',

  -- service section options
  'Type',
  'RemainAfterExit',
  'GuessMainPID',
  'PIDFile',
  'BusName',
  'BusPolicy',
  'ExecStart',
  'ExecStartPre',
  'ExecStartPost',
  'ExecReload',
  'ExecStop',
  'ExecStopPost',
  'RestartSec',
  'TimeoutStartSec',
  'TimeoutStopSec',
  'TimeoutSec',
  'RuntimeMaxSec',
  'WatchdogSec',
  'Restart',
  'SuccessExitStatus',
  'RestartPreventExitStatus',
  'RestartForceExitStatus',
  'PermissionsStartOnly',
  'RootDirectoryStartOnly',
  'NonBlocking',
  'NotifyAccess',
  'Sockets',
  'FailureAction',
  'FileDescriptorStoreMax',
  'USBFunctionDescriptors',
  'USBFunctionStrings',

  -- socket section options
  'ListenStream',
  'ListenDatagram',
  'ListenSequentialPacket',
  'ListenFIFO',
  'ListenSpecial',
  'ListenNetlink',
  'ListenMessageQueue',
  'ListenUSBFunction',
  'SocketProtocol',
  'BindIPv6Only',
  'Backlog',
  'BindToDevice',
  'SocketUser',
  'SocketGroup',
  'SocketMode',
  'DirectoryMode',
  'Accept',
  'Writable',
  'MaxConnections',
  'KeepAlive',
  'KeepAliveTimeSec',
  'KeepAliveIntervalSec',
  'KeepAliveProbes',
  'NoDelay',
  'Priority',
  'DeferAcceptSec',
  'ReceiveBuffer',
  'SendBuffer',
  'IPTOS',
  'IPTTL',
  'Mark',
  'ReusePort',
  'SmackLabel',
  'SmackLabelIPIn',
  'SmackLabelIPOut',
  'SELinuxContextFromNet',
  'PipeSize',
  'MessageQueueMaxMessages',
  'MessageQueueMessageSize',
  'FreeBind',
  'Transparent',
  'Broadcast',
  'PassCredentials',
  'PassSecurity',
  'TCPCongestion',
  'ExecStartPre',
  'ExecStartPost',
  'ExecStopPre',
  'ExecStopPost',
  'TimeoutSec',
  'Service',
  'RemoveOnStop',
  'Symlinks',
  'FileDescriptorName',

  -- mount section options
  'What',
  'Where',
  'Type',
  'Options',
  'SloppyOptions',
  'DirectoryMode',
  'TimeoutSec',

  -- path section options
  'PathExists',
  'PathExistsGlob',
  'PathChanged',
  'PathModified',
  'DirectoryNotEmpty',
  'Unit',
  'MakeDirectory',
  'DirectoryMode',

  -- timer section options
  'OnActiveSec',
  'OnBootSec',
  'OnStartupSec',
  'OnUnitActiveSec',
  'OnUnitInactiveSec',
  'OnCalendar',
  'AccuracySec',
  'RandomizedDelaySec',
  'Unit',
  'Persistent',
  'WakeSystem',
  'RemainAfterElapse',

  -- exec section options
  'WorkingDirectory',
  'RootDirectory',
  'User',
  'Group',
  'SupplementaryGroups',
  'Nice',
  'OOMScoreAdjust',
  'IOSchedulingClass',
  'IOSchedulingPriority',
  'CPUSchedulingPolicy',
  'CPUSchedulingPriority',
  'CPUSchedulingResetOnFork',
  'CPUAffinity',
  'UMask',
  'Environment',
  'EnvironmentFile',
  'PassEnvironment',
  'StandardInput',
  'StandardOutput',
  'StandardError',
  'TTYPath',
  'TTYReset',
  'TTYVHangup',
  'TTYVTDisallocate',
  'SyslogIdentifier',
  'SyslogFacility',
  'SyslogLevel',
  'SyslogLevelPrefix',
  'TimerSlackNSec',
  'LimitCPU',
  'LimitFSIZE',
  'LimitDATA',
  'LimitSTACK',
  'LimitCORE',
  'LimitRSS',
  'LimitNOFILE',
  'LimitAS',
  'LimitNPROC',
  'LimitMEMLOCK',
  'LimitLOCKS',
  'LimitSIGPENDING',
  'LimitMSGQUEUE',
  'LimitNICE',
  'LimitRTPRIO',
  'LimitRTTIME',
  'PAMName',
  'CapabilityBoundingSet',
  'AmbientCapabilities',
  'SecureBits',
  'Capabilities',
  'ReadWriteDirectories',
  'ReadOnlyDirectories',
  'InaccessibleDirectories',
  'PrivateTmp',
  'PrivateDevices',
  'PrivateNetwork',
  'ProtectSystem',
  'ProtectHome',
  'MountFlags',
  'UtmpIdentifier',
  'UtmpMode',
  'SELinuxContext',
  'AppArmorProfile',
  'SmackProcessLabel',
  'IgnoreSIGPIPE',
  'NoNewPrivileges',
  'SystemCallFilter',
  'SystemCallErrorNumber',
  'SystemCallArchitectures',
  'RestrictAddressFamilies',
  'Personality',
  'RuntimeDirectory',
  'RuntimeDirectoryMode'
}
local preproc = token(l.PREPROCESSOR, option_word)

-- Identifiers.
local word = (l.alpha + '_') * (l.alnum + S('_.'))^0
local identifier = token(l.IDENTIFIER, word)

-- Operators.
local operator = token(l.OPERATOR, '=')

M._rules = {
  {'whitespace', ws},
  {'keyword', keyword},
  {'string', string},
  {'preproc', preproc},
  {'identifier', identifier},
  {'comment', comment},
  {'number', number},
  {'operator', operator},
}

M._LEXBYLINE = true

return M
