Compare commits

..

73 Commits

Author SHA1 Message Date
wash d333497a03 meta: add clang-format config 2026-05-24 20:26:49 +01:00
wash 28f8c8f71c doc: sample: add/update sample source files 2026-05-24 20:26:40 +01:00
wash bb09e8b8d1 vim: add operator symbols and keywords 2026-05-24 20:26:29 +01:00
wash 446ee99306 build: add new sub-directories 2026-05-24 20:25:51 +01:00
wash 070a2be969 bshell: initialise verbs and table formatter on start 2026-05-24 20:25:36 +01:00
wash 862028f5a8 debug: update runtime opcode printing 2026-05-24 20:24:44 +01:00
wash d7443d7810 bshell: add an assembly instance to register builtin commands 2026-05-24 20:22:48 +01:00
wash f772f4ba20 alias: implement a set of builtin aliases 2026-05-24 20:22:30 +01:00
wash 0ee1a3575d cmdlet: implement a set of builtin cmdlets 2026-05-24 20:22:19 +01:00
wash 92e58ec7c6 runtime: implement opcodes to run commands and load/store variables and properties 2026-05-24 20:22:05 +01:00
wash 8f7a0b9fb8 runtime: add a class to represent variables and a data structure to store them 2026-05-24 20:21:39 +01:00
wash b6aeb8d4bf runtime: implement pipelines and an interface for calling commands 2026-05-24 20:21:06 +01:00
wash d8c694a43e command: add a hierarchy of classes to represent different types of commands 2026-05-24 20:20:06 +01:00
wash 4f65e3b9f2 format: implement tabular output of fx objects 2026-05-24 20:19:32 +01:00
wash c9cf6b229e bshell: add a class to manage cmdlet verbs 2026-05-24 20:18:12 +01:00
wash c3bf6dcee6 compile: implement compilation of doubles and arrays 2026-05-24 20:17:35 +01:00
wash e679c41725 compile: fstring: fix string elements being loaded at the wrong time 2026-05-24 20:17:13 +01:00
wash 30bfce5701 script-block: implement storing double values in constpool 2026-05-24 20:15:30 +01:00
wash e829c77297 file: update fx_array usage 2026-05-24 20:14:51 +01:00
wash ca8533334b ast: implement to_string for int and double nodes 2026-05-24 20:14:32 +01:00
wash fad13b5476 parse: lots of improvements 2026-05-24 20:13:55 +01:00
wash 82a035f5cc line-ed: update fx_array usage 2026-05-24 20:11:51 +01:00
wash 5a23da4cfe line-ed: add unicode support 2026-05-17 16:50:48 +01:00
wash 2f200e8d1b bshell: generate and evaluate bytecode for shell input 2026-05-17 15:35:22 +01:00
wash f629b05672 debug: implement printers for bytecode instructions and runtime values 2026-05-17 15:34:53 +01:00
wash 58b4bb2c51 runtime: temporary implementation of arithmetic bytecode evaluation 2026-05-17 15:34:24 +01:00
wash 0e8dc9cb6f bshell: implement bytecode generation for arithmetic expressions and if-statements 2026-05-17 15:33:48 +01:00
wash 2cee984092 runtime: add definitions for bshell vm bytecode 2026-05-17 15:33:20 +01:00
wash 1f8b9ff0d3 ast: implement post-order ast traversal 2026-05-17 15:32:19 +01:00
wash e490754625 bshell: add script-block class
bshell_scriptblock acts as a self-contained collection of executable bytecode and associated const value table.
2026-05-17 15:30:20 +01:00
wash 405686f6ec parse: make multi-line structure parsing more robust 2026-05-17 15:29:59 +01:00
wash d78f80a121 parse: lex: fix some state transition edge cases 2026-05-17 15:29:14 +01:00
wash 05dcf1f8b6 operator: add script-block pseudo-operator 2026-05-17 15:28:29 +01:00
wash 34114ca451 debug: add print support for new ast node types 2026-05-12 23:00:03 +01:00
wash 750e3df7d5 lang: add arithmetic operator definitions 2026-05-12 22:59:50 +01:00
wash 83189d4d9b ast: add lots of ast node definitions 2026-05-12 22:59:29 +01:00
wash 1ea3471f0d parse: implement parsing of complex command arguments 2026-05-12 22:58:59 +01:00
wash c4529d474a parse: implement parsing of arithmetic expressions and data structures 2026-05-12 22:58:48 +01:00
wash 227e73853c parse: implement parsing of function definitions 2026-05-12 22:57:49 +01:00
wash 7d2e45edcb parse: implement parsing of if-statements 2026-05-12 22:57:31 +01:00
wash 26e2a63200 parse: implement parsing of {...} statement blocks 2026-05-12 22:57:16 +01:00
wash 0cd7ca2dde parse: add a basic function to report parse errors 2026-05-12 22:56:40 +01:00
wash 5ce780e037 parse: add a range of internal parser definitions 2026-05-12 22:55:59 +01:00
wash 2235d8593b parse: lex: add a range of operator tokens 2026-05-12 22:54:50 +01:00
wash 64903c821c parse: lex: add missing lex ctx members 2026-05-12 22:54:17 +01:00
wash 39457aa7e6 parse: add some more generic token parser functions 2026-05-12 22:53:26 +01:00
wash 440561cb39 parse: implement parsing of semicolon-delimited statement lists 2026-05-12 22:52:48 +01:00
wash cc450da31e parse: lex: support tokens terminating multiple lex states in certain circumstances 2026-05-12 22:51:45 +01:00
wash e3b92fe4f2 parse: lex: fix scanning of sub-expressions and fstrings in statement mode 2026-05-12 22:51:15 +01:00
wash b2190dd4d0 parse: lex: improve scanning of more complex redirection expressions 2026-05-12 22:48:57 +01:00
wash 3dd5f12ee5 parse: lex: fix string state not terminating when encountering a dquote 2026-05-12 22:48:08 +01:00
wash 721e0f851a parse: lex: add a range of new symbol tokens 2026-05-12 22:47:29 +01:00
wash dee4e5dbf7 parse: lex: fix arithmetic state handling dquote symbols incorrectly 2026-05-12 22:45:43 +01:00
wash 39125cea50 parse: lex: switch from arithmetic to statement when scanning =, |, and \n 2026-05-12 22:45:05 +01:00
wash 7ddc140dbf parse: lex: fix arithmetic state not scanning operator tokens 2026-05-12 22:44:33 +01:00
wash a408b9efa2 parse: lex: move per-state token settings to state source files 2026-05-11 23:57:35 +01:00
wash 0c21be8d67 parse: lex: add proper data-driven state-machine functionality
movement between lexer states is now defined (almost) exclusively
by a table of outgoing links defined for each state type.

the main lexer system uses this table to determine when, how, and to
where the state should be changed.

also add a dedicated lexer state for scanning hashtables, due to the
particularly unique rules that apply within.
2026-05-11 23:02:02 +01:00
wash 304eb80e0d bshell: add debug output support for operator tokens 2026-05-10 19:15:41 +01:00
wash ffdb28ba22 parse: lex: replace expression scanner with statement; implement complex-word scanner
also fix a bunch of scanning edge-cases
2026-05-10 19:14:24 +01:00
wash 7aa2aee5bd parse: lex: implement recording coordinates of lex tokens 2026-05-10 19:13:29 +01:00
wash 7071630af8 parse: lex: add flags for lexer states 2026-05-10 19:10:14 +01:00
wash f5d847736a bshell.vim: fix highlighting of nested parentheses in string interpolation 2026-05-10 14:20:40 +01:00
wash 94048c6508 doc: sample: add/update sample script files 2026-05-10 14:20:08 +01:00
wash 3398de6fa9 ast: update redirection node to support fd-redirection with sub-expression 2026-05-10 14:19:29 +01:00
wash ba8a2111eb parse: update parser to support new lexer behaviour 2026-05-10 14:19:06 +01:00
wash 5ea41fcc6e parse: lex: re-implement lexer as a state machine to allow more complex scanning behaviour 2026-05-10 14:18:46 +01:00
wash cfaf53040b parse: improve command arg parsing 2026-05-09 21:21:51 +01:00
wash 7d95d57f98 bshell: first-pass implementation of a syntax lexer/parser 2026-05-09 19:00:02 +01:00
wash b12f59ed2c bshell: add functions for printing lex tokens and ast nodes 2026-05-09 18:59:42 +01:00
wash 090f6a0002 bshell: add ast node definitions 2026-05-09 18:59:13 +01:00
wash 8b0295faf2 bshell: add line-editor and file-based input support 2026-05-07 10:52:00 +01:00
wash 3c15bb1609 meta: add vim plugin for syntax highlighting 2026-05-07 10:51:04 +01:00
wash 84bb6cc0aa meta: add build system 2026-05-02 12:52:32 +01:00
145 changed files with 15624 additions and 0 deletions
+60
View File
@@ -0,0 +1,60 @@
BasedOnStyle: WebKit
IndentWidth: 8
DerivePointerAlignment: false
PointerAlignment: Right
ColumnLimit: 80
AlignAfterOpenBracket: AlwaysBreak
AlignConsecutiveAssignments: None
AlignConsecutiveBitFields: None
AlignConsecutiveDeclarations: None
AlignConsecutiveMacros: AcrossEmptyLinesAndComments
AlignEscapedNewlines: Right
AlignOperands: AlignAfterOperator
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: false
AllowAllConstructorInitializersOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: Empty
AllowShortCaseLabelsOnASingleLine: false
AllowShortEnumsOnASingleLine: false
AllowShortFunctionsOnASingleLine: false
AllowShortIfStatementsOnASingleLine: false
AllowShortLambdasOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: false
BinPackParameters: false
ExperimentalAutoDetectBinPacking: false
BitFieldColonSpacing: Both
BreakBeforeBraces: Linux
BreakBeforeBinaryOperators: All
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeComma
BreakInheritanceList: BeforeComma
BreakStringLiterals: true
ContinuationIndentWidth: 8
Cpp11BracedListStyle: true
IncludeBlocks: Regroup
SortIncludes: true
IndentRequires: true
NamespaceIndentation: Inner
ReflowComments: true
SpacesBeforeTrailingComments: 3
TabWidth: 8
UseTab: AlignWithSpaces
BreakAfterReturnType: Automatic
PenaltyBreakAssignment: 1000000
PenaltyReturnTypeOnItsOwnLine: 100000000
PenaltyExcessCharacter: 10000000
PenaltyBreakOpenParenthesis: 0
PenaltyBreakBeforeFirstCallParameter: 0
PenaltyIndentedWhitespace: 0
AttributeMacros:
- FX_API
ForEachMacros:
- fx_btree_foreach
- fx_queue_foreach
MacroBlockBegin: "FX_(TYPE|ASSEMBLY).*_BEGIN"
MacroBlockEnd: "FX_(TYPE|ASSEMBLY).*_END"
+1
View File
@@ -143,3 +143,4 @@ tags
# End of https://www.toptal.com/developers/gitignore/api/c,vim,linux,macos,cmake
build/
.cache/
+50
View File
@@ -0,0 +1,50 @@
cmake_minimum_required(VERSION 3.31)
project(bshell C)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
if (NOT DEFINED bshell_interactive)
set(bshell_interactive 1)
endif ()
find_package(Python COMPONENTS Interpreter REQUIRED)
find_package(FX REQUIRED COMPONENTS
fx.runtime
fx.collections
fx.term)
execute_process(
COMMAND ${Python_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/build-id.py
OUTPUT_VARIABLE bshell_version)
set(source_dirs
ast compile parse parse/lex parse/syntax
runtime format command cmdlets aliases)
if (bshell_interactive EQUAL 1)
message(STATUS "Interactive support: Enabled")
set(source_dirs ${source_dirs} line-ed)
else ()
message(STATUS "Interactive support: Disabled")
endif ()
file(GLOB bshell_sources
bshell/*.c
bshell/*.h)
foreach (d ${source_dirs})
file(GLOB sources
bshell/${d}/*.c
bshell/${d}/*.h)
set(bshell_sources ${bshell_sources} ${sources})
endforeach (d)
message(STATUS "B Shell version: ${bshell_version}")
add_executable(bshell ${bshell_sources})
target_link_libraries(bshell FX::Runtime FX::Collections FX::Term)
target_compile_definitions(bshell PUBLIC
BSHELL_VERSION="${bshell_version}"
BSHELL_INTERACTIVE=${bshell_interactive})
View File
+1
View File
@@ -0,0 +1 @@
autocmd BufNewFile,BufRead *.bshell setfiletype bshell
+4
View File
@@ -0,0 +1,4 @@
setlocal tabstop=8
setlocal softtabstop=4
setlocal shiftwidth=4
setlocal expandtab
+176
View File
@@ -0,0 +1,176 @@
if exists('b:current_syntax')
finish
endif
let s:save_cpo = &cpoptions
set cpoptions&vim
setlocal iskeyword+=-
syn match bshellFunctionRef /\<[A-Za-z][A-Za-z0-9]*\(-[A-Za-z0-9][A-Za-z0-9]*\)\+\>/
syn match bshellVariable /\$[A-Za-z_][A-Za-z0-9_]*/
syn match bshellArgFlag /\<-[A-Za-z][A-Za-z0-9]*\(-[A-Za-z0-9][A-Za-z0-9]*\)*\>/
syn keyword bshellKeyword func if elseif else
syn keyword bshellBuiltinCommand echo
syn keyword bshellTodo contained TODO FIXME XXX NOTE HACK TBD
syn match bshellLineComment /#.*$/ contains=bshellTodo
syn region bshellInterpolation matchgroup=bshellInterpolationDelimiter start=+$(+ end=+)+ keepend contained contains=@bshellAll
syn region bshellInterpolation matchgroup=bshellInterpolationDelimiter start="$(" end=")" contained contains=ALL
syn region bshellInterpolatedString matchgroup=bshellString start=+"+ end=+"+ extend contains=bshellVariable,bshellInterpolation
syn region bshellLiteralString matchgroup=bshellString start=+\'+ end=+\'+ extend contains=bshellSpecialChar,bshellSpecialError,bshellUnicodeNumber,@Spell
syn region bshell1NestedParentheses start="(" skip="\\\\\|\\)" matchgroup=bshellInterpolation end=")" transparent contained
syn case ignore
syn match bshellInteger "\<0b[01_]*[01]\%([lu]\|lu\|ul\)\=\>" display
syn match bshellInteger "\<\d\+\%(_\+\d\+\)*\%([lu]\|lu\|ul\)\=\>" display
syn match bshellInteger "\<-\d\+\%(_\+\d\+\)*\%([lu]\|lu\|ul\)\=\>" display
syn match bshellInteger "\<0x[[:xdigit:]_]*\x\%([lu]\|lu\|ul\)\=\>" display
syn match bshellReal "\<\d\+\%(_\+\d\+\)*\.\d\+\%(_\+\d\+\)*\%\(e[-+]\=\d\+\%(_\+\d\+\)*\)\=[fdm]\=" display
syn match bshellReal "\.\d\+\%(_\+\d\+\)*\%(e[-+]\=\d\+\%(_\+\d\+\)*\)\=[fdm]\=\>" display
syn match bshellReal "\<\d\+\%(_\+\d\+\)*e[-+]\=\d\+\%(_\+\d\+\)*[fdm]\=\>" display
syn match bshellReal "\<\d\+\%(_\+\d\+\)*[fdm]\>" display
syn case match
syn cluster bshellNumber contains=bshellInteger,bshellReal
syn cluster bshellLiteral contains=@bshellNumber,@bshellString
syn cluster bshellAll contains=
\ @bshellLiteral,bshellVariable,bshellKeyword,bshellArgFlag,
\ bshellFunctionRef,bshellBuiltinCommand,bshellSymbolOp,
\ bshellKeywordOp,bshellTerminator,bshellControlSymbols,
\ bshellBraces,bshellParens,bshellStructureDelimiter
syn match bshellBraces "[{}]" display
syn match bshellParens "[()]" display
syn match bshellStructureDelimiter "@[{(]" display
syn match bshellControlSymbols "[[\]]" display
syn match bshellControlSymbols "|" display
syn match bshellSymbolOp "\*" display
syn match bshellSymbolOp "[\/*%&!,]" display
syn match bshellSymbolOp "[+\-/*%!]=" display
syn match bshellSymbolOp "\s\+[\-+]\s\+" display
syn match bshellTerminator "[;]" display
syn keyword bshellKeywordOp -f -band -bor -bxor -bnot -shl -shr
\ -eq -ne -gt -lt -ge -le -match -notmatch -replace
\ -like -notlike -in -notin -contains -notcontains
\ -and -or -xor -not -split -join -is -isnot -as
hi def link bshellKeyword Statement
hi def link bshellArgFlag Tag
hi def link bshellVariable Identifier
hi def link bshellLineComment Comment
hi def link bshellFunctionRef Function
hi def link bshellBuiltinCommand Function
hi def link bshellString String
hi def link bshellInterpolatedString String
hi def link bshellLiteralString String
hi def link bshellInteger Number
hi def link bshellReal Float
hi def link bshellInterpolationDelimiter Delimiter
hi def link bshellStructureDelimiter Delimiter
hi def link bshellBraces Delimiter
hi def link bshellParens Delimiter
hi def link bshellControlSymbols Operator
hi def link bshellSymbolOp Operator
hi def link bshellKeywordOp Operator
hi def link bshellTerminator Delimiter
" The default highlighting.
" hi def link bshellUnspecifiedStatement Statement
" hi def link bshellUnsupportedStatement Statement
"
" hi def link bshellGlobalNamespaceAlias Include
"
" hi def link bshellType Type
"
" hi def link bshellStorage Keyword
" hi def link bshellIsAs Keyword
" hi def link bshellAccessor Keyword
" hi def link bshellBuiltinVar @variable.builtin
" hi def link bshellSelfVar @variable.builtin
"
" hi def link bshellStatement Statement
" hi def link bshellRepeat Repeat
" hi def link bshellConditional Conditional
" hi def link bshellSelectorLabel Tag
" hi def link bshellUnnamedLabel Comment
" hi def link bshellUnnamedVariable Comment
" hi def link bshellLambdaParameter @variable.builtin
" hi def link bshellException Exception
"
" hi def link bshellParens Delimiter
" hi def link bshellBraces Structure
" hi def link bshellControlSymbols Keyword
"
" hi def link bshellModifier StorageClass
" hi def link bshellAccessModifier bshellModifier
" hi def link bshellAsyncModifier bshellModifier
" hi def link bshellCheckedModifier bshellModifier
" hi def link bshellManagedModifier bshellModifier
" hi def link bshellUsingModifier bshellModifier
"
" hi def link bshellTodo Todo
" hi def link bshellComment Comment
" hi def link bshellLineComment bshellComment
" hi def link bshellBlockComment bshellComment
" hi def link bshellLineContinuation bshellComment
"
" hi def link bshellKeywordOperator Keyword
" hi def link bshellAsyncOperator bshellKeywordOperator
" hi def link bshellTypeOf bshellKeywordOperator
" hi def link bshellTypeOfOperand Typedef
" hi def link bshellTypeOfError Error
" hi def link bshellOpSymbols Operator
" hi def link bshellPackageAccessOperator Operator
" hi def link bshellOtherSymbols Structure
" hi def link bshellLogicSymbols Operator
" hi def link bshellWordOperator Operator
"
" hi def link bshellSpecialError Error
" hi def link bshellSpecialCharError Error
" hi def link bshellString String
" hi def link bshellQuote String
" hi def link bshellInterpolatedString String
" hi def link bshellVerbatimString String
" hi def link bshellInterVerbString String
" hi def link bshellVerbatimQuote SpecialChar
"
" hi def link bshellConstant Constant
" hi def link bshellNull Constant
" hi def link bshellBoolean Boolean
" hi def link bshellCharacter Character
" hi def link bshellSpecialChar SpecialChar
" hi def link bshellInteger Number
" hi def link bshellReal Float
" hi def link bshellWord Identifier
" hi def link bshellUnicodeNumber SpecialChar
" hi def link bshellUnicodeSpecifier SpecialChar
" hi def link bshellInterpolationDelimiter Delimiter
" hi def link bshellInterpolationAlignDel bshellInterpolationDelimiter
" hi def link bshellInterpolationFormat bshellInterpolationDelimiter
" hi def link bshellInterpolationFormatDel bshellInterpolationDelimiter
"
" hi def link bshellGenericBraces bshellBraces
"
" hi def link bshellAtomName Constant
"
" hi def link bshellComplexMessageName Function
" hi def link bshellUnaryMessageName Function
" hi def link bshellPropertyName @property
" hi def link bshellPropertySymbol Statement
"
" hi def link bshellStatementSeparator Comment
" hi def link bshellMessageTerminator @punctuation.special
"
" hi def link bshellPackageStmtIdentifier @string.special.url
" hi def link bshellUseStmtIdentifier @module
let b:current_syntax = 'bshell'
let &cpoptions = s:save_cpo
unlet s:save_cpo
" vim: vts=16,28
+45
View File
@@ -0,0 +1,45 @@
#include "../command/alias.h"
#include "../command/command.h"
#include <fx/reflection/function.h>
#include <fx/string.h>
#include <fx/vector.h>
#define BSHELL_TYPE_ECHO (bshell_echo_get_type())
fx_type_id bshell_echo_get_type(void);
FX_DECLARE_TYPE(bshell_echo);
FX_TYPE_CLASS_DECLARATION_BEGIN(bshell_echo)
FX_TYPE_CLASS_DECLARATION_END(bshell_echo)
struct bshell_echo_p {
};
static void init(fx_object *obj, void *priv)
{
}
FX_TYPE_CLASS_BEGIN(bshell_echo)
FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT)
FX_INTERFACE_ENTRY(to_string) = NULL;
FX_TYPE_VTABLE_INTERFACE_END(fx_object, FX_TYPE_OBJECT)
FX_TYPE_VTABLE_INTERFACE_BEGIN(bshell_alias, BSHELL_TYPE_ALIAS)
FX_INTERFACE_ENTRY(a_callable_name) = "echo";
FX_INTERFACE_ENTRY(a_target_name) = "Write-Output";
FX_TYPE_VTABLE_INTERFACE_END(bshell_cmdlet, BSHELL_TYPE_CMDLET)
FX_TYPE_VTABLE_INTERFACE_BEGIN(bshell_command, BSHELL_TYPE_COMMAND)
FX_TYPE_VTABLE_INTERFACE_END(bshell_command, BSHELL_TYPE_COMMAND)
FX_TYPE_CLASS_END(bshell_echo)
FX_TYPE_DEFINITION_BEGIN(bshell_echo)
FX_TYPE_ID(0xedd507bf, 0x2e32, 0x425b, 0x9c73, 0x2ba9539eace0);
FX_TYPE_NAME("bshell.core.echo");
FX_TYPE_EXTENDS(BSHELL_TYPE_ALIAS);
FX_TYPE_CLASS(bshell_echo_class);
FX_TYPE_INSTANCE_INIT(init);
FX_TYPE_INSTANCE_PRIVATE(struct bshell_echo_p);
FX_TYPE_DEFINITION_END(bshell_echo)
+45
View File
@@ -0,0 +1,45 @@
#include "../command/alias.h"
#include "../command/command.h"
#include <fx/reflection/function.h>
#include <fx/string.h>
#include <fx/vector.h>
#define BSHELL_TYPE_PERCENT (bshell_percent_get_type())
fx_type_id bshell_percent_get_type(void);
FX_DECLARE_TYPE(bshell_percent);
FX_TYPE_CLASS_DECLARATION_BEGIN(bshell_percent)
FX_TYPE_CLASS_DECLARATION_END(bshell_percent)
struct bshell_percent_p {
};
static void init(fx_object *obj, void *priv)
{
}
FX_TYPE_CLASS_BEGIN(bshell_percent)
FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT)
FX_INTERFACE_ENTRY(to_string) = NULL;
FX_TYPE_VTABLE_INTERFACE_END(fx_object, FX_TYPE_OBJECT)
FX_TYPE_VTABLE_INTERFACE_BEGIN(bshell_alias, BSHELL_TYPE_ALIAS)
FX_INTERFACE_ENTRY(a_callable_name) = "%";
FX_INTERFACE_ENTRY(a_target_name) = "ForEach-Object";
FX_TYPE_VTABLE_INTERFACE_END(bshell_cmdlet, BSHELL_TYPE_CMDLET)
FX_TYPE_VTABLE_INTERFACE_BEGIN(bshell_command, BSHELL_TYPE_COMMAND)
FX_TYPE_VTABLE_INTERFACE_END(bshell_command, BSHELL_TYPE_COMMAND)
FX_TYPE_CLASS_END(bshell_percent)
FX_TYPE_DEFINITION_BEGIN(bshell_percent)
FX_TYPE_ID(0x4255a9de, 0xd6f6, 0x4708, 0x829c, 0xffa4c6034edb);
FX_TYPE_NAME("bshell.core.percent");
FX_TYPE_EXTENDS(BSHELL_TYPE_ALIAS);
FX_TYPE_CLASS(bshell_percent_class);
FX_TYPE_INSTANCE_INIT(init);
FX_TYPE_INSTANCE_PRIVATE(struct bshell_percent_p);
FX_TYPE_DEFINITION_END(bshell_percent)
+24
View File
@@ -0,0 +1,24 @@
#include <fx/macros.h>
#include <fx/reflection/assembly.h>
FX_ASSEMBLY_BEGIN(bshell)
FX_ASSEMBLY_NAME("bshell");
FX_ASSEMBLY_VERSION(0, 1, 0, 0);
FX_ASSEMBLY_EXPORT_TYPE("bshell", "cmdlet", bshell_cmdlet);
FX_ASSEMBLY_EXPORT_TYPE("bshell", "scriptblock", bshell_scriptblock);
FX_ASSEMBLY_EXPORT_TYPE("bshell.core", "get_verb", bshell_get_verb);
FX_ASSEMBLY_EXPORT_TYPE(
"bshell.core",
"write_output",
bshell_write_output);
FX_ASSEMBLY_EXPORT_TYPE(
"bshell.core",
"foreach_object",
bshell_foreach_object);
FX_ASSEMBLY_EXPORT_TYPE(
"bshell.core",
"get_command",
bshell_get_command);
FX_ASSEMBLY_EXPORT_TYPE("bshell.core", "percent", bshell_percent);
FX_ASSEMBLY_EXPORT_TYPE("bshell.core", "echo", bshell_echo);
FX_ASSEMBLY_END(bshell)
+25
View File
@@ -0,0 +1,25 @@
#include "../parse/token.h"
#include "ast.h"
static enum bshell_status collect_children(
struct ast_node *node,
struct ast_iterator *it)
{
struct array_ast_node *array = (struct array_ast_node *)node;
fx_queue_entry *cur = fx_queue_first(&array->n_items);
while (cur) {
struct ast_node *child
= fx_unbox(struct ast_node, cur, n_entry);
ast_iterator_enqueue(it, child);
cur = fx_queue_next(cur);
}
return BSHELL_SUCCESS;
}
struct ast_node_definition array_ast_node = {
.def_id = AST_ARRAY,
.def_node_size = sizeof(struct array_ast_node),
.def_collect_children = collect_children,
};
+270
View File
@@ -0,0 +1,270 @@
#include "ast.h"
#include "../status.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
extern struct ast_node_definition null_ast_node;
extern struct ast_node_definition int_ast_node;
extern struct ast_node_definition double_ast_node;
extern struct ast_node_definition word_ast_node;
extern struct ast_node_definition var_ast_node;
extern struct ast_node_definition string_ast_node;
extern struct ast_node_definition fstring_ast_node;
extern struct ast_node_definition cmdcall_ast_node;
extern struct ast_node_definition pipeline_ast_node;
extern struct ast_node_definition redirection_ast_node;
extern struct ast_node_definition block_ast_node;
extern struct ast_node_definition stmt_list_ast_node;
extern struct ast_node_definition func_ast_node;
extern struct ast_node_definition array_ast_node;
extern struct ast_node_definition hashtable_ast_node;
extern struct ast_node_definition hashtable_item_ast_node;
extern struct ast_node_definition if_ast_node;
extern struct ast_node_definition if_branch_ast_node;
extern struct ast_node_definition op_ast_node;
static const struct ast_node_definition *ast_node_definitions[] = {
[AST_NULL] = &null_ast_node,
[AST_INT] = &int_ast_node,
[AST_DOUBLE] = &double_ast_node,
[AST_WORD] = &word_ast_node,
[AST_VAR] = &var_ast_node,
[AST_STRING] = &string_ast_node,
[AST_FSTRING] = &fstring_ast_node,
[AST_CMDCALL] = &cmdcall_ast_node,
[AST_PIPELINE] = &pipeline_ast_node,
[AST_REDIRECTION] = &redirection_ast_node,
[AST_BLOCK] = &block_ast_node,
[AST_STMT_LIST] = &stmt_list_ast_node,
[AST_FUNC] = &func_ast_node,
[AST_ARRAY] = &array_ast_node,
[AST_IF] = &if_ast_node,
[AST_IF_BRANCH] = &if_branch_ast_node,
[AST_HASHTABLE] = &hashtable_ast_node,
[AST_HASHTABLE_ITEM] = &hashtable_item_ast_node,
[AST_OP] = &op_ast_node,
};
static const size_t nr_ast_node_definitions = sizeof ast_node_definitions
/ sizeof ast_node_definitions[0];
struct ast_node *ast_node_create(enum ast_node_type type)
{
assert(type < nr_ast_node_definitions);
const struct ast_node_definition *def = ast_node_definitions[type];
struct ast_node *out = malloc(def->def_node_size);
if (!out) {
return NULL;
}
memset(out, 0x0, def->def_node_size);
out->n_type = type;
return out;
}
void ast_node_destroy(struct ast_node *node)
{
assert(node->n_type < nr_ast_node_definitions);
struct ast_iterator it = {0};
ast_iterator_enqueue(&it, node);
while (1) {
node = ast_iterator_peek(&it);
if (!node) {
break;
}
const struct ast_node_definition *def = ast_node_definitions
[node->n_type];
if (def->def_cleanup) {
def->def_cleanup(node);
}
ast_iterator_dequeue(&it);
free(node);
}
}
void ast_node_to_string(const struct ast_node *node, fx_bstr *out)
{
const struct ast_node_definition *def = ast_node_definitions
[node->n_type];
if (def->def_to_string) {
def->def_to_string(node, out);
}
}
#define ENUM_STR(x) \
case x: \
return #x
const char *ast_node_type_to_string(enum ast_node_type type)
{
switch (type) {
ENUM_STR(AST_NONE);
ENUM_STR(AST_NULL);
ENUM_STR(AST_STMT_LIST);
ENUM_STR(AST_INT);
ENUM_STR(AST_DOUBLE);
ENUM_STR(AST_WORD);
ENUM_STR(AST_STRING);
ENUM_STR(AST_FSTRING);
ENUM_STR(AST_VAR);
ENUM_STR(AST_VAR_SPLAT);
ENUM_STR(AST_FLAG);
ENUM_STR(AST_CMDCALL);
ENUM_STR(AST_PIPELINE);
ENUM_STR(AST_REDIRECTION);
ENUM_STR(AST_BLOCK);
ENUM_STR(AST_FUNC);
ENUM_STR(AST_IF);
ENUM_STR(AST_IF_BRANCH);
ENUM_STR(AST_OP);
ENUM_STR(AST_ARRAY);
ENUM_STR(AST_HASHTABLE);
ENUM_STR(AST_HASHTABLE_ITEM);
default:
return "<unknown>";
}
}
struct ast_node *ast_iterator_peek(struct ast_iterator *it)
{
fx_queue_entry *cur = fx_queue_first(&it->it_queue);
if (!cur) {
return NULL;
}
return fx_unbox(struct ast_node, cur, n_it.e_entry);
}
struct ast_node *ast_iterator_dequeue(struct ast_iterator *it)
{
fx_queue_entry *cur = fx_queue_first(&it->it_queue);
if (!cur) {
return NULL;
}
struct ast_node *node = fx_unbox(struct ast_node, cur, n_it.e_entry);
const struct ast_node_definition *def = ast_node_definitions
[node->n_type];
it->it_insert_after = cur;
if (def->def_collect_children) {
def->def_collect_children(node, it);
}
fx_queue_pop_front(&it->it_queue);
return fx_unbox(struct ast_node, cur, n_it.e_entry);
}
void ast_iterator_enqueue(struct ast_iterator *it, struct ast_node *node)
{
unsigned long new_depth = 0;
fx_queue_entry *cur = fx_queue_first(&it->it_queue);
if (cur) {
struct ast_node *cur_node = fx_unbox(
struct ast_node,
cur,
n_it.e_entry);
new_depth = cur_node->n_it.e_depth + 1;
}
node->n_it.e_depth = new_depth;
if (!it->it_insert_after) {
fx_queue_push_back(&it->it_queue, &node->n_it.e_entry);
return;
}
fx_queue_insert_after(
&it->it_queue,
&node->n_it.e_entry,
it->it_insert_after);
it->it_insert_after = &node->n_it.e_entry;
}
enum bshell_status ast_node_iterate(
struct ast_node *node,
struct ast_iterator *it,
ast_iterator_callback callback,
void *arg)
{
fx_queue_push_back(&it->it_queue, &node->n_it.e_entry);
node->n_it.e_depth = 0;
fx_queue_entry *entry = fx_queue_first(&it->it_queue);
while (entry) {
struct ast_iterator_entry *it_entry = fx_unbox(
struct ast_iterator_entry,
entry,
e_entry);
node = fx_unbox(struct ast_node, it_entry, n_it);
if (!node) {
/* this should never happen. */
return BSHELL_ERR_INTERNAL_FAILURE;
}
struct ast_iterate_result result = callback(
node,
AST_ITERATION_PRE,
it,
arg);
if (result.r_status != BSHELL_SUCCESS
|| result.r_flags & AST_ITERATE_STOP) {
return result.r_status;
}
const struct ast_node_definition *type = ast_node_definitions
[node->n_type];
if (type->def_collect_children
&& result.r_flags & AST_ITERATE_ADD_CHILDREN) {
it->it_insert_after = entry;
type->def_collect_children(node, it);
}
if (!(result.r_flags & AST_ITERATE_REPEAT)) {
entry = fx_queue_next(entry);
}
}
while (!fx_queue_empty(&it->it_queue)) {
fx_queue_entry *entry = fx_queue_last(&it->it_queue);
if (!entry) {
break;
}
node = fx_unbox(struct ast_node, entry, n_it);
if (!node) {
/* this should never happen. */
return BSHELL_ERR_INTERNAL_FAILURE;
}
struct ast_iterate_result result = callback(
node,
AST_ITERATION_POST,
it,
arg);
if (result.r_status != BSHELL_SUCCESS
|| result.r_flags & AST_ITERATE_STOP) {
return result.r_status;
}
if (!(result.r_flags & AST_ITERATE_REPEAT)) {
fx_queue_pop_back(&it->it_queue);
}
}
return BSHELL_SUCCESS;
}
+227
View File
@@ -0,0 +1,227 @@
#ifndef AST_H_
#define AST_H_
#include "../status.h"
#include <fx/bstr.h>
#include <fx/queue.h>
struct lex_token;
#define AST_ITERATE_OK(flags) \
((struct ast_iterate_result) {.r_status = BSHELL_SUCCESS, \
.r_flags = (flags)})
#define AST_ITERATE_ERR(status) \
((struct ast_iterate_result) {.r_status = (status)})
enum ast_node_type {
AST_NONE = 0x00u,
AST_NULL,
AST_STMT_LIST,
AST_INT,
AST_DOUBLE,
AST_WORD,
AST_STRING,
AST_FSTRING,
AST_VAR,
AST_VAR_SPLAT,
AST_FLAG,
AST_CMDCALL,
AST_FUNCALL,
AST_PIPELINE,
AST_REDIRECTION,
AST_BLOCK,
AST_FUNC,
AST_ARRAY,
AST_HASHTABLE,
AST_HASHTABLE_ITEM,
AST_OP,
AST_IF,
AST_IF_BRANCH,
};
struct ast_iterator_entry {
fx_queue_entry e_entry;
unsigned long e_depth;
};
struct ast_node {
enum ast_node_type n_type;
struct ast_node *n_parent;
fx_queue_entry n_entry;
struct ast_iterator_entry n_it;
};
struct null_ast_node {
struct ast_node n_base;
};
struct int_ast_node {
struct ast_node n_base;
struct lex_token *n_value;
};
struct double_ast_node {
struct ast_node n_base;
struct lex_token *n_value;
};
struct word_ast_node {
struct ast_node n_base;
struct lex_token *n_value;
};
struct string_ast_node {
struct ast_node n_base;
struct lex_token *n_value;
};
struct fstring_ast_node {
struct ast_node n_base;
fx_queue n_elements;
};
struct var_ast_node {
struct ast_node n_base;
struct lex_token *n_ident;
};
struct var_splat_ast_node {
struct ast_node n_base;
struct lex_token *n_ident;
};
struct cmdcall_ast_node {
struct ast_node n_base;
fx_queue n_args;
fx_queue n_redirect;
};
struct funcall_ast_node {
struct ast_node n_base;
struct ast_node *n_func;
fx_queue n_args;
};
struct pipeline_ast_node {
struct ast_node n_base;
fx_queue n_stages;
};
struct redirection_ast_node {
struct ast_node n_base;
bool n_append : 1;
bool n_out_is_fd : 1;
bool n_out_is_expr : 1;
unsigned int n_in, n_out;
struct ast_node *n_out_path_expr;
const char *n_out_path;
struct lex_token *n_out_tok;
};
struct stmt_list_ast_node {
struct ast_node n_base;
fx_queue n_statements;
};
struct block_ast_node {
struct ast_node n_base;
fx_queue n_statements;
};
struct func_ast_node {
struct ast_node n_base;
struct lex_token *n_name;
fx_queue n_params;
struct ast_node *n_body;
};
struct array_ast_node {
struct ast_node n_base;
fx_queue n_items;
};
struct hashtable_ast_node {
struct ast_node n_base;
fx_queue n_items;
};
struct hashtable_item_ast_node {
struct ast_node n_base;
struct ast_node *n_key, *n_value;
};
struct op_ast_node {
struct ast_node n_base;
const struct operator_info *n_op;
struct ast_node *n_left, *n_right;
};
struct if_branch_ast_node {
struct ast_node n_base;
struct ast_node *n_cond;
struct ast_node *n_body;
};
struct if_ast_node {
struct ast_node n_base;
fx_queue n_branches;
};
struct ast_iterator {
struct ast_node *it_cur;
fx_queue it_queue;
unsigned int it_depth;
fx_queue_entry *it_insert_after;
};
struct ast_iterate_result {
enum bshell_status r_status;
enum {
AST_ITERATE_CONTINUE = 0x00u,
AST_ITERATE_STOP = 0x01u,
AST_ITERATE_ADD_CHILDREN = 0x02u,
AST_ITERATE_REPEAT = 0x04u,
} r_flags;
};
enum ast_iteration_type {
AST_ITERATION_PRE,
AST_ITERATION_POST,
};
typedef struct ast_iterate_result (*ast_iterator_callback)(
struct ast_node *,
enum ast_iteration_type,
struct ast_iterator *,
void *);
struct ast_node_definition {
enum ast_node_type def_id;
size_t def_node_size;
enum bshell_status (*def_collect_children)(
struct ast_node *,
struct ast_iterator *);
enum bshell_status (*def_cleanup)(struct ast_node *);
void (*def_to_string)(const struct ast_node *, fx_bstr *);
};
extern struct ast_node *ast_node_create(enum ast_node_type type);
extern void ast_node_destroy(struct ast_node *node);
extern void ast_node_to_string(const struct ast_node *node, fx_bstr *out);
extern enum bshell_status ast_node_iterate(
struct ast_node *node,
struct ast_iterator *it,
ast_iterator_callback callback,
void *arg);
extern const char *ast_node_type_to_string(enum ast_node_type type);
extern struct ast_node *ast_iterator_peek(struct ast_iterator *it);
extern struct ast_node *ast_iterator_dequeue(struct ast_iterator *it);
extern void ast_iterator_enqueue(
struct ast_iterator *it,
struct ast_node *node);
#endif
+23
View File
@@ -0,0 +1,23 @@
#include "ast.h"
static enum bshell_status collect_children(
struct ast_node *node,
struct ast_iterator *it)
{
struct block_ast_node *block = (struct block_ast_node *)node;
fx_queue_entry *cur = fx_queue_first(&block->n_statements);
while (cur) {
struct ast_node *child
= fx_unbox(struct ast_node, cur, n_entry);
ast_iterator_enqueue(it, child);
cur = fx_queue_next(cur);
}
return BSHELL_SUCCESS;
}
struct ast_node_definition block_ast_node = {
.def_id = AST_BLOCK,
.def_node_size = sizeof(struct block_ast_node),
.def_collect_children = collect_children,
};
+31
View File
@@ -0,0 +1,31 @@
#include "ast.h"
static enum bshell_status collect_children(
struct ast_node *node,
struct ast_iterator *it)
{
struct cmdcall_ast_node *cmdcall = (struct cmdcall_ast_node *)node;
fx_queue_entry *cur = fx_queue_first(&cmdcall->n_args);
while (cur) {
struct ast_node *child
= fx_unbox(struct ast_node, cur, n_entry);
ast_iterator_enqueue(it, child);
cur = fx_queue_next(cur);
}
cur = fx_queue_first(&cmdcall->n_redirect);
while (cur) {
struct ast_node *child
= fx_unbox(struct ast_node, cur, n_entry);
ast_iterator_enqueue(it, child);
cur = fx_queue_next(cur);
}
return BSHELL_SUCCESS;
}
struct ast_node_definition cmdcall_ast_node = {
.def_id = AST_CMDCALL,
.def_node_size = sizeof(struct cmdcall_ast_node),
.def_collect_children = collect_children,
};
+16
View File
@@ -0,0 +1,16 @@
#include "../parse/token.h"
#include "ast.h"
#include <fx/stream.h>
static void to_string(const struct ast_node *node, fx_bstr *out)
{
struct double_ast_node *i = (struct double_ast_node *)node;
fx_value_to_string(&i->n_value->tok_number, (fx_stream *)out, NULL);
}
struct ast_node_definition double_ast_node = {
.def_id = AST_DOUBLE,
.def_node_size = sizeof(struct double_ast_node),
.def_to_string = to_string,
};
+23
View File
@@ -0,0 +1,23 @@
#include "ast.h"
static enum bshell_status collect_children(
struct ast_node *node,
struct ast_iterator *it)
{
struct fstring_ast_node *fstring = (struct fstring_ast_node *)node;
fx_queue_entry *cur = fx_queue_first(&fstring->n_elements);
while (cur) {
struct ast_node *child
= fx_unbox(struct ast_node, cur, n_entry);
ast_iterator_enqueue(it, child);
cur = fx_queue_next(cur);
}
return BSHELL_SUCCESS;
}
struct ast_node_definition fstring_ast_node = {
.def_id = AST_FSTRING,
.def_node_size = sizeof(struct fstring_ast_node),
.def_collect_children = collect_children,
};
+36
View File
@@ -0,0 +1,36 @@
#include "../parse/token.h"
#include "ast.h"
static enum bshell_status collect_children(
struct ast_node *node,
struct ast_iterator *it)
{
struct func_ast_node *func = (struct func_ast_node *)node;
fx_queue_entry *cur = fx_queue_first(&func->n_params);
while (cur) {
struct ast_node *child
= fx_unbox(struct ast_node, cur, n_entry);
ast_iterator_enqueue(it, child);
cur = fx_queue_next(cur);
}
if (func->n_body) {
ast_iterator_enqueue(it, func->n_body);
}
return BSHELL_SUCCESS;
}
static void to_string(const struct ast_node *node, fx_bstr *out)
{
const struct func_ast_node *func = (const struct func_ast_node *)node;
fx_bstr_write_fmt(out, NULL, "%s", func->n_name->tok_str);
}
struct ast_node_definition func_ast_node = {
.def_id = AST_FUNC,
.def_node_size = sizeof(struct func_ast_node),
.def_collect_children = collect_children,
.def_to_string = to_string,
};
+26
View File
@@ -0,0 +1,26 @@
#include "../parse/token.h"
#include "ast.h"
static enum bshell_status collect_children(
struct ast_node *node,
struct ast_iterator *it)
{
struct hashtable_item_ast_node *item
= (struct hashtable_item_ast_node *)node;
if (item->n_key) {
ast_iterator_enqueue(it, item->n_key);
}
if (item->n_value) {
ast_iterator_enqueue(it, item->n_value);
}
return BSHELL_SUCCESS;
}
struct ast_node_definition hashtable_item_ast_node = {
.def_id = AST_HASHTABLE_ITEM,
.def_node_size = sizeof(struct hashtable_item_ast_node),
.def_collect_children = collect_children,
};
+24
View File
@@ -0,0 +1,24 @@
#include "ast.h"
static enum bshell_status collect_children(
struct ast_node *node,
struct ast_iterator *it)
{
struct hashtable_ast_node *hashtable
= (struct hashtable_ast_node *)node;
fx_queue_entry *cur = fx_queue_first(&hashtable->n_items);
while (cur) {
struct ast_node *child
= fx_unbox(struct ast_node, cur, n_entry);
ast_iterator_enqueue(it, child);
cur = fx_queue_next(cur);
}
return BSHELL_SUCCESS;
}
struct ast_node_definition hashtable_ast_node = {
.def_id = AST_HASHTABLE,
.def_node_size = sizeof(struct hashtable_ast_node),
.def_collect_children = collect_children,
};
+24
View File
@@ -0,0 +1,24 @@
#include "ast.h"
static enum bshell_status collect_children(
struct ast_node *node,
struct ast_iterator *it)
{
struct if_branch_ast_node *if_branch
= (struct if_branch_ast_node *)node;
if (if_branch->n_cond) {
ast_iterator_enqueue(it, if_branch->n_cond);
}
if (if_branch->n_body) {
ast_iterator_enqueue(it, if_branch->n_body);
}
return BSHELL_SUCCESS;
}
struct ast_node_definition if_branch_ast_node = {
.def_id = AST_IF_BRANCH,
.def_node_size = sizeof(struct if_branch_ast_node),
.def_collect_children = collect_children,
};
+23
View File
@@ -0,0 +1,23 @@
#include "ast.h"
static enum bshell_status collect_children(
struct ast_node *node,
struct ast_iterator *it)
{
struct if_ast_node *if_group = (struct if_ast_node *)node;
fx_queue_entry *cur = fx_queue_first(&if_group->n_branches);
while (cur) {
struct ast_node *child
= fx_unbox(struct ast_node, cur, n_entry);
ast_iterator_enqueue(it, child);
cur = fx_queue_next(cur);
}
return BSHELL_SUCCESS;
}
struct ast_node_definition if_ast_node = {
.def_id = AST_IF,
.def_node_size = sizeof(struct if_ast_node),
.def_collect_children = collect_children,
};
+16
View File
@@ -0,0 +1,16 @@
#include "../parse/token.h"
#include "ast.h"
#include <fx/stream.h>
static void to_string(const struct ast_node *node, fx_bstr *out)
{
struct int_ast_node *i = (struct int_ast_node *)node;
fx_value_to_string(&i->n_value->tok_number, (fx_stream *)out, NULL);
}
struct ast_node_definition int_ast_node = {
.def_id = AST_INT,
.def_node_size = sizeof(struct int_ast_node),
.def_to_string = to_string,
};
+7
View File
@@ -0,0 +1,7 @@
#include "../parse/token.h"
#include "ast.h"
struct ast_node_definition null_ast_node = {
.def_id = AST_NULL,
.def_node_size = sizeof(struct null_ast_node),
};
+37
View File
@@ -0,0 +1,37 @@
#include "../operator.h"
#include "../parse/token.h"
#include "ast.h"
static enum bshell_status collect_children(
struct ast_node *node,
struct ast_iterator *it)
{
struct op_ast_node *op = (struct op_ast_node *)node;
if (op->n_left) {
ast_iterator_enqueue(it, op->n_left);
}
if (op->n_right) {
ast_iterator_enqueue(it, op->n_right);
}
return BSHELL_SUCCESS;
}
static void to_string(const struct ast_node *node, fx_bstr *out)
{
const struct op_ast_node *op = (const struct op_ast_node *)node;
fx_bstr_write_fmt(
out,
NULL,
"%s",
operator_id_to_string(op->n_op->op_id));
}
struct ast_node_definition op_ast_node = {
.def_id = AST_OP,
.def_node_size = sizeof(struct op_ast_node),
.def_collect_children = collect_children,
.def_to_string = to_string,
};
+23
View File
@@ -0,0 +1,23 @@
#include "ast.h"
static enum bshell_status collect_children(
struct ast_node *node,
struct ast_iterator *it)
{
struct pipeline_ast_node *pipeline = (struct pipeline_ast_node *)node;
fx_queue_entry *cur = fx_queue_first(&pipeline->n_stages);
while (cur) {
struct ast_node *child
= fx_unbox(struct ast_node, cur, n_entry);
ast_iterator_enqueue(it, child);
cur = fx_queue_next(cur);
}
return BSHELL_SUCCESS;
}
struct ast_node_definition pipeline_ast_node = {
.def_id = AST_PIPELINE,
.def_node_size = sizeof(struct pipeline_ast_node),
.def_collect_children = collect_children,
};
+49
View File
@@ -0,0 +1,49 @@
#include "ast.h"
static enum bshell_status collect_children(
struct ast_node *node,
struct ast_iterator *it)
{
struct redirection_ast_node *redirection
= (struct redirection_ast_node *)node;
if (redirection->n_out_path_expr) {
ast_iterator_enqueue(it, redirection->n_out_path_expr);
}
return BSHELL_SUCCESS;
}
static void to_string(const struct ast_node *node, fx_bstr *out)
{
struct redirection_ast_node *redirection
= (struct redirection_ast_node *)node;
fx_bstr_write_fmt(out, NULL, "&%u", redirection->n_in);
if (redirection->n_append) {
fx_bstr_write_fmt(out, NULL, " >>");
} else {
fx_bstr_write_fmt(out, NULL, " >");
}
if (redirection->n_out_is_fd) {
fx_bstr_write_fmt(out, NULL, " &");
} else {
fx_bstr_write_fmt(out, NULL, " ");
}
if (redirection->n_out_is_expr) {
fx_bstr_write_fmt(out, NULL, "<expr>");
} else if (redirection->n_out_path) {
fx_bstr_write_fmt(out, NULL, "'%s'", redirection->n_out_path);
} else {
fx_bstr_write_fmt(out, NULL, "%u", redirection->n_out);
}
}
struct ast_node_definition redirection_ast_node = {
.def_id = AST_REDIRECTION,
.def_node_size = sizeof(struct redirection_ast_node),
.def_collect_children = collect_children,
.def_to_string = to_string,
};
+24
View File
@@ -0,0 +1,24 @@
#include "ast.h"
static enum bshell_status collect_children(
struct ast_node *node,
struct ast_iterator *it)
{
struct stmt_list_ast_node *stmt_list
= (struct stmt_list_ast_node *)node;
fx_queue_entry *cur = fx_queue_first(&stmt_list->n_statements);
while (cur) {
struct ast_node *child
= fx_unbox(struct ast_node, cur, n_entry);
ast_iterator_enqueue(it, child);
cur = fx_queue_next(cur);
}
return BSHELL_SUCCESS;
}
struct ast_node_definition stmt_list_ast_node = {
.def_id = AST_STMT_LIST,
.def_node_size = sizeof(struct stmt_list_ast_node),
.def_collect_children = collect_children,
};
+15
View File
@@ -0,0 +1,15 @@
#include "../parse/token.h"
#include "ast.h"
static void to_string(const struct ast_node *node, fx_bstr *out)
{
const struct string_ast_node *string
= (const struct string_ast_node *)node;
fx_bstr_write_fmt(out, NULL, "%s", string->n_value->tok_str);
}
struct ast_node_definition string_ast_node = {
.def_id = AST_STRING,
.def_node_size = sizeof(struct string_ast_node),
.def_to_string = to_string,
};
+14
View File
@@ -0,0 +1,14 @@
#include "../parse/token.h"
#include "ast.h"
static void to_string(const struct ast_node *node, fx_bstr *out)
{
const struct var_ast_node *var = (const struct var_ast_node *)node;
fx_bstr_write_fmt(out, NULL, "%s", var->n_ident->tok_str);
}
struct ast_node_definition var_ast_node = {
.def_id = AST_VAR,
.def_node_size = sizeof(struct var_ast_node),
.def_to_string = to_string,
};
+14
View File
@@ -0,0 +1,14 @@
#include "../parse/token.h"
#include "ast.h"
static void to_string(const struct ast_node *node, fx_bstr *out)
{
const struct word_ast_node *word = (const struct word_ast_node *)node;
fx_bstr_write_fmt(out, NULL, "%s", word->n_value->tok_str);
}
struct ast_node_definition word_ast_node = {
.def_id = AST_WORD,
.def_node_size = sizeof(struct word_ast_node),
.def_to_string = to_string,
};
+102
View File
@@ -0,0 +1,102 @@
#include "../command/cmdlet.h"
#include "../command/command.h"
#include "../runtime/pipeline.h"
#include "../runtime/runtime.h"
#include "../script-block.h"
#include "../status.h"
#include <fx/reflection/function.h>
#include <fx/string.h>
#include <fx/vector.h>
#define BSHELL_TYPE_FOREACH_OBJECT (bshell_foreach_object_get_type())
fx_type_id bshell_foreach_object_get_type(void);
FX_DECLARE_TYPE(bshell_foreach_object);
FX_TYPE_CLASS_DECLARATION_BEGIN(bshell_foreach_object)
FX_TYPE_CLASS_DECLARATION_END(bshell_foreach_object)
struct bshell_foreach_object_p {
bshell_scriptblock *f_block;
};
static enum bshell_status begin_processing(bshell_cmdlet *cmdlet)
{
struct bshell_foreach_object_p *p = fx_object_get_private(
cmdlet,
BSHELL_TYPE_FOREACH_OBJECT);
const fx_value *block_v = bshell_command_get_arg(cmdlet, 1);
if (!block_v) {
return BSHELL_ERR_INVALID_ARGUMENT;
}
fx_value_get_object(block_v, &p->f_block);
return BSHELL_SUCCESS;
}
static enum bshell_status process_record(
bshell_cmdlet *cmdlet,
bshell_pipeline *pipeline,
struct bshell_runtime *rt)
{
struct bshell_foreach_object_p *p = fx_object_get_private(
cmdlet,
BSHELL_TYPE_FOREACH_OBJECT);
fx_value in = bshell_pipeline_read_value(pipeline);
if (!fx_value_is_set(&in)) {
return BSHELL_SUCCESS;
}
bshell_runtime_push_scope(rt, p->f_block);
bshell_variable *item = bshell_runtime_define_var(rt, "_");
bshell_variable_set_value(item, &in);
fx_value out = bshell_runtime_eval(rt);
bshell_runtime_pop_scope(rt);
fx_value_unset(&in);
bshell_pipeline_write_value(pipeline, out, false);
fx_value_unset(&out);
return BSHELL_SUCCESS;
}
static enum bshell_status end_processing(bshell_cmdlet *cmdlet)
{
struct bshell_foreach_object_p *p = fx_object_get_private(
cmdlet,
BSHELL_TYPE_FOREACH_OBJECT);
return BSHELL_SUCCESS;
}
static void init(fx_object *obj, void *priv)
{
}
FX_TYPE_CLASS_BEGIN(bshell_foreach_object)
FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT)
FX_INTERFACE_ENTRY(to_string) = NULL;
FX_TYPE_VTABLE_INTERFACE_END(fx_object, FX_TYPE_OBJECT)
FX_TYPE_VTABLE_INTERFACE_BEGIN(bshell_cmdlet, BSHELL_TYPE_CMDLET)
FX_INTERFACE_ENTRY(c_verb) = BSHELL_VERB_FOREACH;
FX_INTERFACE_ENTRY(c_noun) = "Object";
FX_TYPE_VTABLE_INTERFACE_END(bshell_cmdlet, BSHELL_TYPE_CMDLET)
FX_TYPE_VTABLE_INTERFACE_BEGIN(bshell_command, BSHELL_TYPE_COMMAND)
FX_INTERFACE_ENTRY(c_begin_processing) = begin_processing;
FX_INTERFACE_ENTRY(c_process_record) = process_record;
FX_INTERFACE_ENTRY(c_end_processing) = end_processing;
FX_TYPE_VTABLE_INTERFACE_END(bshell_command, BSHELL_TYPE_COMMAND)
FX_TYPE_CLASS_END(bshell_foreach_object)
FX_TYPE_DEFINITION_BEGIN(bshell_foreach_object)
FX_TYPE_ID(0x44ae3223, 0x14b3, 0x4c89, 0x9753, 0x6c7901ae00e9);
FX_TYPE_NAME("bshell.core.foreach_object");
FX_TYPE_EXTENDS(BSHELL_TYPE_CMDLET);
FX_TYPE_CLASS(bshell_foreach_object_class);
FX_TYPE_INSTANCE_INIT(init);
FX_TYPE_INSTANCE_PRIVATE(struct bshell_foreach_object_p);
FX_TYPE_DEFINITION_END(bshell_foreach_object)
+9
View File
@@ -0,0 +1,9 @@
#include "../status.h"
#include <fx/object.h>
#include <fx/stream.h>
enum bshell_status do_format_table(fx_object *object, fx_stream *dest)
{
return BSHELL_SUCCESS;
}
+143
View File
@@ -0,0 +1,143 @@
#include "../command/cmdlet.h"
#include "../command/command.h"
#include "../runtime/pipeline.h"
#include "../runtime/runtime.h"
#include "../runtime/scope.h"
#include "../status.h"
#include <fx/reflection/assembly.h>
#include <fx/reflection/function.h>
#include <fx/string.h>
#include <fx/vector.h>
#define BSHELL_TYPE_GET_COMMAND (bshell_get_command_get_type())
fx_type_id bshell_get_command_get_type(void);
FX_DECLARE_TYPE(bshell_get_command);
FX_TYPE_CLASS_DECLARATION_BEGIN(bshell_get_command)
FX_TYPE_CLASS_DECLARATION_END(bshell_get_command)
struct bshell_get_command_p {
bool c_eof;
};
extern const fx_assembly *bshell_assembly_get(void);
static enum bshell_status begin_processing(bshell_cmdlet *cmdlet)
{
struct bshell_get_command_p *p = fx_object_get_private(
cmdlet,
BSHELL_TYPE_GET_COMMAND);
return BSHELL_SUCCESS;
}
static enum bshell_status process_record(
bshell_cmdlet *cmdlet,
bshell_pipeline *pipeline,
struct bshell_runtime *rt)
{
struct bshell_get_command_p *p = fx_object_get_private(
cmdlet,
BSHELL_TYPE_GET_COMMAND);
if (p->c_eof) {
return BSHELL_SUCCESS;
}
fx_iterator *it = fx_iterator_begin(rt->rt_aliases);
fx_foreach(item_v, it)
{
fx_hashtable_item *item;
fx_value_get_object(&item_v, &item);
fx_value alias_v = fx_hashtable_item_get_value(item);
bshell_pipeline_write_value(pipeline, alias_v, false);
}
fx_iterator_unref(it);
fx_queue_entry *cur = fx_queue_last(&rt->rt_scope);
while (cur) {
struct runtime_scope *scope = fx_unbox(
struct runtime_scope,
cur,
s_entry);
it = fx_iterator_begin(scope->s_functions);
fx_foreach(item_v, it)
{
fx_hashtable_item *item;
fx_value_get_object(&item_v, &item);
fx_value func_v = fx_hashtable_item_get_value(item);
bshell_pipeline_write_value(pipeline, func_v, false);
}
fx_iterator_unref(it);
cur = fx_queue_prev(cur);
}
const fx_assembly *self = bshell_assembly_get();
it = fx_assembly_get_types(self);
fx_foreach(v, it)
{
fx_type *ty = NULL;
fx_value_get_object(&v, &ty);
if (fx_type_id_compare(fx_type_get_id(ty), BSHELL_TYPE_CMDLET)
== 0) {
continue;
}
bshell_command_class *cmd = fx_type_get_interface(
ty,
BSHELL_TYPE_COMMAND);
if (!cmd) {
continue;
}
fx_value callable = FX_VALUE_OBJECT(
fx_object_create(fx_type_get_id(ty)));
bshell_pipeline_write_value(pipeline, callable, false);
fx_value_unset(&callable);
}
fx_iterator_unref(it);
p->c_eof = true;
return BSHELL_SUCCESS;
}
static enum bshell_status end_processing(bshell_cmdlet *cmdlet)
{
struct bshell_get_command_p *p = fx_object_get_private(
cmdlet,
BSHELL_TYPE_GET_COMMAND);
return BSHELL_SUCCESS;
}
static void init(fx_object *obj, void *priv)
{
}
FX_TYPE_CLASS_BEGIN(bshell_get_command)
FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT)
FX_INTERFACE_ENTRY(to_string) = NULL;
FX_TYPE_VTABLE_INTERFACE_END(fx_object, FX_TYPE_OBJECT)
FX_TYPE_VTABLE_INTERFACE_BEGIN(bshell_cmdlet, BSHELL_TYPE_CMDLET)
FX_INTERFACE_ENTRY(c_verb) = BSHELL_VERB_GET;
FX_INTERFACE_ENTRY(c_noun) = "Command";
FX_TYPE_VTABLE_INTERFACE_END(bshell_cmdlet, BSHELL_TYPE_CMDLET)
FX_TYPE_VTABLE_INTERFACE_BEGIN(bshell_command, BSHELL_TYPE_COMMAND)
FX_INTERFACE_ENTRY(c_begin_processing) = begin_processing;
FX_INTERFACE_ENTRY(c_process_record) = process_record;
FX_INTERFACE_ENTRY(c_end_processing) = end_processing;
FX_TYPE_VTABLE_INTERFACE_END(bshell_cmdlet, BSHELL_TYPE_CMDLET)
FX_TYPE_CLASS_END(bshell_get_command)
FX_TYPE_DEFINITION_BEGIN(bshell_get_command)
FX_TYPE_ID(0x4f862cc3, 0xf3d3, 0x4f04, 0xb68e, 0x8764079f03c4);
FX_TYPE_NAME("bshell.core.get_command");
FX_TYPE_EXTENDS(BSHELL_TYPE_CMDLET);
FX_TYPE_CLASS(bshell_get_command_class);
FX_TYPE_INSTANCE_INIT(init);
FX_TYPE_INSTANCE_PRIVATE(struct bshell_get_command_p);
FX_TYPE_DEFINITION_END(bshell_get_command)
+114
View File
@@ -0,0 +1,114 @@
#include "../command/cmdlet.h"
#include "../command/command.h"
#include "../runtime/pipeline.h"
#include "../status.h"
#include <fx/reflection/function.h>
#include <fx/string.h>
#include <fx/vector.h>
#define BSHELL_TYPE_GET_VERB (bshell_get_verb_get_type())
fx_type_id bshell_get_verb_get_type(void);
FX_DECLARE_TYPE(bshell_get_verb);
FX_TYPE_CLASS_DECLARATION_BEGIN(bshell_get_verb)
FX_TYPE_CLASS_DECLARATION_END(bshell_get_verb)
struct bshell_get_verb_p {
fx_hashtable *c_verbs;
fx_iterator *c_it;
};
static enum bshell_status begin_processing(bshell_cmdlet *cmdlet)
{
struct bshell_get_verb_p *p = fx_object_get_private(
cmdlet,
BSHELL_TYPE_GET_VERB);
p->c_verbs = bshell_get_all_verbs();
p->c_it = fx_iterator_begin(p->c_verbs);
return BSHELL_SUCCESS;
}
static enum bshell_status process_record(
bshell_cmdlet *cmdlet,
bshell_pipeline *pipeline,
struct bshell_runtime *rt)
{
struct bshell_get_verb_p *p = fx_object_get_private(
cmdlet,
BSHELL_TYPE_GET_VERB);
do {
fx_value v = fx_iterator_get_value(p->c_it);
if (!v.v_type) {
return BSHELL_SUCCESS;
}
fx_hashtable_item *item = NULL;
fx_value_get_object(&v, &item);
v = fx_hashtable_item_get_value(item);
bshell_verb *verb = NULL;
fx_value_get_object(&v, &verb);
fx_iterator_move_next(p->c_it);
if (bshell_verb_is_reserved(verb)) {
continue;
}
bshell_verb_ref(verb);
bshell_pipeline_write_value(
pipeline,
fx_value_copy_return(fx_hashtable_item_get_value(item)),
false);
break;
} while (1);
return BSHELL_SUCCESS;
}
static enum bshell_status end_processing(bshell_cmdlet *cmdlet)
{
struct bshell_get_verb_p *p = fx_object_get_private(
cmdlet,
BSHELL_TYPE_GET_VERB);
if (p->c_it) {
fx_iterator_unref(p->c_it);
p->c_it = NULL;
}
return BSHELL_SUCCESS;
}
static void init(fx_object *obj, void *priv)
{
}
FX_TYPE_CLASS_BEGIN(bshell_get_verb)
FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT)
FX_INTERFACE_ENTRY(to_string) = NULL;
FX_TYPE_VTABLE_INTERFACE_END(fx_object, FX_TYPE_OBJECT)
FX_TYPE_VTABLE_INTERFACE_BEGIN(bshell_cmdlet, BSHELL_TYPE_CMDLET)
FX_INTERFACE_ENTRY(c_verb) = BSHELL_VERB_GET;
FX_INTERFACE_ENTRY(c_noun) = "Verb";
FX_TYPE_VTABLE_INTERFACE_END(bshell_cmdlet, BSHELL_TYPE_CMDLET)
FX_TYPE_VTABLE_INTERFACE_BEGIN(bshell_command, BSHELL_TYPE_COMMAND)
FX_INTERFACE_ENTRY(c_begin_processing) = begin_processing;
FX_INTERFACE_ENTRY(c_process_record) = process_record;
FX_INTERFACE_ENTRY(c_end_processing) = end_processing;
FX_TYPE_VTABLE_INTERFACE_END(bshell_cmdlet, BSHELL_TYPE_CMDLET)
FX_TYPE_CLASS_END(bshell_get_verb)
FX_TYPE_DEFINITION_BEGIN(bshell_get_verb)
FX_TYPE_ID(0xa47976a6, 0xf7d8, 0x447f, 0x887b, 0x6c5d78b7bd2e);
FX_TYPE_NAME("bshell.core.get_verb");
FX_TYPE_EXTENDS(BSHELL_TYPE_CMDLET);
FX_TYPE_CLASS(bshell_get_verb_class);
FX_TYPE_INSTANCE_INIT(init);
FX_TYPE_INSTANCE_PRIVATE(struct bshell_get_verb_p);
FX_TYPE_DEFINITION_END(bshell_get_verb)
+144
View File
@@ -0,0 +1,144 @@
#include "../command/cmdlet.h"
#include "../command/command.h"
#include "../format/format.h"
#include "../runtime/pipeline.h"
#include "../status.h"
#include <fx/collections/array.h>
#include <fx/reflection/function.h>
#include <fx/string.h>
#include <fx/value-type.h>
#include <fx/vector.h>
#define BSHELL_TYPE_WRITE_OUTPUT (bshell_write_output_get_type())
fx_type_id bshell_write_output_get_type(void);
FX_DECLARE_TYPE(bshell_write_output);
FX_TYPE_CLASS_DECLARATION_BEGIN(bshell_write_output)
FX_TYPE_CLASS_DECLARATION_END(bshell_write_output)
struct bshell_write_output_p {
struct table_format_ctx w_table_ctx;
bool w_table_initialised;
};
static void initialise_table(
struct bshell_write_output_p *cmd,
const fx_value *first_record)
{
table_format_ctx_init(&cmd->w_table_ctx, fx_stdout);
const fx_type *ty = fx_type_get_by_id(first_record->v_type);
table_format_ctx_prepare_columns_from_format(
&cmd->w_table_ctx,
ty,
NULL);
table_format_ctx_print_headers(&cmd->w_table_ctx);
cmd->w_table_initialised = true;
}
static bool value_requires_table(const fx_value *value)
{
if (fx_type_is_value_type(value->v_type)) {
return false;
}
if (fx_value_is_type(value, FX_TYPE_STRING)
|| fx_value_is_type(value, FX_TYPE_ARRAY)) {
return false;
}
return true;
}
static void write_value(
struct bshell_write_output_p *cmd,
const fx_value *value)
{
if (!value_requires_table(value)) {
fx_value_to_string(value, fx_stdout, NULL);
fx_stream_write_char(fx_stdout, '\n');
return;
}
if (!cmd->w_table_initialised) {
initialise_table(cmd, value);
}
table_format_ctx_print_record(&cmd->w_table_ctx, value);
}
static enum bshell_status begin_processing(bshell_cmdlet *cmdlet)
{
struct bshell_write_output_p *p = fx_object_get_private(
cmdlet,
BSHELL_TYPE_WRITE_OUTPUT);
for (size_t i = 1;; i++) {
const fx_value *v = bshell_command_get_arg(cmdlet, i);
if (!v) {
break;
}
write_value(p, v);
}
return BSHELL_SUCCESS;
}
static enum bshell_status process_record(
bshell_cmdlet *cmdlet,
bshell_pipeline *pipeline,
struct bshell_runtime *rt)
{
struct bshell_write_output_p *p = fx_object_get_private(
cmdlet,
BSHELL_TYPE_WRITE_OUTPUT);
fx_value in = bshell_pipeline_read_value(pipeline);
if (fx_value_is_set(&in)) {
write_value(p, &in);
fx_value_unset(&in);
}
return BSHELL_SUCCESS;
}
static enum bshell_status end_processing(bshell_cmdlet *cmdlet)
{
struct bshell_write_output_p *p = fx_object_get_private(
cmdlet,
BSHELL_TYPE_WRITE_OUTPUT);
return BSHELL_SUCCESS;
}
static void init(fx_object *obj, void *priv)
{
}
FX_TYPE_CLASS_BEGIN(bshell_write_output)
FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT)
FX_INTERFACE_ENTRY(to_string) = NULL;
FX_TYPE_VTABLE_INTERFACE_END(fx_object, FX_TYPE_OBJECT)
FX_TYPE_VTABLE_INTERFACE_BEGIN(bshell_cmdlet, BSHELL_TYPE_CMDLET)
FX_INTERFACE_ENTRY(c_verb) = BSHELL_VERB_WRITE;
FX_INTERFACE_ENTRY(c_noun) = "Output";
FX_TYPE_VTABLE_INTERFACE_END(bshell_cmdlet, BSHELL_TYPE_CMDLET)
FX_TYPE_VTABLE_INTERFACE_BEGIN(bshell_command, BSHELL_TYPE_COMMAND)
FX_INTERFACE_ENTRY(c_begin_processing) = begin_processing;
FX_INTERFACE_ENTRY(c_process_record) = process_record;
FX_INTERFACE_ENTRY(c_end_processing) = end_processing;
FX_TYPE_VTABLE_INTERFACE_END(bshell_cmdlet, BSHELL_TYPE_CMDLET)
FX_TYPE_CLASS_END(bshell_write_output)
FX_TYPE_DEFINITION_BEGIN(bshell_write_output)
FX_TYPE_ID(0xf7f9c42d, 0xc053, 0x4ffc, 0xa8ec, 0x78fef4844ac0);
FX_TYPE_NAME("bshell.core.write_output");
FX_TYPE_EXTENDS(BSHELL_TYPE_CMDLET);
FX_TYPE_CLASS(bshell_write_output_class);
FX_TYPE_INSTANCE_INIT(init);
FX_TYPE_INSTANCE_PRIVATE(struct bshell_write_output_p);
FX_TYPE_DEFINITION_END(bshell_write_output)
+133
View File
@@ -0,0 +1,133 @@
#include "alias.h"
#include "../status.h"
#include "command.h"
#include <fx/reflection/function.h>
#include <fx/string.h>
#include <fx/vector.h>
struct bshell_alias_p {
};
static void init(fx_object *obj, void *priv)
{
}
enum bshell_status bshell_alias_get_callable_name(
const bshell_alias *alias,
fx_string *out)
{
bshell_alias_class *c = fx_object_get_interface(
alias,
BSHELL_TYPE_ALIAS);
if (!c) {
return BSHELL_ERR_NOT_SUPPORTED;
}
const char *callable_name = c->a_callable_name;
if (c->a_get_callable_name) {
callable_name = c->a_get_callable_name(alias);
}
if (callable_name) {
fx_string_append_cstr(out, callable_name);
}
return BSHELL_SUCCESS;
}
enum bshell_status bshell_alias_get_target_name(
const bshell_alias *alias,
fx_string *out)
{
bshell_alias_class *c = fx_object_get_interface(
alias,
BSHELL_TYPE_ALIAS);
if (!c) {
return BSHELL_ERR_NOT_SUPPORTED;
}
const char *target_name = c->a_target_name;
if (c->a_get_target_name) {
target_name = c->a_get_target_name(alias);
}
if (target_name) {
fx_string_append_cstr(out, target_name);
}
return BSHELL_SUCCESS;
}
static enum bshell_status get_callable_name_static(
const fx_type *ty,
fx_string *out)
{
bshell_alias_class *c = fx_type_get_interface(ty, BSHELL_TYPE_ALIAS);
if (!c) {
return BSHELL_ERR_NOT_SUPPORTED;
}
if (c->a_callable_name) {
fx_string_append_cstr(out, c->a_callable_name);
}
return BSHELL_SUCCESS;
}
static enum bshell_status get_description(
const bshell_command *cmd,
fx_string *out)
{
bshell_alias_class *c = fx_object_get_interface(cmd, BSHELL_TYPE_ALIAS);
if (!c) {
return BSHELL_ERR_NOT_SUPPORTED;
}
const char *callable_name = c->a_callable_name;
if (c->a_get_callable_name) {
callable_name = c->a_get_callable_name(cmd);
}
const char *target_name = c->a_target_name;
if (c->a_get_target_name) {
target_name = c->a_get_target_name(cmd);
}
fx_string_append_cstr(out, callable_name);
fx_string_append_cstr(out, " -> ");
fx_string_append_cstr(out, target_name);
return BSHELL_SUCCESS;
}
enum bshell_status get_description_static(const fx_type *ty, fx_string *out)
{
return BSHELL_SUCCESS;
}
FX_TYPE_CLASS_BEGIN(bshell_alias)
FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT)
FX_INTERFACE_ENTRY(to_string) = NULL;
FX_TYPE_VTABLE_INTERFACE_END(fx_object, FX_TYPE_OBJECT)
FX_TYPE_VTABLE_INTERFACE_BEGIN(bshell_command, BSHELL_TYPE_COMMAND)
FX_INTERFACE_ENTRY(c_command_type) = "Alias";
FX_INTERFACE_ENTRY(c_get_callable_name)
= bshell_alias_get_callable_name;
FX_INTERFACE_ENTRY(c_get_callable_name_static)
= get_callable_name_static;
FX_INTERFACE_ENTRY(c_get_description) = get_description;
FX_INTERFACE_ENTRY(c_get_description_static)
= get_description_static;
FX_TYPE_VTABLE_INTERFACE_END(bshell_command, BSHELL_TYPE_COMMAND)
FX_TYPE_CLASS_END(bshell_alias)
FX_TYPE_DEFINITION_BEGIN(bshell_alias)
FX_TYPE_ID(0x1736c10e, 0xebe6, 0x4ba5, 0x83d9, 0x5da3b7dc706c);
FX_TYPE_NAME("bshell.alias");
FX_TYPE_EXTENDS(BSHELL_TYPE_COMMAND);
FX_TYPE_CLASS(bshell_alias_class);
FX_TYPE_INSTANCE_INIT(init);
FX_TYPE_INSTANCE_PRIVATE(struct bshell_alias_p);
FX_TYPE_DEFINITION_END(bshell_alias)
+35
View File
@@ -0,0 +1,35 @@
#ifndef COMMAND_ALIAS_H_
#define COMMAND_ALIAS_H_
#include "../verb.h"
#include <fx/macros.h>
#include <fx/string.h>
FX_DECLS_BEGIN;
struct bshell_runtime;
#define BSHELL_TYPE_ALIAS (bshell_alias_get_type())
FX_DECLARE_TYPE(bshell_alias);
FX_TYPE_CLASS_DECLARATION_BEGIN(bshell_alias)
const char *a_callable_name;
const char *a_target_name;
const char *(*a_get_callable_name)(const bshell_alias *);
const char *(*a_get_target_name)(const bshell_alias *);
FX_TYPE_CLASS_DECLARATION_END(bshell_alias)
extern fx_type_id bshell_alias_get_type(void);
extern enum bshell_status bshell_alias_get_callable_name(
const bshell_alias *alias,
fx_string *out);
extern enum bshell_status bshell_alias_get_target_name(
const bshell_alias *alias,
fx_string *out);
FX_DECLS_END;
#endif
+60
View File
@@ -0,0 +1,60 @@
#include "cmdlet.h"
#include "../status.h"
#include "command.h"
#include <fx/reflection/function.h>
#include <fx/string.h>
#include <fx/vector.h>
struct bshell_cmdlet_p {
};
static void init(fx_object *obj, void *priv)
{
}
static enum bshell_status get_callable_name_static(
const fx_type *ty,
fx_string *out)
{
bshell_cmdlet_class *class = fx_type_get_interface(
ty,
BSHELL_TYPE_CMDLET);
if (!class) {
return BSHELL_ERR_NOT_SUPPORTED;
}
const bshell_verb *verb_info = bshell_verb_get_by_id(class->c_verb);
if (!verb_info) {
return BSHELL_ERR_BAD_STATE;
}
fx_string_append_cstrf(
out,
"%s-%s",
bshell_verb_get_name(verb_info),
class->c_noun);
return BSHELL_SUCCESS;
}
FX_TYPE_CLASS_BEGIN(bshell_cmdlet)
FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT)
FX_INTERFACE_ENTRY(to_string) = NULL;
FX_TYPE_VTABLE_INTERFACE_END(fx_object, FX_TYPE_OBJECT)
FX_TYPE_VTABLE_INTERFACE_BEGIN(bshell_command, BSHELL_TYPE_COMMAND)
FX_INTERFACE_ENTRY(c_command_type) = "Cmdlet";
FX_INTERFACE_ENTRY(c_get_callable_name_static)
= get_callable_name_static;
FX_TYPE_VTABLE_INTERFACE_END(bshell_command, BSHELL_TYPE_COMMAND)
FX_TYPE_CLASS_END(bshell_cmdlet)
FX_TYPE_DEFINITION_BEGIN(bshell_cmdlet)
FX_TYPE_ID(0xc71f4d59, 0x8066, 0x4294, 0xa6b0, 0xe1f3eb04f454);
FX_TYPE_NAME("bshell.cmdlet");
FX_TYPE_EXTENDS(BSHELL_TYPE_COMMAND);
FX_TYPE_CLASS(bshell_cmdlet_class);
FX_TYPE_INSTANCE_INIT(init);
FX_TYPE_INSTANCE_PRIVATE(struct bshell_cmdlet_p);
FX_TYPE_DEFINITION_END(bshell_cmdlet)
+25
View File
@@ -0,0 +1,25 @@
#ifndef COMMAND_CMDLET_H_
#define COMMAND_CMDLET_H_
#include "../verb.h"
#include <fx/macros.h>
FX_DECLS_BEGIN;
struct bshell_runtime;
#define BSHELL_TYPE_CMDLET (bshell_cmdlet_get_type())
FX_DECLARE_TYPE(bshell_cmdlet);
FX_TYPE_CLASS_DECLARATION_BEGIN(bshell_cmdlet)
enum bshell_verb_id c_verb;
const char *c_noun;
FX_TYPE_CLASS_DECLARATION_END(bshell_cmdlet)
extern fx_type_id bshell_cmdlet_get_type(void);
FX_DECLS_END;
#endif
+272
View File
@@ -0,0 +1,272 @@
#include "command.h"
#include "../runtime/pipeline.h"
#include "../status.h"
#include <fx/reflection/assembly.h>
#include <fx/reflection/function.h>
#include <fx/reflection/type.h>
#include <fx/string.h>
#include <fx/vector.h>
struct bshell_command_p {
fx_value *cmd_args;
size_t cmd_nr_args;
bool cmd_args_reversed;
};
static void init(fx_object *obj, void *priv)
{
}
static void command_set_args(
struct bshell_command_p *cmd,
fx_value *args,
size_t nr_args)
{
cmd->cmd_args = args;
cmd->cmd_nr_args = nr_args;
cmd->cmd_args_reversed = false;
}
static void command_set_args_reverse(
struct bshell_command_p *cmd,
fx_value *args,
size_t nr_args)
{
cmd->cmd_args = args;
cmd->cmd_nr_args = nr_args;
cmd->cmd_args_reversed = true;
}
static const fx_value *command_get_arg(
struct bshell_command_p *cmd,
size_t index)
{
if (index >= cmd->cmd_nr_args) {
return NULL;
}
if (cmd->cmd_args_reversed) {
return &cmd->cmd_args[cmd->cmd_nr_args - index - 1];
}
return &cmd->cmd_args[index];
}
static fx_status get_command_type(
const fx_value *cmd_v,
const fx_property *prop,
fx_value *out)
{
bshell_command *cmd = NULL;
fx_value_get_object(cmd_v, &cmd);
bshell_command_class *cmd_class = fx_object_get_interface(
cmd,
BSHELL_TYPE_COMMAND);
*out = FX_CSTR(cmd_class->c_command_type);
return FX_SUCCESS;
}
static fx_status get_name(
const fx_value *cmd_v,
const fx_property *prop,
fx_value *out)
{
bshell_command *cmd = NULL;
fx_value_get_object(cmd_v, &cmd);
bshell_command_class *cmd_class = fx_object_get_interface(
cmd,
BSHELL_TYPE_COMMAND);
fx_string *result = fx_string_create();
const fx_type *ty = fx_type_get_by_id(fx_object_query_type(cmd));
if (cmd_class->c_get_description) {
cmd_class->c_get_description(cmd, result);
} else if (cmd_class->c_get_description_static) {
cmd_class->c_get_description_static(ty, result);
} else if (cmd_class->c_get_callable_name) {
cmd_class->c_get_callable_name(cmd, result);
} else if (cmd_class->c_get_callable_name_static) {
cmd_class->c_get_callable_name_static(ty, result);
}
*out = FX_VALUE_OBJECT(result);
return FX_SUCCESS;
}
static fx_status get_version(
const fx_value *cmd_v,
const fx_property *prop,
fx_value *out)
{
bshell_command *cmd = NULL;
fx_value_get_object(cmd_v, &cmd);
const fx_type *ty = fx_type_get_by_id(fx_object_query_type(cmd));
const fx_assembly *assembly = fx_type_get_assembly(ty);
long major = 0, minor = 0, build = 0, revision = 0;
fx_assembly_get_version(assembly, &major, &minor, &build, &revision);
fx_string *result = fx_string_create();
if (revision != 0) {
fx_string_append_cstrf(
result,
"%ld.%ld.%ld.%ld",
major,
minor,
build,
revision);
} else {
fx_string_append_cstrf(
result,
"%ld.%ld.%ld",
major,
minor,
build);
}
*out = FX_VALUE_OBJECT(result);
return FX_SUCCESS;
}
static fx_status get_source(
const fx_value *cmd_v,
const fx_property *prop,
fx_value *out)
{
bshell_command *cmd = NULL;
fx_value_get_object(cmd_v, &cmd);
const fx_type *ty = fx_type_get_by_id(fx_object_query_type(cmd));
const fx_assembly *assembly = fx_type_get_assembly(ty);
*out = FX_CSTR(fx_assembly_get_name(assembly));
return FX_SUCCESS;
}
extern const fx_assembly *bshell_assembly_get(void);
bshell_command *bshell_command_find_static(const char *name)
{
fx_string *target_name = fx_string_create();
fx_string *call_name = fx_string_create_from_cstr(name);
fx_string_transform_lowercase(call_name);
const fx_assembly *self = bshell_assembly_get();
fx_iterator *it = fx_assembly_get_types(self);
fx_foreach(v, it)
{
fx_type *ty = NULL;
fx_value_get_object(&v, &ty);
const char *ty_name = fx_type_get_name(ty);
bshell_command_class *cmd = fx_type_get_interface(
ty,
BSHELL_TYPE_COMMAND);
if (!cmd || !cmd->c_get_callable_name_static) {
continue;
}
fx_string_clear(target_name);
cmd->c_get_callable_name_static(ty, target_name);
if (!fx_strcmp_nocase(
fx_string_get_cstr(call_name),
fx_string_get_cstr(target_name))) {
return fx_object_create(fx_type_get_id(ty));
}
}
fx_iterator_unref(it);
return NULL;
}
void bshell_command_set_args(
bshell_command *cmd,
fx_value *args,
size_t nr_args)
{
FX_CLASS_DISPATCH_STATIC_V(
BSHELL_TYPE_COMMAND,
command_set_args,
cmd,
args,
nr_args);
}
void bshell_command_set_args_reverse(
bshell_command *cmd,
fx_value *args,
size_t nr_args)
{
FX_CLASS_DISPATCH_STATIC_V(
BSHELL_TYPE_COMMAND,
command_set_args_reverse,
cmd,
args,
nr_args);
}
const fx_value *bshell_command_get_arg(bshell_command *cmd, size_t index)
{
FX_CLASS_DISPATCH_STATIC(
BSHELL_TYPE_COMMAND,
command_get_arg,
cmd,
index);
}
enum bshell_status bshell_command_begin_processing(bshell_command *command)
{
FX_CLASS_DISPATCH_VIRTUAL_0(
bshell_command,
BSHELL_TYPE_COMMAND,
BSHELL_ERR_NOT_SUPPORTED,
c_begin_processing,
command);
}
enum bshell_status bshell_command_process_record(
bshell_command *command,
bshell_pipeline *pipeline,
struct bshell_runtime *rt)
{
FX_CLASS_DISPATCH_VIRTUAL(
bshell_command,
BSHELL_TYPE_COMMAND,
BSHELL_ERR_NOT_SUPPORTED,
c_process_record,
command,
pipeline,
rt);
}
enum bshell_status bshell_command_end_processing(bshell_command *command)
{
FX_CLASS_DISPATCH_VIRTUAL_0(
bshell_command,
BSHELL_TYPE_COMMAND,
BSHELL_ERR_NOT_SUPPORTED,
c_end_processing,
command);
}
FX_TYPE_CLASS_BEGIN(bshell_command)
FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT)
FX_INTERFACE_ENTRY(to_string) = NULL;
FX_TYPE_VTABLE_INTERFACE_END(fx_object, FX_TYPE_OBJECT)
FX_TYPE_PROPERTY("command_type", get_command_type, NULL);
FX_TYPE_PROPERTY("name", get_name, NULL);
FX_TYPE_PROPERTY("version", get_version, NULL);
FX_TYPE_PROPERTY("source", get_source, NULL);
FX_TYPE_CLASS_END(bshell_command)
FX_TYPE_DEFINITION_BEGIN(bshell_command)
FX_TYPE_ID(0x5c50630d, 0x7535, 0x40ed, 0xbae5, 0x88aabc274d79);
FX_TYPE_NAME("bshell.command");
FX_TYPE_CLASS(bshell_command_class);
FX_TYPE_INSTANCE_INIT(init);
FX_TYPE_INSTANCE_PRIVATE(struct bshell_command_p);
FX_TYPE_DEFINITION_END(bshell_command)
+62
View File
@@ -0,0 +1,62 @@
#ifndef COMMAND_COMMAND_H_
#define COMMAND_COMMAND_H_
#include <fx/macros.h>
#include <fx/reflection/type.h>
#include <fx/string.h>
#include <fx/value.h>
FX_DECLS_BEGIN;
struct bshell_runtime;
#define BSHELL_TYPE_COMMAND (bshell_command_get_type())
FX_DECLARE_TYPE(bshell_command);
FX_TYPE_CLASS_DECLARATION_BEGIN(bshell_command)
const char *c_command_type;
enum bshell_status (
*c_get_callable_name)(const bshell_command *, fx_string *);
enum bshell_status (
*c_get_callable_name_static)(const fx_type *, fx_string *);
enum bshell_status (
*c_get_description)(const bshell_command *, fx_string *);
enum bshell_status (
*c_get_description_static)(const fx_type *, fx_string *);
enum bshell_status (*c_begin_processing)(bshell_command *);
enum bshell_status (*c_process_record)(
bshell_command *,
FX_TYPE_FWDREF(bshell_pipeline) * pipeline,
struct bshell_runtime *);
enum bshell_status (*c_end_processing)(bshell_command *);
FX_TYPE_CLASS_DECLARATION_END(bshell_command)
extern fx_type_id bshell_command_get_type(void);
extern bshell_command *bshell_command_find_static(const char *callable_name);
extern void bshell_command_set_args(
bshell_command *cmd,
fx_value *args,
size_t nr_args);
extern void bshell_command_set_args_reverse(
bshell_command *cmd,
fx_value *args,
size_t nr_args);
extern const fx_value *bshell_command_get_arg(
bshell_command *cmd,
size_t index);
extern enum bshell_status bshell_command_begin_processing(
bshell_command *command);
extern enum bshell_status bshell_command_process_record(
bshell_command *command,
FX_TYPE_FWDREF(bshell_pipeline) * pipeline,
struct bshell_runtime *rt);
extern enum bshell_status bshell_command_end_processing(
bshell_command *command);
FX_DECLS_END;
#endif
View File
+23
View File
@@ -0,0 +1,23 @@
#ifndef COMMAND_FUNCTION_H_
#define COMMAND_FUNCTION_H_
#include <fx/macros.h>
FX_DECLS_BEGIN;
struct bshell_runtime;
#define BSHELL_TYPE_FUNCTION (bshell_function_get_type())
FX_DECLARE_TYPE(bshell_function);
FX_TYPE_CLASS_DECLARATION_BEGIN(bshell_function)
FX_TYPE_CLASS_DECLARATION_END(bshell_function)
extern fx_type_id bshell_function_get_type(void);
extern bshell_function *bshell_function_create(const char *name);
FX_DECLS_END;
#endif
+12
View File
@@ -0,0 +1,12 @@
#ifndef COMPILE_H_
#define COMPILE_H_
#include "script-block.h"
struct ast_node;
extern enum bshell_status bshell_compile(
struct ast_node *src,
bshell_scriptblock *dest);
#endif
+24
View File
@@ -0,0 +1,24 @@
#include "compile-node.h"
static const struct compile_value_type array_type = {};
static const struct compile_value_type array_item_type = {};
struct compile_result compile_array(
struct compile_ctx *ctx,
struct ast_node *src)
{
struct array_ast_node *array = (struct array_ast_node *)src;
size_t nr_items = fx_queue_length(&array->n_items);
for (size_t i = 0; i < nr_items; i++) {
struct compile_value *item = compile_pop_value(ctx);
compile_value_load(ctx, item);
compile_value_destroy(item);
}
bshell_scriptblock_push_instruction(
ctx->c_block,
OPCODE_MK_ARRAY,
nr_items);
compile_push_value(ctx, &array_type, src);
return COMPILE_OK(0);
}
+61
View File
@@ -0,0 +1,61 @@
#include "../compile.h"
#include "compile-node.h"
#include <stdio.h>
static enum bshell_status block_load(
struct compile_ctx *ctx,
struct compile_value *value)
{
bshell_scriptblock_push_instruction(
ctx->c_block,
OPCODE_LDBLOCK,
value->v_pool);
return BSHELL_SUCCESS;
}
static const struct compile_value_type block_type = {
.v_load = block_load,
};
struct compile_result compile_block(
struct compile_ctx *ctx,
struct ast_node *src)
{
bshell_scriptblock *sub_block = bshell_scriptblock_create();
if (!sub_block) {
return COMPILE_ERR(BSHELL_ERR_NO_MEMORY);
}
enum bshell_status status = bshell_compile(src, sub_block);
if (status != BSHELL_SUCCESS) {
bshell_scriptblock_unref(sub_block);
return COMPILE_ERR(status);
}
fx_value sub_block_value = FX_VALUE_OBJECT(sub_block);
unsigned long slot = bshell_scriptblock_add_pool_value(
ctx->c_block,
&sub_block_value);
fx_value_unset(&sub_block_value);
compile_push_pool_value(ctx, &block_type, slot);
return COMPILE_OK(0);
}
enum bshell_status compile_block_immediate(
struct compile_ctx *ctx,
struct ast_node *src)
{
enum bshell_status status = BSHELL_SUCCESS;
struct block_ast_node *block = (struct block_ast_node *)src;
fx_queue_entry *cur = fx_queue_first(&block->n_statements);
while (cur && status == BSHELL_SUCCESS) {
struct ast_node *node = fx_unbox(struct ast_node, cur, n_entry);
status = compile_node(ctx, node);
cur = fx_queue_next(cur);
}
return status;
}
+72
View File
@@ -0,0 +1,72 @@
#include "compile-node.h"
enum bshell_status cmdcall_load(
struct compile_ctx *ctx,
struct compile_value *value)
{
struct cmdcall_ast_node *cmdcall = (struct cmdcall_ast_node *)
value->v_node;
bshell_scriptblock_push_instruction(ctx->c_block, OPCODE_PEXEC, 1);
return BSHELL_SUCCESS;
}
enum bshell_status pipeline_load(
struct compile_ctx *ctx,
struct compile_value *value)
{
struct pipeline_ast_node *pipeline = (struct pipeline_ast_node *)
value->v_node;
size_t nr_items = fx_queue_length(&pipeline->n_stages);
bshell_scriptblock_push_instruction(
ctx->c_block,
OPCODE_PEXEC,
nr_items);
return BSHELL_SUCCESS;
}
static const struct compile_value_type cmdcall_type = {
.v_load = cmdcall_load,
};
static const struct compile_value_type pipeline_type = {
.v_load = pipeline_load,
};
struct compile_result compile_cmdcall(
struct compile_ctx *ctx,
struct ast_node *src)
{
struct cmdcall_ast_node *cmdcall = (struct cmdcall_ast_node *)src;
size_t nr_args = fx_queue_length(&cmdcall->n_args);
for (size_t i = 0; i < nr_args; i++) {
struct compile_value *arg = compile_pop_value(ctx);
compile_value_load(ctx, arg);
compile_value_destroy(arg);
}
bshell_scriptblock_push_instruction(
ctx->c_block,
OPCODE_LDCMD,
nr_args);
compile_push_value(ctx, &cmdcall_type, src);
return COMPILE_OK(0);
}
struct compile_result compile_pipeline(
struct compile_ctx *ctx,
struct ast_node *src)
{
struct pipeline_ast_node *pipeline = (struct pipeline_ast_node *)src;
size_t nr_items = fx_queue_length(&pipeline->n_stages);
for (size_t i = 0; i < nr_items; i++) {
struct compile_value *item = compile_pop_value(ctx);
if (item->v_node->n_type != AST_CMDCALL) {
compile_value_load(ctx, item);
}
compile_value_destroy(item);
}
compile_push_value(ctx, &pipeline_type, src);
return COMPILE_OK(0);
}
+187
View File
@@ -0,0 +1,187 @@
#ifndef COMPILE_COMPILE_NODE_H_
#define COMPILE_COMPILE_NODE_H_
#include "../ast/ast.h"
#include "../operator.h"
#include "../parse/token.h"
#include "../script-block.h"
#include <fx/queue.h>
#include <fx/vector.h>
#define COMPILE_OK(flags) \
((struct compile_result) {.r_status = BSHELL_SUCCESS, \
.r_flags = (flags)})
#define COMPILE_ERR(status) ((struct compile_result) {.r_status = (status)})
struct compile_ctx;
struct compile_value;
struct compile_result {
enum bshell_status r_status;
enum {
COMPILE_REPEAT_NODE = 0x00u,
} r_flags;
};
typedef struct compile_result (*compile_begin_impl)(struct compile_ctx *);
typedef struct compile_result (*compile_end_impl)(struct compile_ctx *);
typedef struct compile_result (
*compile_node_impl)(struct compile_ctx *, struct ast_node *);
enum compile_state_type_id {
COMPILE_NORMAL = 0,
COMPILE_FSTRING,
};
struct compile_value_type {
enum bshell_status (
*v_load)(struct compile_ctx *, struct compile_value *);
enum bshell_status (
*v_store)(struct compile_ctx *, struct compile_value *);
};
struct compile_value {
const struct compile_value_type *v_type;
enum operator_id v_op;
struct compile_value *v_left, *v_right;
struct compile_value **v_args;
size_t v_nr_args;
struct ast_node *v_node;
unsigned long v_pool;
fx_queue_entry v_entry;
};
struct compile_label {
size_t l_ip;
};
struct compile_label_ref {
fx_queue_entry ref_entry;
size_t ref_offset;
size_t ref_label_id;
};
struct compile_ctx {
fx_queue c_state, c_stack;
bshell_scriptblock *c_block;
FX_VECTOR_DECLARE(struct compile_label, c_labels);
fx_queue c_label_refs;
size_t c_depth;
};
struct compile_state_type {
size_t s_size;
compile_begin_impl s_compile_begin;
compile_end_impl s_compile_end;
const compile_node_impl *s_compile_node;
compile_node_impl s_compile_node_generic;
size_t s_nr_compile_node;
};
struct compile_state {
const struct compile_state_type *s_type;
fx_queue_entry s_entry;
size_t s_depth;
};
extern struct compile_state *compile_push_state(
struct compile_ctx *ctx,
enum compile_state_type_id state_type);
extern void compile_pop_state(struct compile_ctx *ctx);
extern struct compile_state *compile_get_state(const struct compile_ctx *ctx);
extern enum bshell_status compile_push_value(
struct compile_ctx *ctx,
const struct compile_value_type *type,
struct ast_node *node);
extern enum bshell_status compile_push_value_array(
struct compile_ctx *ctx,
const struct compile_value_type *type,
struct ast_node *node,
struct compile_value **values,
size_t nr_values);
extern enum bshell_status compile_push_pool_value(
struct compile_ctx *ctx,
const struct compile_value_type *type,
unsigned long pool_slot);
extern enum bshell_status compile_push_op(
struct compile_ctx *ctx,
struct ast_node *node,
const struct compile_value_type *type,
enum operator_id op,
struct compile_value *left,
struct compile_value *right);
extern struct compile_value *compile_pop_value(struct compile_ctx *ctx);
extern void compile_value_load(
struct compile_ctx *ctx,
struct compile_value *item);
extern void compile_value_store(
struct compile_ctx *ctx,
struct compile_value *item);
extern void compile_value_destroy(struct compile_value *item);
extern size_t compile_declare_label(struct compile_ctx *ctx);
extern void compile_define_label(struct compile_ctx *ctx, size_t label_id);
extern void compile_ref_label(struct compile_ctx *ctx, size_t label_id);
extern enum bshell_status compile_resolve_labels(struct compile_ctx *ctx);
extern struct compile_result compile_int(
struct compile_ctx *ctx,
struct ast_node *src);
extern struct compile_result compile_double(
struct compile_ctx *ctx,
struct ast_node *src);
extern struct compile_result compile_string(
struct compile_ctx *ctx,
struct ast_node *src);
extern struct compile_result compile_word(
struct compile_ctx *ctx,
struct ast_node *src);
extern struct compile_result compile_op(
struct compile_ctx *ctx,
struct ast_node *src);
extern struct compile_result compile_var(
struct compile_ctx *ctx,
struct ast_node *src);
extern struct compile_result compile_fstring(
struct compile_ctx *ctx,
struct ast_node *src);
extern struct compile_result compile_array(
struct compile_ctx *ctx,
struct ast_node *src);
extern struct compile_result compile_hashtable_item(
struct compile_ctx *ctx,
struct ast_node *src);
extern struct compile_result compile_hashtable(
struct compile_ctx *ctx,
struct ast_node *src);
extern struct compile_result compile_block(
struct compile_ctx *ctx,
struct ast_node *src);
extern struct compile_result compile_cmdcall(
struct compile_ctx *ctx,
struct ast_node *src);
extern struct compile_result compile_pipeline(
struct compile_ctx *ctx,
struct ast_node *src);
extern enum bshell_status compile_if(
struct compile_ctx *ctx,
struct ast_node *src);
extern enum bshell_status compile_block_immediate(
struct compile_ctx *ctx,
struct ast_node *src);
extern enum bshell_status compile_expression(
struct compile_ctx *ctx,
struct ast_node *src);
extern enum bshell_status compile_node(
struct compile_ctx *ctx,
struct ast_node *src);
#endif
+43
View File
@@ -0,0 +1,43 @@
#include "../compile.h"
#include "../ast/ast.h"
#include "../debug.h"
#include "../status.h"
#include "compile-node.h"
#include <stdio.h>
extern enum bshell_status compile_node(
struct compile_ctx *ctx,
struct ast_node *src)
{
switch (src->n_type) {
case AST_IF:
return compile_if(ctx, src);
case AST_BLOCK:
return compile_block_immediate(ctx, src);
default:
return compile_expression(ctx, src);
}
return BSHELL_SUCCESS;
}
enum bshell_status bshell_compile(
struct ast_node *src,
bshell_scriptblock *dest)
{
struct compile_ctx ctx = {.c_block = dest};
enum bshell_status status = compile_node(&ctx, src);
if (status != BSHELL_SUCCESS) {
return status;
}
status = compile_resolve_labels(&ctx);
if (status != BSHELL_SUCCESS) {
return status;
}
return BSHELL_SUCCESS;
}
+108
View File
@@ -0,0 +1,108 @@
#include "compile-node.h"
enum bshell_status int_load(
struct compile_ctx *ctx,
struct compile_value *value)
{
struct int_ast_node *i = (struct int_ast_node *)value->v_node;
long long v;
fx_value_get_longlong(&i->n_value->tok_number, &v);
bshell_scriptblock_push_instruction(ctx->c_block, OPCODE_LDC_INT, v);
return BSHELL_SUCCESS;
}
enum bshell_status double_load(
struct compile_ctx *ctx,
struct compile_value *value)
{
struct double_ast_node *d = (struct double_ast_node *)value->v_node;
double v;
fx_value_get_double(&d->n_value->tok_number, &v);
unsigned long index = bshell_scriptblock_get_double(ctx->c_block, v);
bshell_scriptblock_push_instruction(ctx->c_block, OPCODE_LDC_FP, index);
return BSHELL_SUCCESS;
}
enum bshell_status string_load(
struct compile_ctx *ctx,
struct compile_value *value)
{
struct string_ast_node *s = (struct string_ast_node *)value->v_node;
unsigned long index = bshell_scriptblock_get_string(
ctx->c_block,
s->n_value->tok_str);
if (index == POOL_INDEX_INVALID) {
return BSHELL_ERR_NO_MEMORY;
}
bshell_scriptblock_push_instruction(
ctx->c_block,
OPCODE_LDC_STR,
index);
return BSHELL_SUCCESS;
}
enum bshell_status word_load(
struct compile_ctx *ctx,
struct compile_value *value)
{
struct word_ast_node *s = (struct word_ast_node *)value->v_node;
unsigned long index = bshell_scriptblock_get_string(
ctx->c_block,
s->n_value->tok_str);
if (index == POOL_INDEX_INVALID) {
return BSHELL_ERR_NO_MEMORY;
}
bshell_scriptblock_push_instruction(
ctx->c_block,
OPCODE_LDC_STR,
index);
return BSHELL_SUCCESS;
}
static const struct compile_value_type int_type = {
.v_load = int_load,
};
static const struct compile_value_type double_type = {
.v_load = double_load,
};
static const struct compile_value_type string_type = {
.v_load = string_load,
};
static const struct compile_value_type word_type = {
.v_load = word_load,
};
struct compile_result compile_int(struct compile_ctx *ctx, struct ast_node *src)
{
compile_push_value(ctx, &int_type, src);
return COMPILE_OK(0);
}
struct compile_result compile_double(
struct compile_ctx *ctx,
struct ast_node *src)
{
compile_push_value(ctx, &double_type, src);
return COMPILE_OK(0);
}
struct compile_result compile_string(
struct compile_ctx *ctx,
struct ast_node *src)
{
compile_push_value(ctx, &string_type, src);
return COMPILE_OK(0);
}
struct compile_result compile_word(
struct compile_ctx *ctx,
struct ast_node *src)
{
compile_push_value(ctx, &word_type, src);
return COMPILE_OK(0);
}
+72
View File
@@ -0,0 +1,72 @@
#include "compile-node.h"
static struct ast_iterate_result do_compile_node(
struct ast_node *node,
enum ast_iteration_type it_type,
struct ast_iterator *it,
struct compile_ctx *ctx)
{
if (it_type != AST_ITERATION_POST) {
int flags = 0;
switch (node->n_type) {
case AST_BLOCK:
break;
default:
flags |= AST_ITERATE_ADD_CHILDREN;
break;
}
return AST_ITERATE_OK(flags);
}
ctx->c_depth = node->n_it.e_depth;
struct compile_state *state = compile_get_state(ctx);
while (state->s_depth >= ctx->c_depth && state->s_depth > 0) {
compile_pop_state(ctx);
state = compile_get_state(ctx);
}
compile_node_impl impl = NULL;
if (node->n_type < state->s_type->s_nr_compile_node) {
impl = state->s_type->s_compile_node[node->n_type];
}
if (!impl) {
impl = state->s_type->s_compile_node_generic;
}
if (!impl) {
return AST_ITERATE_OK(0);
}
struct compile_result result = impl(ctx, node);
unsigned int flags = 0;
if (result.r_flags & COMPILE_REPEAT_NODE) {
flags |= AST_ITERATE_REPEAT;
}
if (result.r_status == BSHELL_SUCCESS) {
return AST_ITERATE_OK(flags);
}
return AST_ITERATE_ERR(result.r_status);
}
enum bshell_status compile_expression(
struct compile_ctx *ctx,
struct ast_node *src)
{
compile_push_state(ctx, COMPILE_NORMAL);
struct ast_iterator it = {0};
ast_node_iterate(src, &it, (ast_iterator_callback)do_compile_node, ctx);
struct compile_value *result = compile_pop_value(ctx);
if (result) {
compile_value_load(ctx, result);
compile_value_destroy(result);
}
return BSHELL_SUCCESS;
}
+39
View File
@@ -0,0 +1,39 @@
#include "compile-node.h"
enum bshell_status fstring_load(
struct compile_ctx *ctx,
struct compile_value *value)
{
struct fstring_ast_node *fstring = (struct fstring_ast_node *)
value->v_node;
for (size_t i = 0; i < value->v_nr_args; i++) {
compile_value_load(ctx, value->v_args[i]);
}
bshell_scriptblock_push_instruction(
ctx->c_block,
OPCODE_MK_STR,
fx_queue_length(&fstring->n_elements));
return BSHELL_SUCCESS;
}
static const struct compile_value_type fstring_type = {
.v_load = fstring_load,
};
struct compile_result compile_fstring(
struct compile_ctx *ctx,
struct ast_node *src)
{
struct fstring_ast_node *fstring = (struct fstring_ast_node *)src;
size_t nr_items = fx_queue_length(&fstring->n_elements);
struct compile_value **items = calloc(nr_items, sizeof *items);
for (size_t i = 0; i < nr_items; i++) {
struct compile_value *item = compile_pop_value(ctx);
items[i] = item;
}
compile_push_value_array(ctx, &fstring_type, src, items, nr_items);
return COMPILE_OK(0);
}
+39
View File
@@ -0,0 +1,39 @@
#include "compile-node.h"
static const struct compile_value_type hashtable_type = {};
static const struct compile_value_type hashtable_item_type = {};
struct compile_result compile_hashtable(
struct compile_ctx *ctx,
struct ast_node *src)
{
struct hashtable_ast_node *hashtable = (struct hashtable_ast_node *)src;
size_t nr_items = fx_queue_length(&hashtable->n_items);
for (size_t i = 0; i < nr_items; i++) {
struct compile_value *item = compile_pop_value(ctx);
compile_value_load(ctx, item);
compile_value_destroy(item);
}
bshell_scriptblock_push_instruction(
ctx->c_block,
OPCODE_MK_HTAB,
nr_items);
compile_push_value(ctx, &hashtable_type, src);
return COMPILE_OK(0);
}
struct compile_result compile_hashtable_item(
struct compile_ctx *ctx,
struct ast_node *src)
{
struct compile_value *value = compile_pop_value(ctx);
struct compile_value *key = compile_pop_value(ctx);
compile_value_load(ctx, key);
compile_value_load(ctx, value);
compile_value_destroy(key);
compile_value_destroy(value);
compile_push_value(ctx, &hashtable_item_type, src);
return COMPILE_OK(0);
}
+75
View File
@@ -0,0 +1,75 @@
#include "compile-node.h"
enum bshell_status compile_if(struct compile_ctx *ctx, struct ast_node *src)
{
struct if_ast_node *if_group = (struct if_ast_node *)src;
size_t nr_labels = fx_queue_length(&if_group->n_branches);
// add one extra label for the end of the if group
nr_labels++;
size_t *label_ids = calloc(nr_labels, sizeof *label_ids);
if (!label_ids) {
return BSHELL_ERR_NO_MEMORY;
}
size_t i;
for (i = 0; i < nr_labels; i++) {
label_ids[i] = compile_declare_label(ctx);
}
i = 0;
fx_queue_entry *cur = fx_queue_first(&if_group->n_branches);
while (cur) {
struct if_branch_ast_node *branch = fx_unbox(
struct if_branch_ast_node,
cur,
n_base.n_entry);
if (!branch->n_cond) {
compile_block_immediate(ctx, branch->n_body);
compile_ref_label(ctx, label_ids[nr_labels - 1]);
bshell_scriptblock_push_instruction(
ctx->c_block,
OPCODE_BR,
0);
break;
}
compile_expression(ctx, branch->n_cond);
compile_ref_label(ctx, label_ids[i++]);
bshell_scriptblock_push_instruction(
ctx->c_block,
OPCODE_BR_TRUE,
0);
cur = fx_queue_next(cur);
}
i = 0;
cur = fx_queue_first(&if_group->n_branches);
while (cur) {
struct if_branch_ast_node *branch = fx_unbox(
struct if_branch_ast_node,
cur,
n_base.n_entry);
if (!branch->n_cond) {
break;
}
compile_define_label(ctx, label_ids[i++]);
compile_block_immediate(ctx, branch->n_body);
cur = fx_queue_next(cur);
if (i < nr_labels - 2) {
compile_ref_label(ctx, label_ids[nr_labels - 1]);
bshell_scriptblock_push_instruction(
ctx->c_block,
OPCODE_BR,
0);
}
}
compile_define_label(ctx, label_ids[nr_labels - 1]);
free(label_ids);
return BSHELL_SUCCESS;
}
+67
View File
@@ -0,0 +1,67 @@
#include "compile-node.h"
size_t compile_declare_label(struct compile_ctx *ctx)
{
size_t id = ctx->c_labels.count;
struct compile_label *label = fx_vector_emplace_back(
ctx->c_labels,
NULL);
return id;
}
void compile_define_label(struct compile_ctx *ctx, size_t label_id)
{
size_t offset = 0;
bshell_scriptblock_get_text(ctx->c_block, NULL, &offset);
offset *= sizeof(bshell_instruction);
ctx->c_labels.items[label_id].l_ip = offset;
}
void compile_ref_label(struct compile_ctx *ctx, size_t label_id)
{
struct compile_label_ref *ref = malloc(sizeof *ref);
if (!ref) {
return;
}
memset(ref, 0x0, sizeof *ref);
ref->ref_label_id = label_id;
bshell_scriptblock_get_text(ctx->c_block, NULL, &ref->ref_offset);
ref->ref_offset *= sizeof(bshell_instruction);
fx_queue_push_back(&ctx->c_label_refs, &ref->ref_entry);
}
enum bshell_status compile_resolve_labels(struct compile_ctx *ctx)
{
fx_queue_entry *cur = fx_queue_pop_front(&ctx->c_label_refs);
while (cur) {
struct compile_label_ref *ref = fx_unbox(
struct compile_label_ref,
cur,
ref_entry);
struct compile_label *label = &ctx->c_labels.items
[ref->ref_label_id];
long long rel_value = (long long)label->l_ip
- (long long)ref->ref_offset;
enum bshell_opcode op;
uint32_t arg;
bshell_scriptblock_read_instruction(
ctx->c_block,
ref->ref_offset,
&op,
&arg);
arg = (uint32_t)rel_value;
bshell_scriptblock_patch_instruction(
ctx->c_block,
ref->ref_offset,
op,
arg);
free(ref);
cur = fx_queue_pop_front(&ctx->c_label_refs);
}
return BSHELL_SUCCESS;
}
+25
View File
@@ -0,0 +1,25 @@
#include "compile-node.h"
static const compile_node_impl node_compilers[] = {
[AST_INT] = compile_int,
[AST_DOUBLE] = compile_double,
[AST_STRING] = compile_string,
[AST_WORD] = compile_word,
[AST_OP] = compile_op,
[AST_VAR] = compile_var,
[AST_FSTRING] = compile_fstring,
[AST_ARRAY] = compile_array,
[AST_HASHTABLE] = compile_hashtable,
[AST_HASHTABLE_ITEM] = compile_hashtable_item,
[AST_CMDCALL] = compile_cmdcall,
[AST_PIPELINE] = compile_pipeline,
[AST_BLOCK] = compile_block,
};
static const size_t nr_node_compilers = sizeof node_compilers
/ sizeof node_compilers[0];
const struct compile_state_type normal_state = {
.s_size = sizeof(struct compile_state),
.s_compile_node = node_compilers,
.s_nr_compile_node = nr_node_compilers,
};
+123
View File
@@ -0,0 +1,123 @@
#include "../operator.h"
#include "compile-node.h"
static enum bshell_opcode opcode_for_op(enum operator_id op)
{
switch (op) {
case OP_ADD:
return OPCODE_ADD;
case OP_SUBTRACT:
return OPCODE_SUB;
case OP_MULTIPLY:
return OPCODE_MUL;
case OP_DIVIDE:
return OPCODE_DIV;
case OP_MODULO:
return OPCODE_MOD;
case OP_INCREMENT:
return OPCODE_INC;
case OP_DECREMENT:
return OPCODE_DEC;
case OP_LEFT_SHIFT:
return OPCODE_SHL;
case OP_RIGHT_SHIFT:
return OPCODE_SHR;
case OP_BINARY_AND:
return OPCODE_BAND;
case OP_BINARY_OR:
return OPCODE_BOR;
case OP_BINARY_XOR:
return OPCODE_BXOR;
case OP_BINARY_NOT:
return OPCODE_BNOT;
case OP_LESS_THAN:
return OPCODE_CMP_LT;
case OP_GREATER_THAN:
return OPCODE_CMP_GT;
case OP_EQUAL:
return OPCODE_CMP_EQ;
case OP_NOT_EQUAL:
return OPCODE_CMP_NE;
case OP_LESS_EQUAL:
return OPCODE_CMP_LE;
case OP_GREATER_EQUAL:
return OPCODE_CMP_GE;
case OP_LOGICAL_AND:
return OPCODE_LAND;
case OP_LOGICAL_OR:
return OPCODE_LOR;
case OP_LOGICAL_XOR:
return OPCODE_LXOR;
case OP_LOGICAL_NOT:
return OPCODE_LNOT;
case OP_RANGE:
return OPCODE_RANGE;
case OP_MATCH:
return OPCODE_MATCH;
case OP_REPLACE:
return OPCODE_REPLACE;
case OP_LIKE:
return OPCODE_LIKE;
case OP_IN:
return OPCODE_IN;
case OP_FORMAT:
return OPCODE_FMT;
case OP_CONTAINS:
return OPCODE_CONTAINS;
case OP_USPLIT:
return OPCODE_SPLIT;
case OP_BSPLIT:
return OPCODE_SPLIT;
case OP_UJOIN:
return OPCODE_JOIN;
case OP_BJOIN:
return OPCODE_JOIN;
case OP_IS:
return OPCODE_IS;
case OP_AS:
return OPCODE_AS;
case OP_ACCESS:
return OPCODE_LDPROP;
default:
return OPCODE_NOP;
}
}
enum bshell_status op_load(struct compile_ctx *ctx, struct compile_value *value)
{
enum bshell_opcode opcode = opcode_for_op(value->v_op);
if (opcode != OPCODE_NOP) {
compile_value_load(ctx, value->v_right);
compile_value_load(ctx, value->v_left);
bshell_scriptblock_push_instruction(
ctx->c_block,
opcode_for_op(value->v_op),
0);
return BSHELL_SUCCESS;
}
switch (value->v_op) {
case OP_ASSIGN:
compile_value_load(ctx, value->v_right);
compile_value_store(ctx, value->v_left);
break;
default:
return BSHELL_ERR_NOT_SUPPORTED;
}
return BSHELL_SUCCESS;
}
static const struct compile_value_type op_type = {
.v_load = op_load,
};
struct compile_result compile_op(struct compile_ctx *ctx, struct ast_node *src)
{
struct op_ast_node *op = (struct op_ast_node *)src;
struct compile_value *left = NULL, *right = NULL;
left = compile_pop_value(ctx);
right = compile_pop_value(ctx);
compile_push_op(ctx, src, &op_type, op->n_op->op_id, left, right);
return COMPILE_OK(0);
}
+132
View File
@@ -0,0 +1,132 @@
#include "compile-node.h"
enum bshell_status compile_push_value(
struct compile_ctx *ctx,
const struct compile_value_type *type,
struct ast_node *node)
{
struct compile_value *item = malloc(sizeof *item);
if (!item) {
return BSHELL_ERR_NO_MEMORY;
}
memset(item, 0x0, sizeof *item);
item->v_type = type;
item->v_node = node;
fx_queue_push_back(&ctx->c_stack, &item->v_entry);
return BSHELL_SUCCESS;
}
enum bshell_status compile_push_value_array(
struct compile_ctx *ctx,
const struct compile_value_type *type,
struct ast_node *node,
struct compile_value **values,
size_t nr_values)
{
struct compile_value *item = malloc(sizeof *item);
if (!item) {
return BSHELL_ERR_NO_MEMORY;
}
memset(item, 0x0, sizeof *item);
item->v_type = type;
item->v_node = node;
item->v_args = values;
item->v_nr_args = nr_values;
fx_queue_push_back(&ctx->c_stack, &item->v_entry);
return BSHELL_SUCCESS;
}
enum bshell_status compile_push_pool_value(
struct compile_ctx *ctx,
const struct compile_value_type *type,
unsigned long pool_slot)
{
struct compile_value *item = malloc(sizeof *item);
if (!item) {
return BSHELL_ERR_NO_MEMORY;
}
memset(item, 0x0, sizeof *item);
item->v_type = type;
item->v_pool = pool_slot;
fx_queue_push_back(&ctx->c_stack, &item->v_entry);
return BSHELL_SUCCESS;
}
enum bshell_status compile_push_op(
struct compile_ctx *ctx,
struct ast_node *node,
const struct compile_value_type *type,
enum operator_id op,
struct compile_value *left,
struct compile_value *right)
{
struct compile_value *item = malloc(sizeof *item);
if (!item) {
return BSHELL_ERR_NO_MEMORY;
}
memset(item, 0x0, sizeof *item);
item->v_type = type;
item->v_node = node;
item->v_op = op;
item->v_left = left;
item->v_right = right;
fx_queue_push_back(&ctx->c_stack, &item->v_entry);
return BSHELL_SUCCESS;
}
struct compile_value *compile_pop_value(struct compile_ctx *ctx)
{
fx_queue_entry *entry = fx_queue_pop_back(&ctx->c_stack);
if (!entry) {
return NULL;
}
return fx_unbox(struct compile_value, entry, v_entry);
}
void compile_value_load(struct compile_ctx *ctx, struct compile_value *item)
{
if (item->v_type->v_load) {
item->v_type->v_load(ctx, item);
}
}
void compile_value_store(struct compile_ctx *ctx, struct compile_value *item)
{
if (item->v_type->v_store) {
item->v_type->v_store(ctx, item);
}
}
void compile_value_destroy(struct compile_value *value)
{
if (value->v_left) {
compile_value_destroy(value->v_left);
}
if (value->v_right) {
compile_value_destroy(value->v_right);
}
for (size_t i = 0; i < value->v_nr_args; i++) {
compile_value_destroy(value->v_args[i]);
}
if (value->v_args) {
free(value->v_args);
}
free(value);
}
+66
View File
@@ -0,0 +1,66 @@
#include "compile-node.h"
extern const struct compile_state_type normal_state;
static const struct compile_state_type *state_types[] = {
[COMPILE_NORMAL] = &normal_state,
};
static const size_t nr_state_types = sizeof state_types / sizeof state_types[0];
struct compile_state *compile_push_state(
struct compile_ctx *ctx,
enum compile_state_type_id state_type_id)
{
if (state_type_id >= nr_state_types) {
return NULL;
}
const struct compile_state_type *state_type = state_types
[state_type_id];
if (!state_type) {
return NULL;
}
struct compile_state *state = malloc(state_type->s_size);
if (!state) {
return NULL;
}
state->s_type = state_type;
state->s_depth = ctx->c_depth;
fx_queue_push_back(&ctx->c_state, &state->s_entry);
if (state_type->s_compile_begin) {
state_type->s_compile_begin(ctx);
}
return state;
}
void compile_pop_state(struct compile_ctx *ctx)
{
struct compile_state *state = compile_get_state(ctx);
if (!state) {
return;
}
if (!fx_queue_prev(&state->s_entry)) {
return;
}
if (state->s_type->s_compile_end) {
state->s_type->s_compile_end(ctx);
}
fx_queue_pop_back(&ctx->c_state);
free(state);
}
struct compile_state *compile_get_state(const struct compile_ctx *ctx)
{
fx_queue_entry *entry = fx_queue_last(&ctx->c_state);
if (!entry) {
return NULL;
}
return fx_unbox(struct compile_state, entry, s_entry);
}
+50
View File
@@ -0,0 +1,50 @@
#include "compile-node.h"
enum bshell_status var_load(
struct compile_ctx *ctx,
struct compile_value *value)
{
struct var_ast_node *var = (struct var_ast_node *)value->v_node;
unsigned long index = bshell_scriptblock_get_string(
ctx->c_block,
var->n_ident->tok_str);
if (index == POOL_INDEX_INVALID) {
return BSHELL_ERR_NO_MEMORY;
}
bshell_scriptblock_push_instruction(
ctx->c_block,
OPCODE_LDLOCAL,
index);
return BSHELL_SUCCESS;
}
enum bshell_status var_store(
struct compile_ctx *ctx,
struct compile_value *value)
{
struct var_ast_node *var = (struct var_ast_node *)value->v_node;
unsigned long index = bshell_scriptblock_get_string(
ctx->c_block,
var->n_ident->tok_str);
if (index == POOL_INDEX_INVALID) {
return BSHELL_ERR_NO_MEMORY;
}
bshell_scriptblock_push_instruction(
ctx->c_block,
OPCODE_STLOCAL,
index);
return BSHELL_SUCCESS;
}
static const struct compile_value_type var_type = {
.v_load = var_load,
.v_store = var_store,
};
struct compile_result compile_var(struct compile_ctx *ctx, struct ast_node *src)
{
compile_push_value(ctx, &var_type, src);
return COMPILE_OK(0);
}
+464
View File
@@ -0,0 +1,464 @@
#include "debug.h"
#include "ast/ast.h"
#include "parse/token.h"
#include "runtime/opcode.h"
#include "script-block.h"
#include <fx/string.h>
#include <fx/term/print.h>
#include <stdio.h>
extern void print_lex_token(struct lex_token *tok)
{
printf("[%lu:%lu - %lu:%lu] ",
tok->tok_start.c_row,
tok->tok_start.c_col,
tok->tok_end.c_row,
tok->tok_end.c_col);
switch (tok->tok_type) {
case TOK_KEYWORD:
fx_puts("[magenta]");
break;
case TOK_SYMBOL:
fx_puts("[blue]");
break;
case TOK_INT:
case TOK_DOUBLE:
case TOK_VAR:
case TOK_VAR_SPLAT:
fx_puts("[yellow]");
break;
case TOK_OPERATOR:
fx_puts("[red]");
break;
case TOK_WORD:
case TOK_WORD_START:
case TOK_WORD_END:
fx_puts("[cyan]");
break;
case TOK_STRING:
case TOK_STR_START:
case TOK_STR_END:
fx_puts("[green]");
break;
case TOK_LINEFEED:
fx_puts("[dark_grey]");
break;
default:
break;
}
fx_puts(token_type_to_string(tok->tok_type));
switch (tok->tok_type) {
case TOK_WORD:
case TOK_FLAG:
case TOK_STRING:
case TOK_VAR:
case TOK_VAR_SPLAT:
printf("(%s)", tok->tok_str);
break;
case TOK_OPERATOR:
printf("(%s)", token_operator_to_string(tok->tok_operator));
break;
case TOK_SYMBOL:
printf("(%s)", token_symbol_to_string(tok->tok_symbol));
break;
case TOK_KEYWORD:
printf("(%s)", token_keyword_to_string(tok->tok_keyword));
break;
case TOK_INT:
case TOK_DOUBLE:
printf("(");
fx_value_to_string(&tok->tok_number, fx_stdout, NULL);
printf(")");
break;
default:
break;
}
fx_puts("[reset]\n");
}
void print_ast_node(struct ast_node *node)
{
for (unsigned long i = 0; i < node->n_it.e_depth; i++) {
fx_puts(" ");
}
switch (node->n_type) {
case AST_IF:
case AST_IF_BRANCH:
case AST_BLOCK:
case AST_FUNC:
case AST_STMT_LIST:
fx_puts("[magenta]");
break;
case AST_REDIRECTION:
case AST_PIPELINE:
case AST_OP:
case AST_ARRAY:
case AST_HASHTABLE:
case AST_HASHTABLE_ITEM:
fx_puts("[blue]");
break;
case AST_CMDCALL:
case AST_NULL:
fx_puts("[red]");
break;
case AST_INT:
case AST_DOUBLE:
case AST_VAR:
fx_puts("[yellow]");
break;
case AST_WORD:
fx_puts("[cyan]");
break;
case AST_STRING:
case AST_FSTRING:
fx_puts("[green]");
break;
default:
break;
}
fx_printf("%s", ast_node_type_to_string(node->n_type));
char s[128] = {0};
fx_bstr str;
fx_bstr_begin(&str, s, sizeof s);
ast_node_to_string(node, &str);
if (fx_bstr_get_size(&str)) {
fx_printf("(%s)", fx_bstr_end(&str));
}
fx_printf("[reset]\n");
}
void print_ast_node_recursive(struct ast_node *node)
{
struct ast_iterator it = {0};
ast_iterator_enqueue(&it, node);
while (1) {
node = ast_iterator_peek(&it);
if (!node) {
break;
}
print_ast_node(node);
ast_iterator_dequeue(&it);
}
}
void print_scriptblock(bshell_scriptblock *block, size_t depth)
{
bshell_instruction *instr = NULL;
size_t nr_instr = 0;
bshell_scriptblock_get_text(block, &instr, &nr_instr);
for (size_t i = 0; i < nr_instr; i++) {
size_t offset = i * sizeof *instr;
for (size_t ii = 0; ii < depth; ii++) {
fputs("| ", stdout);
}
printf("%04zx: ", offset);
print_instruction(offset, instr[i], block, depth);
}
}
void print_instruction(
size_t offset,
bshell_instruction instr,
bshell_scriptblock *container,
size_t depth)
{
enum bshell_opcode opcode = 0;
uint32_t arg = 0;
fx_value *pool_value = NULL;
fx_object *obj;
double d;
const char *s = NULL;
bshell_instruction_decode(instr, &opcode, &arg);
fx_printf("%08x [blue]", instr);
switch (opcode) {
case OPCODE_LDC_INT:
fx_printf("ldc.int [yellow]#0x%02x", arg);
break;
case OPCODE_LDC_FP:
pool_value = bshell_scriptblock_get_pool_value(
container,
arg,
FX_TYPE_DOUBLE);
fx_printf("ldc.fp [cyan]@0x%02x ", arg);
if (pool_value) {
fx_value_get_double(pool_value, &d);
fx_printf("[reset][[[yellow]%g[reset]]", d);
} else {
fx_printf("[red]<INVALID>[reset]");
}
break;
case OPCODE_LDC_STR:
pool_value = bshell_scriptblock_get_pool_value(
container,
arg,
FX_TYPE_STRING);
if (!pool_value) {
pool_value = bshell_scriptblock_get_pool_value(
container,
arg,
FX_TYPE_CSTR);
}
fx_printf("ldc.str [cyan]@0x%02x ", arg);
if (pool_value) {
fx_value_get_cstr(pool_value, &s);
fx_printf("[reset][[[green]%s[reset]]", s);
} else {
fx_printf("[red]<INVALID>[reset]");
}
break;
case OPCODE_LDGLOBAL:
fx_printf("ldglobal [cyan]@0x%02x", arg);
break;
case OPCODE_LDBLOCK:
pool_value = bshell_scriptblock_get_pool_value(
container,
arg,
BSHELL_TYPE_SCRIPTBLOCK);
fx_printf("ldblock [cyan]@0x%02x ", arg);
if (pool_value) {
fx_printf("[reset][[[magenta]vvv[reset]]\n");
fx_value_get_object(pool_value, &obj);
print_scriptblock(obj, depth + 1);
} else {
fx_printf("[red]<INVALID>[reset]");
}
break;
case OPCODE_LDLOCAL:
pool_value = bshell_scriptblock_get_pool_value(
container,
arg,
FX_TYPE_STRING);
if (!pool_value) {
pool_value = bshell_scriptblock_get_pool_value(
container,
arg,
FX_TYPE_CSTR);
}
fx_printf("ldlocal [cyan]@0x%02x ", arg);
if (pool_value) {
fx_value_get_cstr(pool_value, &s);
fx_printf("[reset][[[green]%s[reset]]", s);
} else {
fx_printf("[red]<INVALID>");
}
break;
case OPCODE_LDENV:
fx_printf("ldenv [cyan]@0x%02x", arg);
break;
case OPCODE_LDSTATIC:
fx_printf("ldstatic [cyan]@0x%02x", arg);
break;
case OPCODE_LDARG:
fx_printf("ldarg [cyan]@0x%02x", arg);
break;
case OPCODE_LDPROP:
fx_printf("ldprop");
break;
case OPCODE_LDELEM:
fx_printf("ldelem [cyan]@0x%02x", arg);
break;
case OPCODE_LDELEM_C:
fx_printf("ldelem.c [cyan]@0x%02x", arg);
break;
case OPCODE_STLOCAL:
pool_value = bshell_scriptblock_get_pool_value(
container,
arg,
FX_TYPE_STRING);
if (!pool_value) {
pool_value = bshell_scriptblock_get_pool_value(
container,
arg,
FX_TYPE_CSTR);
}
fx_printf("stlocal [cyan]@0x%02x ", arg);
if (pool_value) {
fx_value_get_cstr(pool_value, &s);
fx_printf("[reset][[[green]%s[reset]]", s);
} else {
fx_printf("<INVALID>");
}
break;
case OPCODE_STPROP:
fx_printf("stprop [cyan]@0x%02x", arg);
break;
case OPCODE_STELEM:
fx_printf("stelem [cyan]@0x%02x", arg);
break;
case OPCODE_LDCMD:
fx_printf("ldcmd [yellow]#0x%02x", arg);
break;
case OPCODE_PEXEC:
fx_printf("pexec [yellow]#0x%02x", arg);
break;
case OPCODE_CALL:
fx_printf("call");
break;
case OPCODE_MK_STR:
fx_printf("mk.str [yellow]#0x%02x", arg);
break;
case OPCODE_MK_ARRAY:
fx_printf("mk.array [yellow]#0x%02x", arg);
break;
case OPCODE_MK_HTAB:
fx_printf("mk.htab [yellow]#0x%02x", arg);
break;
case OPCODE_POP:
fx_printf("pop");
break;
case OPCODE_ADD:
fx_printf("add");
break;
case OPCODE_SUB:
fx_printf("sub");
break;
case OPCODE_MUL:
fx_printf("mul");
break;
case OPCODE_DIV:
fx_printf("div");
break;
case OPCODE_ADD_IP:
fx_printf("add.ip");
break;
case OPCODE_MOD:
fx_printf("mod");
break;
case OPCODE_INC:
fx_printf("inc");
break;
case OPCODE_DEC:
fx_printf("dec");
break;
case OPCODE_SHL:
fx_printf("shl");
break;
case OPCODE_SHR:
fx_printf("shr");
break;
case OPCODE_BAND:
fx_printf("band");
break;
case OPCODE_BOR:
fx_printf("bor");
break;
case OPCODE_BXOR:
fx_printf("bxor");
break;
case OPCODE_BNOT:
fx_printf("bnot");
break;
case OPCODE_CMP_NULL:
fx_printf("cmp.null");
break;
case OPCODE_CMP_EQ:
fx_printf("cmp.eq");
break;
case OPCODE_CMP_NE:
fx_printf("cmp.ne");
break;
case OPCODE_CMP_LT:
fx_printf("cmp.lt");
break;
case OPCODE_CMP_GT:
fx_printf("cmp.gt");
break;
case OPCODE_CMP_LE:
fx_printf("cmp.le");
break;
case OPCODE_CMP_GE:
fx_printf("cmp.ge");
break;
case OPCODE_LAND:
fx_printf("land");
break;
case OPCODE_LOR:
fx_printf("lor");
break;
case OPCODE_LXOR:
fx_printf("lxor");
break;
case OPCODE_LNOT:
fx_printf("lnot");
break;
case OPCODE_BR:
fx_printf(
"br [yellow]#0x%04x [=%04llx]",
arg,
(long long)arg + (long long)offset);
break;
case OPCODE_BR_TRUE:
fx_printf(
"br.true [yellow]#0x%04x [=%04llx]",
arg,
(long long)arg + (long long)offset);
break;
case OPCODE_RANGE:
fx_printf("range");
break;
case OPCODE_MATCH:
fx_printf("match");
break;
case OPCODE_REPLACE:
fx_printf("replace");
break;
case OPCODE_LIKE:
fx_printf("like");
break;
case OPCODE_IN:
fx_printf("in");
break;
case OPCODE_FMT:
fx_printf("fmt");
break;
case OPCODE_CONTAINS:
fx_printf("contains");
break;
case OPCODE_SPLIT:
fx_printf("split");
break;
case OPCODE_JOIN:
fx_printf("join");
break;
case OPCODE_IS:
fx_printf("is");
break;
case OPCODE_AS:
fx_printf("as");
break;
default:
fx_printf("INVALID\n");
break;
}
fx_printf("[reset]\n");
}
void print_value(fx_value *val)
{
fx_printf("[yellow]");
fx_value_to_string(val, fx_stdout, NULL);
fx_printf("[reset]\n");
}
+23
View File
@@ -0,0 +1,23 @@
#ifndef DEBUG_H_
#define DEBUG_H_
#include "runtime/opcode.h"
#include "script-block.h"
#include <stdbool.h>
struct ast_node;
struct lex_token;
extern void print_lex_token(struct lex_token *tok);
extern void print_ast_node(struct ast_node *node);
extern void print_ast_node_recursive(struct ast_node *node);
extern void print_scriptblock(bshell_scriptblock *block, size_t depth);
extern void print_instruction(
size_t offset,
bshell_instruction instr,
bshell_scriptblock *container,
size_t depth);
extern void print_value(fx_value *val);
#endif
+118
View File
@@ -0,0 +1,118 @@
#include "file.h"
#include "line-source.h"
#include <errno.h>
#include <fx/collections/array.h>
#include <fx/string.h>
#include <stdlib.h>
#include <string.h>
static enum bshell_status get_name(
struct line_source *src,
char *buf,
size_t count,
size_t *nr_read)
{
struct file *f = (struct file *)src;
*nr_read = snprintf(buf, count, "%s", f->f_path);
return BSHELL_SUCCESS;
}
static enum bshell_status get_row(
struct line_source *src,
size_t row,
char *buf,
size_t count,
size_t *nr_read)
{
struct file *f = (struct file *)src;
size_t nr_rows = fx_array_get_size(f->f_lines);
if (row > nr_rows) {
return BSHELL_ERR_EOF;
}
fx_value *line_v = fx_array_get_ref(f->f_lines, row - 1);
fx_string *line = NULL;
fx_value_get_object(line_v, &line);
const char *line_str = fx_string_get_cstr(line);
size_t line_len = fx_string_get_size(line, FX_STRLEN_NORMAL);
size_t copy_len = fx_min(ulong, count, line_len);
memcpy(buf, line_str, copy_len);
buf[copy_len] = 0;
buf[strcspn(buf, "\n")] = 0;
*nr_read = copy_len;
return BSHELL_SUCCESS;
}
static enum bshell_status readline(
struct line_source *src,
fx_stringstream *out)
{
struct file *f = (struct file *)src;
fx_wchar c = FX_WCHAR_INVALID;
size_t nr_read = 0;
while (1) {
fx_status status = fx_stream_read_char(f->f_strp, &c);
if (!FX_OK(status)) {
break;
}
fx_stream_write_char(out, c);
nr_read++;
if (c == '\n') {
break;
}
}
if (nr_read == 0) {
return BSHELL_ERR_EOF;
}
return BSHELL_SUCCESS;
}
enum bshell_status file_open(const char *path, struct file **out)
{
FILE *fp = fopen(path, "r");
if (!fp) {
return bshell_status_from_errno(errno);
}
fx_stream *strp = fx_stream_open_fp(fp);
struct file *file = malloc(sizeof *file);
if (!file) {
fclose(fp);
return BSHELL_ERR_NO_MEMORY;
}
memset(file, 0x0, sizeof *file);
file->f_base.s_get_name = get_name;
file->f_base.s_get_row = get_row;
file->f_base.s_readline = readline;
file->f_fp = fp;
file->f_strp = strp;
file->f_path = fx_strdup(path);
file->f_lines = fx_array_create();
*out = file;
return BSHELL_SUCCESS;
}
void file_close(struct file *file)
{
fx_stream_unref(file->f_strp);
fx_array_unref(file->f_lines);
free(file->f_path);
fclose(file->f_fp);
free(file);
}
+20
View File
@@ -0,0 +1,20 @@
#ifndef FILE_H_
#define FILE_H_
#include "line-source.h"
#include <fx/collections/array.h>
#include <stdio.h>
struct file {
struct line_source f_base;
fx_array *f_lines;
char *f_path;
fx_stream *f_strp;
FILE *f_fp;
};
extern enum bshell_status file_open(const char *path, struct file **out);
extern void file_close(struct file *file);
#endif
+14
View File
@@ -0,0 +1,14 @@
#include "format.h"
static struct table_format_column command_table_columns[] = {
TABLE_COLUMN("command_type", "command_type", 13),
TABLE_COLUMN("name", "name", COL_WIDTH_INFINITE),
TABLE_COLUMN("version", "version", 12),
TABLE_COLUMN("source", "source", 35),
};
struct table_format command_table_format = {
.fmt_columns = command_table_columns,
.fmt_column_count = sizeof command_table_columns
/ sizeof command_table_columns[0],
};
+29
View File
@@ -0,0 +1,29 @@
#include "format.h"
#include <fx/term/print.h>
#include <fx/type.h>
#include <fx/value-type.h>
static void format_value_fallback(const fx_value *value, fx_stream *dest)
{
fx_printf("[yellow]");
fx_value_to_string(value, dest, NULL);
fx_printf("[reset]\n");
}
enum bshell_status format_value_default(const fx_value *value, fx_stream *dest)
{
if (fx_type_is_value_type(value->v_type)) {
format_value_fallback(value, dest);
return BSHELL_SUCCESS;
}
fx_object *object = NULL;
fx_value_get_object(value, &object);
enum bshell_status status = format_object_table(object, dest);
if (status != BSHELL_SUCCESS) {
format_value_fallback(value, dest);
}
return BSHELL_SUCCESS;
}
+78
View File
@@ -0,0 +1,78 @@
#ifndef FORMAT_H_
#define FORMAT_H_
#include "../status.h"
#include <fx/namemap.h>
#include <fx/object.h>
#include <fx/queue.h>
#include <fx/reflection/property.h>
#include <fx/reflection/type.h>
#include <fx/stream.h>
#include <fx/value.h>
#define TABLE_COLUMN(display_name, property_name, width) \
{ \
.col_display_name = (display_name), \
.col_property_name = (property_name), \
.col_width = (width), \
}
#define COL_WIDTH_INFINITE ((size_t)-1)
struct table_format_column {
const char *col_display_name;
const char *col_property_name;
size_t col_width;
};
struct table_format {
fx_namemap_entry fmt_entry;
const struct table_format_column *fmt_columns;
size_t fmt_column_count;
};
struct table_format_ctx_column {
const char *col_title;
size_t col_width;
size_t col_title_chars_written;
const char *col_title_ptr;
const fx_property *col_property;
};
struct table_format_ctx {
const struct table_format *ctx_fmt;
struct table_format_ctx_column *ctx_columns;
size_t ctx_columns_count;
unsigned int ctx_tty_width;
size_t ctx_variable_columns_count;
fx_stream *ctx_out;
};
extern enum bshell_status table_format_init(void);
extern const struct table_format *table_format_get_for_type(const fx_type *ty);
extern enum bshell_status format_object_table(
fx_object *object,
fx_stream *dest);
extern enum bshell_status format_value_default(
const fx_value *value,
fx_stream *dest);
extern enum bshell_status table_format_ctx_init(
struct table_format_ctx *ctx,
fx_stream *out);
extern enum bshell_status table_format_ctx_cleanup(
struct table_format_ctx *ctx);
extern enum bshell_status table_format_ctx_prepare_columns_from_format(
struct table_format_ctx *ctx,
const fx_type *record_type,
const struct table_format *fmt);
extern enum bshell_status table_format_ctx_prepare_columns_from_object(
struct table_format_ctx *ctx,
const fx_type *record_type);
extern enum bshell_status table_format_ctx_print_headers(
struct table_format_ctx *ctx);
extern enum bshell_status table_format_ctx_print_record(
struct table_format_ctx *ctx,
const fx_value *record);
#endif
+12
View File
@@ -0,0 +1,12 @@
#include "format.h"
static struct table_format_column hashtable_table_columns[] = {
TABLE_COLUMN("key", "key", 37),
TABLE_COLUMN("value", "value", COL_WIDTH_INFINITE),
};
struct table_format hashtable_table_format = {
.fmt_columns = hashtable_table_columns,
.fmt_column_count = sizeof hashtable_table_columns
/ sizeof hashtable_table_columns[0],
};
+353
View File
@@ -0,0 +1,353 @@
#include "format.h"
#include <fx/collections/array.h>
#include <fx/iterator.h>
#include <fx/reflection/type.h>
#include <fx/stringstream.h>
#include <fx/term/print.h>
#include <fx/term/tty.h>
#include <fx/value.h>
static fx_namemap table_formats = FX_NAMEMAP_INIT;
extern struct table_format hashtable_table_format;
extern struct table_format verb_table_format;
extern struct table_format command_table_format;
enum bshell_status table_format_init(void)
{
fx_namemap_put(
&table_formats,
"fx.collections.hashtable",
&hashtable_table_format.fmt_entry);
fx_namemap_put(
&table_formats,
"bshell.verb",
&verb_table_format.fmt_entry);
fx_namemap_put(
&table_formats,
"bshell.command",
&command_table_format.fmt_entry);
return BSHELL_SUCCESS;
}
const struct table_format *table_format_get_for_type(const fx_type *ty)
{
while (ty) {
const char *name = fx_type_get_name(ty);
fx_namemap_entry *entry = fx_namemap_get(&table_formats, name);
if (entry) {
return fx_unbox(struct table_format, entry, fmt_entry);
}
ty = fx_type_get_parent(ty);
}
return NULL;
}
enum bshell_status format_object_table(fx_object *object, fx_stream *dest)
{
const fx_type *type = fx_type_get_by_id(fx_object_query_type(object));
if (!type) {
return BSHELL_ERR_NOT_SUPPORTED;
}
const struct table_format *fmt = table_format_get_for_type(type);
if (!fmt) {
return BSHELL_ERR_NOT_SUPPORTED;
}
struct table_format_ctx ctx;
table_format_ctx_init(&ctx, dest);
fx_iterator *it = fx_iterator_begin(object);
if (!it) {
table_format_ctx_prepare_columns_from_format(
&ctx,
fx_type_get_by_id(fx_object_query_type(object)),
fmt);
table_format_ctx_print_headers(&ctx);
table_format_ctx_print_record(&ctx, &FX_VALUE_OBJECT(object));
} else {
fx_value first = fx_iterator_get_value(it);
if (first.v_type) {
table_format_ctx_prepare_columns_from_format(
&ctx,
fx_type_get_by_id(first.v_type),
fmt);
}
table_format_ctx_print_headers(&ctx);
fx_foreach(value, it)
{
table_format_ctx_print_record(&ctx, &value);
}
}
table_format_ctx_cleanup(&ctx);
return BSHELL_SUCCESS;
}
enum bshell_status table_format_ctx_init(
struct table_format_ctx *ctx,
fx_stream *out)
{
memset(ctx, 0x0, sizeof *ctx);
ctx->ctx_out = out;
return BSHELL_SUCCESS;
}
enum bshell_status table_format_ctx_cleanup(struct table_format_ctx *ctx)
{
if (ctx->ctx_columns) {
free(ctx->ctx_columns);
}
memset(ctx, 0x0, sizeof *ctx);
return BSHELL_SUCCESS;
}
enum bshell_status table_format_ctx_prepare_columns_from_format(
struct table_format_ctx *ctx,
const fx_type *record_type,
const struct table_format *fmt)
{
if (!fmt && !record_type) {
return BSHELL_ERR_INVALID_ARGUMENT;
}
if (!fmt) {
fmt = table_format_get_for_type(record_type);
}
if (!fmt) {
return table_format_ctx_prepare_columns_from_object(
ctx,
record_type);
}
ctx->ctx_fmt = fmt;
struct table_format_ctx_column *columns = calloc(
fmt->fmt_column_count,
sizeof *columns);
if (!columns) {
return BSHELL_ERR_NO_MEMORY;
}
const struct table_format_column *column_defs = fmt->fmt_columns;
ctx->ctx_columns = columns;
ctx->ctx_columns_count = fmt->fmt_column_count;
fx_tty_get_dimensions(fx_stdtty, &ctx->ctx_tty_width, NULL);
if (!ctx->ctx_tty_width) {
ctx->ctx_tty_width = 150;
}
ctx->ctx_variable_columns_count = 0;
unsigned int tty_width = ctx->ctx_tty_width;
for (size_t i = 0; i < fmt->fmt_column_count; i++) {
columns[i].col_property = fx_type_get_property(
record_type,
column_defs[i].col_property_name);
columns[i].col_title = column_defs[i].col_display_name;
columns[i].col_title_ptr = column_defs[i].col_display_name;
if (column_defs[i].col_width == COL_WIDTH_INFINITE) {
ctx->ctx_variable_columns_count++;
continue;
}
if (column_defs[i].col_width <= tty_width) {
columns[i].col_width = column_defs[i].col_width;
tty_width -= columns[i].col_width;
continue;
}
columns[i].col_width = tty_width;
tty_width = 0;
break;
}
for (size_t i = 0; i < fmt->fmt_column_count; i++) {
if (column_defs[i].col_width == COL_WIDTH_INFINITE) {
columns[i].col_width = tty_width
/ ctx->ctx_variable_columns_count;
}
}
return BSHELL_SUCCESS;
}
enum bshell_status table_format_ctx_prepare_columns_from_object(
struct table_format_ctx *ctx,
const fx_type *record_type)
{
fx_array *properties = fx_array_create();
fx_iterator *it = fx_type_get_properties(record_type);
fx_foreach(prop_v, it)
{
fx_array_push_back(properties, prop_v);
}
fx_iterator_unref(it);
size_t column_count = fx_array_get_size(properties);
if (!column_count) {
fx_array_unref(properties);
return BSHELL_SUCCESS;
}
struct table_format_ctx_column *columns = calloc(
column_count,
sizeof *columns);
if (!columns) {
fx_array_unref(properties);
return BSHELL_ERR_NO_MEMORY;
}
ctx->ctx_columns = columns;
ctx->ctx_columns_count = column_count;
fx_tty_get_dimensions(fx_stdtty, &ctx->ctx_tty_width, NULL);
if (!ctx->ctx_tty_width) {
ctx->ctx_tty_width = 150;
}
ctx->ctx_variable_columns_count = 0;
unsigned int tty_width = ctx->ctx_tty_width;
for (size_t i = 0; i < column_count; i++) {
fx_value *prop_v = fx_array_get_ref(properties, i);
fx_property *prop = NULL;
fx_value_get_object(prop_v, &prop);
columns[i].col_property = prop;
columns[i].col_title = fx_property_get_name(prop);
columns[i].col_title_ptr = columns[i].col_title;
columns[i].col_width = tty_width / column_count;
}
return BSHELL_SUCCESS;
}
enum bshell_status table_format_ctx_print_headers(struct table_format_ctx *ctx)
{
fx_printf("[bold,bright_green]");
bool repeat = false;
struct table_format_ctx_column *c = NULL;
do {
repeat = false;
for (size_t i = 0; i < ctx->ctx_columns_count; i++) {
c = &ctx->ctx_columns[i];
if (c->col_width == 0) {
continue;
}
size_t title_chars_remaining = strlen(c->col_title_ptr);
size_t to_write = title_chars_remaining;
if (to_write > c->col_width) {
to_write = c->col_width;
}
size_t x;
for (x = 0; x < to_write; x++) {
fx_stream_write_char(
ctx->ctx_out,
*c->col_title_ptr);
c->col_title_ptr++;
}
for (; x < c->col_width; x++) {
fx_stream_write_char(ctx->ctx_out, ' ');
}
if (to_write != title_chars_remaining) {
repeat = true;
}
}
} while (repeat);
fx_stream_write_char(ctx->ctx_out, '\n');
for (size_t i = 0; i < ctx->ctx_columns_count; i++) {
struct table_format_ctx_column *c = &ctx->ctx_columns[i];
if (c->col_width == 0) {
continue;
}
size_t title_len = strlen(c->col_title);
if (title_len > c->col_width) {
title_len = c->col_width;
}
size_t x;
for (x = 0; x < title_len; x++) {
fx_stream_write_char(ctx->ctx_out, '-');
}
for (; x < c->col_width; x++) {
fx_stream_write_char(ctx->ctx_out, ' ');
}
}
fx_printf("[reset]");
fx_stream_write_char(ctx->ctx_out, '\n');
return BSHELL_SUCCESS;
}
enum bshell_status table_format_ctx_print_record(
struct table_format_ctx *ctx,
const fx_value *record)
{
fx_stringstream *strm = fx_stringstream_create();
for (size_t i = 0; i < ctx->ctx_columns_count; i++) {
struct table_format_ctx_column *c = &ctx->ctx_columns[i];
if (c->col_width == 0) {
continue;
}
fx_stringstream_reset(strm);
fx_value column_value = FX_VALUE_EMPTY;
if (c->col_property) {
fx_status status = fx_property_get_value(
c->col_property,
record,
&column_value);
if (FX_OK(status)) {
fx_value_to_string(&column_value, strm, NULL);
}
}
size_t to_write = fx_stringstream_get_length(strm);
const char *s = fx_stringstream_ptr(strm);
bool overflow = false;
if (to_write > c->col_width) {
to_write = c->col_width - 2;
overflow = true;
}
size_t x;
for (x = 0; x < to_write; x++) {
fx_stream_write_char(ctx->ctx_out, s[x]);
}
if (overflow) {
fx_stream_write_char(ctx->ctx_out, L'');
x++;
}
for (; x < c->col_width; x++) {
fx_stream_write_char(ctx->ctx_out, ' ');
}
}
fx_stream_write_char(ctx->ctx_out, '\n');
fx_stringstream_unref(strm);
return BSHELL_SUCCESS;
}
+14
View File
@@ -0,0 +1,14 @@
#include "format.h"
static struct table_format_column verb_table_columns[] = {
TABLE_COLUMN("verb", "verb", 13),
TABLE_COLUMN("alias_prefix", "alias_prefix", 13),
TABLE_COLUMN("group", "group", 13),
TABLE_COLUMN("description", "description", COL_WIDTH_INFINITE),
};
struct table_format verb_table_format = {
.fmt_columns = verb_table_columns,
.fmt_column_count = sizeof verb_table_columns
/ sizeof verb_table_columns[0],
};
+43
View File
@@ -0,0 +1,43 @@
#include "buffer.h"
#include "line-ed.h"
#include <fx/wstr.h>
const fx_wchar *line_start(struct line_ed *ed, size_t y)
{
const fx_wchar *line = ed->l_buf;
for (size_t i = 0; i < y; i++) {
line += fx_wstrcspn(line, L"\n");
if (*line == '\n') {
line++;
}
}
return line;
}
size_t line_length(struct line_ed *ed, size_t y)
{
const fx_wchar *line = ed->l_buf;
for (size_t i = 0; i < y; i++) {
line += fx_wstrcspn(line, L"\n");
if (*line == '\n') {
line++;
}
}
if (*line == '\0') {
return 0;
}
size_t len = fx_wstrcspn(line, L"\n");
if (line[len] == '\n') {
len++;
}
return len;
}
+17
View File
@@ -0,0 +1,17 @@
#ifndef LINE_ED_BUFFER_H_
#define LINE_ED_BUFFER_H_
#include <fx/encoding.h>
#include <stddef.h>
struct line_ed;
/* returns a pointer to the start of the line based on the given `y`
* coordinate */
extern const fx_wchar *line_start(struct line_ed *ed, size_t y);
/* returns the length of the line based on the given `y` coordinate.
* for any line other than the last line in the buffer, this length
* INCLUDES the trailing linefeed. */
extern size_t line_length(struct line_ed *ed, size_t y);
#endif
+26
View File
@@ -0,0 +1,26 @@
#include "line-ed.h"
#include "cursor.h"
#include "prompt.h"
void line_ed_coords_to_physical_coords(
struct line_ed *ed, size_t x, size_t y, size_t *out_x, size_t *out_y)
{
size_t prompt_len = 0;
if (ed->l_cursor_y == 0) {
prompt_len = prompt_length(ed, PROMPT_MAIN);
} else if (ed->l_cursor_y <= ed->l_continuations) {
prompt_len = prompt_length(ed, PROMPT_CONT);
}
if (y == 0) {
x += prompt_len;
}
if (out_x) {
*out_x = x;
}
if (out_y) {
*out_y = y;
}
}
+9
View File
@@ -0,0 +1,9 @@
#ifndef LINE_ED_CURSOR_H_
#define LINE_ED_CURSOR_H_
struct line_ed;
extern void line_ed_coords_to_physical_coords(
struct line_ed *ed, size_t x, size_t y, size_t *out_x, size_t *out_y);
#endif
+87
View File
@@ -0,0 +1,87 @@
#include "../misc.h"
#include "line-ed.h"
#include <fx/collections/array.h>
#include <fx/string.h>
void alloc_empty_history_entry(struct line_ed *ed)
{
fx_value *str_v = fx_array_get_ref(
ed->l_history,
fx_array_get_size(ed->l_history) - 1);
fx_string *str = NULL;
fx_value_get_object(str_v, &str);
if (!str || fx_string_get_size(str, FX_STRLEN_NORMAL) > 0) {
str = fx_string_create();
fx_array_push_back(ed->l_history, FX_VALUE_OBJECT(str));
fx_string_unref(str);
}
ed->l_history_pos = fx_array_get_size(ed->l_history) - 1;
}
void save_buf_to_history(struct line_ed *ed)
{
fx_value *cur_v = fx_array_get_ref(ed->l_history, ed->l_history_pos);
fx_string *cur = NULL;
fx_value_get_object(cur_v, &cur);
fx_string_clear(cur);
fx_string_append_wstr(cur, ed->l_buf);
}
void append_buf_to_history(struct line_ed *ed)
{
fx_value *cur_v = fx_array_get_ref(ed->l_history, ed->l_history_pos);
fx_string *cur = NULL;
fx_value_get_object(cur_v, &cur);
char s[] = {'\n', 0};
fx_string_append_cstr(cur, s);
fx_string_append_wstr(cur, ed->l_buf);
}
void load_buf_from_history(struct line_ed *ed)
{
fx_value *cur_v = fx_array_get_ref(ed->l_history, ed->l_history_pos);
fx_string *cur = NULL;
fx_value_get_object(cur_v, &cur);
size_t len = MIN(
(size_t)(ed->l_buf_end - ed->l_buf - 1),
fx_string_get_size(cur, FX_STRLEN_CODEPOINTS));
const char *src = fx_string_get_cstr(cur);
for (size_t i = 0; i < len; i++) {
fx_wchar ch_src = fx_wchar_utf8_codepoint_decode(src);
ed->l_buf[i] = ch_src;
src += fx_wchar_utf8_codepoint_stride(src);
}
ed->l_buf[len] = '\0';
unsigned int x = 0, y = 0;
for (size_t i = 0; ed->l_buf[i]; i++) {
if (ed->l_buf[i] == '\n') {
x = 0;
y++;
} else {
x++;
}
}
ed->l_buf_ptr = ed->l_buf + len;
ed->l_line_end = ed->l_buf_ptr;
ed->l_cursor_x = x;
ed->l_cursor_y = y;
}
const char *last_history_line(struct line_ed *ed)
{
size_t nlines = fx_array_get_size(ed->l_history);
if (nlines < 2) {
return NULL;
}
fx_value *last_v = fx_array_get_ref(ed->l_history, nlines - 2);
fx_string *last = NULL;
fx_value_get_object(last_v, &last);
return fx_string_get_cstr(last);
}
+12
View File
@@ -0,0 +1,12 @@
#ifndef LINE_ED_HISTORY_H_
#define LINE_ED_HISTORY_H_
struct line_ed;
extern void alloc_empty_history_entry(struct line_ed *ed);
extern void save_buf_to_history(struct line_ed *ed);
extern void append_buf_to_history(struct line_ed *ed);
extern void load_buf_from_history(struct line_ed *ed);
extern const char *last_history_line(struct line_ed *ed);
#endif
+41
View File
@@ -0,0 +1,41 @@
#include "hook.h"
#include "line-ed.h"
void line_ed_add_hook(struct line_ed *ed, struct line_ed_hook *hook)
{
fx_queue_push_back(&ed->l_hooks, &hook->hook_entry);
}
void line_ed_remove_hook(struct line_ed *ed, struct line_ed_hook *hook)
{
fx_queue_delete(&ed->l_hooks, &hook->hook_entry);
}
void hook_keypress(struct line_ed *ed, fx_keycode key)
{
fx_queue_entry *entry = fx_queue_first(&ed->l_hooks);
while (entry) {
struct line_ed_hook *hook
= fx_unbox(struct line_ed_hook, entry, hook_entry);
if (hook->hook_keypress) {
hook->hook_keypress(ed, hook, key);
}
entry = fx_queue_next(entry);
}
}
void hook_buffer_modified(struct line_ed *ed)
{
fx_queue_entry *entry = fx_queue_first(&ed->l_hooks);
while (entry) {
struct line_ed_hook *hook
= fx_unbox(struct line_ed_hook, entry, hook_entry);
if (hook->hook_buffer_modified) {
hook->hook_buffer_modified(ed, hook);
}
entry = fx_queue_next(entry);
}
}
+16
View File
@@ -0,0 +1,16 @@
#ifndef LINE_ED_HOOK_H_
#define LINE_ED_HOOK_H_
#include <fx/term/tty.h>
enum hook_id {
HOOK_KEYPRESS,
HOOK_BEFORE_PAINT,
};
struct line_ed;
extern void hook_keypress(struct line_ed *ed, fx_keycode key);
extern void hook_buffer_modified(struct line_ed *ed);
#endif
+232
View File
@@ -0,0 +1,232 @@
#include "buffer.h"
#include "cursor.h"
#include "history.h"
#include "hook.h"
#include "line-ed.h"
#include "prompt.h"
#include "refresh.h"
#include <stdio.h>
void put_char(struct line_ed *ed, fx_wchar c)
{
if (ed->l_buf_ptr > ed->l_line_end + 1) {
return;
}
struct refresh_state state = {
.r_prev_cursor_x = ed->l_cursor_x,
.r_prev_cursor_y = ed->l_cursor_y,
};
size_t prev_cursor = ed->l_buf_ptr - ed->l_buf;
fx_wchar *dest = ed->l_buf_ptr;
size_t len = (ed->l_line_end - ed->l_buf_ptr + 1) * sizeof *dest;
if (dest < ed->l_line_end) {
memmove(dest + 1, dest, len);
}
ed->l_cursor_x++;
ed->l_line_end++;
ed->l_buf_ptr++;
*dest = c;
if (ed->l_buf_ptr == ed->l_line_end) {
*ed->l_buf_ptr = '\0';
}
hook_buffer_modified(ed);
put_refresh(ed, &state);
}
static void backspace_simple(struct line_ed *ed)
{
if (ed->l_buf_ptr == ed->l_buf) {
return;
}
struct refresh_state state = {
.r_prev_cursor_x = ed->l_cursor_x,
.r_prev_cursor_y = ed->l_cursor_y,
};
size_t prev_cursor = ed->l_buf_ptr - ed->l_buf;
fx_wchar *dest = ed->l_buf_ptr;
size_t len = (ed->l_line_end - ed->l_buf_ptr + 1) * sizeof *dest;
memmove(dest - 1, dest, len);
ed->l_cursor_x--;
ed->l_line_end--;
ed->l_buf_ptr--;
hook_buffer_modified(ed);
backspace_simple_refresh(ed, &state);
}
static void backspace_nl(struct line_ed *ed)
{
size_t prev_line_len = line_length(ed, ed->l_cursor_y - 1);
struct refresh_state state = {
.r_prev_cursor_x = ed->l_cursor_x,
.r_prev_cursor_y = ed->l_cursor_y,
.r_prev_line_len = prev_line_len,
};
fx_wchar *dest = ed->l_buf_ptr;
size_t len = (ed->l_line_end - ed->l_buf_ptr + 1) * sizeof *dest;
memmove(dest - 1, dest, len);
ed->l_cursor_x = prev_line_len - 1;
ed->l_cursor_y--;
ed->l_buf_ptr--;
ed->l_line_end--;
hook_buffer_modified(ed);
backspace_nl_refresh(ed, &state);
}
void backspace(struct line_ed *ed)
{
if (ed->l_buf_ptr == ed->l_buf) {
return;
}
if (ed->l_cursor_x == 0 && ed->l_cursor_y <= ed->l_continuations) {
return;
}
if (ed->l_cursor_x == 0 && ed->l_cursor_y > 0) {
backspace_nl(ed);
} else {
backspace_simple(ed);
}
}
void cursor_left(struct line_ed *ed)
{
if (ed->l_cursor_x != 0) {
// fputs("\010", stdout);
fx_tty_move_cursor_x(ed->l_tty, FX_TTY_POS_CURSOR, -1);
fflush(stdout);
ed->l_cursor_x--;
ed->l_buf_ptr--;
return;
}
if (ed->l_cursor_y <= ed->l_continuations
|| ed->l_buf_ptr <= ed->l_buf) {
return;
}
ed->l_cursor_y--;
ed->l_buf_ptr--;
size_t prompt_len = 0;
if (ed->l_cursor_y == 0) {
prompt_len = prompt_length(ed, PROMPT_MAIN);
}
size_t len = line_length(ed, ed->l_cursor_y);
ed->l_cursor_x = len - 1;
// printf("\033[A\033[%dG", len + prompt_len);
fx_tty_move_cursor_y(ed->l_tty, FX_TTY_POS_CURSOR, -1);
fx_tty_move_cursor_x(
ed->l_tty,
FX_TTY_POS_START,
(int)(len + prompt_len));
fflush(stdout);
}
void cursor_right(struct line_ed *ed)
{
if (ed->l_buf_ptr >= ed->l_line_end) {
return;
}
if (*ed->l_buf_ptr != '\n') {
ed->l_cursor_x++;
ed->l_buf_ptr++;
// fputs("\033[C", stdout);
fx_tty_move_cursor_x(ed->l_tty, FX_TTY_POS_CURSOR, 1);
fflush(stdout);
return;
}
if (ed->l_buf_ptr >= ed->l_line_end) {
return;
}
ed->l_cursor_y++;
ed->l_cursor_x = 0;
ed->l_buf_ptr++;
// printf("\033[B\033[G");
fx_tty_move_cursor_y(ed->l_tty, FX_TTY_POS_CURSOR, 1);
fx_tty_move_cursor_x(ed->l_tty, FX_TTY_POS_START, 0);
fflush(stdout);
}
void arrow_up(struct line_ed *ed)
{
if (ed->l_history_pos == 0) {
return;
}
if (ed->l_cursor_y > 0) {
// printf("\033[%uA", ed->l_cursor_y);
fx_tty_move_cursor_y(
ed->l_tty,
FX_TTY_POS_CURSOR,
(long long)ed->l_cursor_y);
}
// printf("\033[%zuG\033[J", prompt_length(ed, PROMPT_MAIN) + 1);
fx_tty_move_cursor_x(
ed->l_tty,
FX_TTY_POS_START,
(long long)prompt_length(ed, PROMPT_MAIN));
fx_tty_clear(ed->l_tty, FX_TTY_CLEAR_SCREEN | FX_TTY_CLEAR_FROM_CURSOR);
save_buf_to_history(ed);
ed->l_history_pos--;
load_buf_from_history(ed);
print_buffer(ed);
fflush(stdout);
}
void arrow_down(struct line_ed *ed)
{
if (ed->l_history_pos == fx_array_get_size(ed->l_history) - 1) {
return;
}
if (ed->l_cursor_y > 0) {
// printf("\033[%uA", ed->l_cursor_y);
fx_tty_move_cursor_y(
ed->l_tty,
FX_TTY_POS_CURSOR,
(int)ed->l_cursor_y);
}
// printf("\033[%zuG\033[J", prompt_length(ed, PROMPT_MAIN) + 1);
fx_tty_move_cursor_x(
ed->l_tty,
FX_TTY_POS_START,
prompt_length(ed, PROMPT_MAIN) + 1);
save_buf_to_history(ed);
ed->l_history_pos++;
load_buf_from_history(ed);
print_buffer(ed);
fflush(stdout);
}
+15
View File
@@ -0,0 +1,15 @@
#ifndef LINE_ED_INPUT_H_
#define LINE_ED_INPUT_H_
#include <fx/encoding.h>
struct line_ed;
extern void put_char(struct line_ed *ed, fx_wchar c);
extern void backspace(struct line_ed *ed);
extern void cursor_left(struct line_ed *ed);
extern void cursor_right(struct line_ed *ed);
extern void arrow_up(struct line_ed *ed);
extern void arrow_down(struct line_ed *ed);
#endif
+302
View File
@@ -0,0 +1,302 @@
#include "line-ed.h"
#include "history.h"
#include "hook.h"
#include "input.h"
#include "prompt.h"
#include <ctype.h>
#include <fx/term/tty.h>
#include <fx/wstr.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <wctype.h>
#define LINE_ED_FROM_LEX_SOURCE(p) \
((struct line_ed *)((char *)p \
- offsetof(struct line_ed, l_line_source)))
static enum bshell_status readline(
struct line_source *src,
fx_stringstream *out)
{
struct line_ed *ed = LINE_ED_FROM_LEX_SOURCE(src);
long r = line_ed_readline(ed, out);
line_ed_set_scope_type(ed, NULL);
if (r < 0) {
return BSHELL_ERR_EOF;
}
return BSHELL_SUCCESS;
}
struct line_ed *line_ed_create(void)
{
struct line_ed *out = malloc(sizeof *out);
if (!out) {
return NULL;
}
memset(out, 0x0, sizeof *out);
out->l_buf = malloc(LINE_MAX);
if (!out->l_buf) {
free(out);
return NULL;
}
out->l_history = fx_array_create();
if (!out->l_history) {
free(out->l_buf);
free(out);
return NULL;
}
out->l_tty = fx_stdtty;
out->l_buf_end = out->l_buf + LINE_MAX;
out->l_buf_ptr = out->l_buf;
out->l_line_end = out->l_buf;
out->l_prompt[PROMPT_MAIN] = ">>> ";
out->l_prompt[PROMPT_CONT] = "> ";
out->l_line_source.s_readline = readline;
return out;
}
void line_ed_destroy(struct line_ed *ed)
{
fx_array_unref(ed->l_history);
free(ed->l_buf);
free(ed);
}
void line_ed_set_flags(struct line_ed *ed, enum line_ed_flags flags)
{
ed->l_flags |= flags;
}
void line_ed_set_scope_type(struct line_ed *ed, const char *scope_type)
{
ed->l_scope_type = scope_type;
}
static void clear_buffer(struct line_ed *ed)
{
memset(ed->l_buf, 0x0, ed->l_buf_end - ed->l_buf);
ed->l_buf_ptr = ed->l_buf;
ed->l_line_end = ed->l_buf;
ed->l_cursor_x = 0;
ed->l_cursor_y = 0;
ed->l_continuations = 0;
}
static void convert_continuation_feeds(fx_wchar *s, size_t max)
{
fx_wchar *end = s + max;
size_t len = fx_wstrlen(s);
while (1) {
size_t r = fx_wstrcspn(s, L"\\");
if (s + r > end) {
break;
}
s += r;
len -= r;
if (*s == '\0') {
break;
}
if (*(s + 1) != '\n') {
s++;
continue;
}
memmove(s, s + 1, len);
s += 1;
}
}
static void remove_continuation_feeds(fx_wchar *s, size_t max)
{
fx_wchar *end = s + max;
size_t len = fx_wstrlen(s);
while (1) {
size_t r = fx_wstrcspn(s, L"\\");
if (s + r > end) {
break;
}
s += r;
len -= r;
if (*s == '\0') {
break;
}
if (*(s + 1) != '\n') {
s++;
continue;
}
memmove(s, s + 2, len);
s += 2;
}
}
static bool input_is_empty(struct line_ed *ed)
{
const fx_wchar *p = ed->l_buf;
while (p < ed->l_line_end) {
if (!fx_wchar_is_space(*p)) {
return false;
}
}
return true;
}
size_t line_ed_readline(struct line_ed *ed, fx_stringstream *out)
{
clear_buffer(ed);
bool append_history = false;
size_t append_to_index = (size_t)-1;
if (!ed->l_scope_type) {
alloc_empty_history_entry(ed);
} else {
append_history = true;
append_to_index = ed->l_history_pos;
}
fx_tty *tty = ed->l_tty;
fx_tty_set_mode(tty, FX_TTY_RAW);
show_prompt(ed);
for (int i = 0; ed->l_buf[i]; i++) {
if (ed->l_buf[i] == '\n') {
fputc('\r', stdout);
}
fx_stream_write_char(fx_stdout, ed->l_buf[i]);
}
fflush(stdout);
bool end = false;
bool eof = false;
while (!end) {
fx_keycode key = fx_tty_read_key(tty);
hook_keypress(ed, key);
if (key == FX_TTY_CTRL_KEY('d')) {
if (!input_is_empty(ed)) {
continue;
}
eof = true;
break;
}
if (key & FX_MOD_CTRL) {
continue;
}
switch (key) {
case FX_KEY_RETURN:
fx_tty_reset_vmode(tty);
if (ed->l_line_end > ed->l_buf
&& *(ed->l_line_end - 1) != '\\') {
end = true;
break;
}
if (input_is_empty(ed)) {
fputc('\r', stdout);
fputc('\n', stdout);
clear_buffer(ed);
show_prompt(ed);
break;
}
*ed->l_line_end = '\n';
ed->l_line_end++;
ed->l_buf_ptr = ed->l_line_end;
ed->l_cursor_x = 0;
ed->l_cursor_y++;
ed->l_continuations++;
fputc('\r', stdout);
fputc('\n', stdout);
// fputs("\033[G\n", stdout);
show_prompt(ed);
break;
case FX_KEY_BACKSPACE:
backspace(ed);
break;
case FX_KEY_ARROW_LEFT:
cursor_left(ed);
break;
case FX_KEY_ARROW_RIGHT:
cursor_right(ed);
break;
case FX_KEY_ARROW_UP:
arrow_up(ed);
break;
case FX_KEY_ARROW_DOWN:
arrow_down(ed);
break;
default:
if (fx_wchar_is_graph(key) || key == ' ') {
put_char(ed, key);
}
break;
}
}
fx_tty_set_mode(tty, FX_TTY_CANONICAL);
fputc('\n', stdout);
if (*ed->l_buf == '\0') {
return eof ? -1 : 0;
}
if (append_history) {
ed->l_history_pos = append_to_index;
append_buf_to_history(ed);
} else {
ed->l_history_pos = fx_array_get_size(ed->l_history) - 1;
const char *last = last_history_line(ed);
if (!last || fx_wastrcmp(last, ed->l_buf) != 0) {
save_buf_to_history(ed);
}
}
size_t sz = ed->l_line_end - ed->l_buf + 1;
if ((ed->l_flags & LINE_ED_REMOVE_CONTINUATIONS)
== LINE_ED_REMOVE_CONTINUATIONS) {
remove_continuation_feeds(ed->l_buf, sz);
} else if (
(ed->l_flags & LINE_ED_CONVERT_CONTINUATIONS)
== LINE_ED_CONVERT_CONTINUATIONS) {
convert_continuation_feeds(ed->l_buf, sz);
}
fx_stream_write_wstr(out, ed->l_buf, NULL);
fx_stream_write_char(out, '\n');
return sz;
}
+107
View File
@@ -0,0 +1,107 @@
#ifndef LINE_ED_H_
#define LINE_ED_H_
#define LINE_MAX 4096
#include "../line-source.h"
#include <fx/collections/array.h>
#include <fx/queue.h>
#include <fx/term/tty.h>
#include <stddef.h>
struct s_tty;
struct fx_tty_vmode;
struct line_ed;
struct line_ed_hook {
void (*hook_keypress)(
struct line_ed *,
struct line_ed_hook *,
fx_keycode);
void (*hook_buffer_modified)(struct line_ed *, struct line_ed_hook *);
fx_queue_entry hook_entry;
};
enum line_ed_flags {
/* always reprint an entire line when a character is added/deleted.
* without this flag, only the modified character any subsequent
* characters are reprinted. */
LINE_ED_FULL_REPRINT = 0x01u,
/* keep line continuation (backslash-newline) tokens in the output
* buffer (default behaviour) */
LINE_ED_KEEP_CONTINUATIONS = 0x00u,
/* convert line continuation tokens in the output buffer to simple
* linefeeds. */
LINE_ED_CONVERT_CONTINUATIONS = 0x02u,
/* remove line continuation tokens from the output buffer, so that all
* chars are on a single line */
LINE_ED_REMOVE_CONTINUATIONS = 0x06u,
};
struct line_ed {
enum line_ed_flags l_flags;
/* array of basic prompt strings */
const char *l_prompt[2];
/* input buffer, pointer to the buffer cell that corresponds to
* the current cursor position, and pointer to the byte AFTER the last
* usable byte in the buffer */
fx_wchar *l_buf, *l_buf_ptr, *l_buf_end;
/* pointer to the byte AFTER the last byte of the user's input */
fx_wchar *l_line_end;
/* 2-dimensional coordinates of the current cursor position.
* this does NOT include any prompts that are visible on the terminal */
size_t l_cursor_x, l_cursor_y;
/* the number of line continuations that have been inputted */
unsigned int l_continuations;
struct line_source l_line_source;
/* pointer to tty interface */
fx_tty *l_tty;
/* the lexical scope that we are currently in.
* this is provided by components further up the input pipeline,
* for example, when we are inside a string or if-statement. */
const char *l_scope_type;
/* array of previously entered commands */
fx_array *l_history;
/* index of the currently selected history entry */
size_t l_history_pos;
/* list of defined highlight ranges */
fx_queue l_hl_ranges;
/* list of installed hooks */
fx_queue l_hooks;
};
extern struct line_ed *line_ed_create(void);
extern void line_ed_destroy(struct line_ed *ed);
extern void line_ed_set_flags(struct line_ed *ed, enum line_ed_flags flags);
extern void line_ed_set_scope_type(struct line_ed *ed, const char *scope_type);
extern void line_ed_put_highlight(
struct line_ed *ed,
unsigned long start_x,
unsigned long start_y,
unsigned long end_x,
unsigned long end_y,
const struct fx_tty_vmode *vmode);
extern void line_ed_clear_highlights(struct line_ed *ed);
extern void line_ed_print_highlights(struct line_ed *ed);
extern void line_ed_add_hook(struct line_ed *ed, struct line_ed_hook *hook);
extern void line_ed_remove_hook(struct line_ed *ed, struct line_ed_hook *hook);
extern size_t line_ed_readline(struct line_ed *ed, fx_stringstream *out);
static inline struct line_source *line_ed_to_line_source(struct line_ed *ed)
{
return &ed->l_line_source;
}
#endif
+27
View File
@@ -0,0 +1,27 @@
#include <stdio.h>
#include "line-ed.h"
#include "prompt.h"
void show_prompt(struct line_ed *ed)
{
int type = PROMPT_MAIN;
if (ed->l_scope_type) {
type = PROMPT_CONT;
/* this is a temporary solution to show the current
* scope type, until prompts are implemented properly. */
fputs(ed->l_scope_type, stdout);
}
if (ed->l_continuations > 0) {
type = PROMPT_CONT;
}
fputs(ed->l_prompt[type], stdout);
fflush(stdout);
}
size_t prompt_length(struct line_ed *ed, int prompt_id)
{
return strlen(ed->l_prompt[prompt_id]);
}
+12
View File
@@ -0,0 +1,12 @@
#ifndef LINE_ED_PROMPT_H_
#define LINE_ED_PROMPT_H_
#define PROMPT_MAIN 0
#define PROMPT_CONT 1
struct line_ed;
extern void show_prompt(struct line_ed *ed);
extern size_t prompt_length(struct line_ed *ed, int prompt_id);
#endif
+266
View File
@@ -0,0 +1,266 @@
#include "refresh.h"
#include "buffer.h"
#include "cursor.h"
#include "line-ed.h"
#include <fx/term/tty.h>
#include <fx/wstr.h>
#include <stdio.h>
#include <stdlib.h>
/* prints the provided string to the terminal, applying any relevant highlight
* ranges. this function prints all characters in `s` until it encounters a null
* char (\0) or linefeed (\n).
*
* the (x, y) coordinates provided should be the data coordinates of the
* first character in `s`.
*/
void print_text(struct line_ed *ed, size_t x, size_t y, const fx_wchar *s)
{
fx_tty *tty = ed->l_tty;
for (size_t i = 0; s[i] != '\n' && s[i] != '\0'; i++) {
fx_stream_write_char(fx_stdout, s[i]);
}
}
void print_buffer(struct line_ed *ed)
{
const fx_wchar *line_buf = ed->l_buf;
size_t line_len = fx_wstrcspn(line_buf, L"\n");
unsigned int y = 0;
while (1) {
print_text(ed, 0, y, line_buf);
line_buf += line_len;
if (*line_buf == '\n') {
line_buf++;
}
if (*line_buf == '\0') {
break;
}
y++;
line_len = fx_wstrcspn(line_buf, L"\n");
fputc('\r', stdout);
fputc('\n', stdout);
}
}
/* this function is called after a character is inserted into the line_ed
*buffer. the function performs the following steps:
* 1. get a pointer to the start of the line that was modified.
* 2. determine the first character in the line that needs to be redrawn.
* this may result in an append, a partial reprint, or a full reprint.
* 3. re-print the relevant portion of the buffer:
* for an append (a character added to the end of the line):
* * write the inserted char to the terminal.
* * done.
* for a partial reprint:
* * clear all printed chars from the logical cursor position to
*the end of the line.
* * print the portion of the line corresponding to the cleared
*section.
* * move the physical (terminal) cursor backwards until its
*position matches the logical (line_ed) cursor. for a full reprint:
* * same as a partial reprint except that, rather than reprinting
* from the logical cursor position, the entire line is
*reprinted.
*/
void put_refresh(struct line_ed *ed, struct refresh_state *state)
{
/* get the data for the line that is being refreshed */
const fx_wchar *line_buf = line_start(ed, ed->l_cursor_y);
size_t line_len = fx_wstrcspn(line_buf, L"\n");
/* the index of the first char in line_buf that needs to be reprinted */
size_t start_x = state->r_prev_cursor_x;
/* the distance between the first char to be reprinted and the end
* of the line.
* the physical cursor will be moved back by this amount after the
* line is reprinted. */
long cursor_rdelta = (long)(line_len - start_x);
if (ed->l_flags & LINE_ED_FULL_REPRINT) {
if (start_x) {
// fprintf(stdout, "\033[%uD", start_x);
fx_tty_move_cursor_x(
ed->l_tty,
FX_TTY_POS_CURSOR,
-(long long)start_x);
}
start_x = 0;
}
print_text(ed, start_x, ed->l_cursor_y, line_buf + start_x);
/* decrement the rdelta (move the cursor back one fewer cells),
* so that the physical cursor will be placed AFTER the character that
* was just inserted. */
cursor_rdelta--;
fx_tty_move_cursor_x(ed->l_tty, FX_TTY_POS_CURSOR, -cursor_rdelta);
#if 0
for (unsigned int i = 0; i < cursor_rdelta; i++) {
fputs("\010", stdout);
}
#endif
fflush(stdout);
}
/* this function is called after a character is removed from the line_ed buffer.
* IF the character was a linefeed.
*
* this is separate from backspace_simple_refresh because, in this situation,
* the cursor position depends on the length of the previous line before
* the linefeed was deleted, and we have to reprint every line following the
* two that were combined.
*/
void backspace_nl_refresh(struct line_ed *ed, struct refresh_state *state)
{
/* get the data for the line that is being refreshed.
* relative to where the cursor was before the linefeed was deleted,
* this is the PREVIOUS line. */
const fx_wchar *line_buf = line_start(ed, ed->l_cursor_y);
size_t line_len = fx_wstrcspn(line_buf, L"\n");
/* the index of the first char in line_buf that needs to be reprinted.
* subtract one to account for the linefeed that has since been deleted.
*/
size_t start_x = state->r_prev_line_len - 1;
/* the column to move the physical cursor to after it has been moved
* to the previous line.
* NOTE that this number includes the length of the prompt!
* we add 1 to start_x to ensure that the cursor is moved to the cell
* AFTER the last char of the line. */
size_t new_x;
line_ed_coords_to_physical_coords(
ed,
start_x + 1,
ed->l_cursor_y,
&new_x,
NULL);
/* the physical cursor is currently at the beginning of the line that
* has just been moved up. from here, clear this line and the rest
* from the screen. */
// fputs("\033[J", stdout);
fx_tty_clear(ed->l_tty, FX_TTY_CLEAR_SCREEN | FX_TTY_CLEAR_FROM_CURSOR);
if (ed->l_flags & LINE_ED_FULL_REPRINT) {
/* next, move the physical cursor up and to the beginning of the
* previous line */
size_t tmp_x;
line_ed_coords_to_physical_coords(
ed,
0,
ed->l_cursor_y,
&tmp_x,
NULL);
fx_tty_move_cursor_y(ed->l_tty, FX_TTY_POS_CURSOR, -1);
fx_tty_move_cursor_x(ed->l_tty, FX_TTY_POS_START, tmp_x);
// fprintf(stdout, "\033[A\033[%uG", tmp_x + 1);
start_x = 0;
} else {
/* next, move the physical cursor up and to the end of the
* previous line */
// fprintf(stdout, "\033[A\033[%uG", new_x);
fx_tty_move_cursor_y(ed->l_tty, FX_TTY_POS_CURSOR, -1);
fx_tty_move_cursor_x(ed->l_tty, FX_TTY_POS_START, new_x);
}
/* now reprint all of the buffer lines, starting with the first of the
* two lines that were concatenated. */
size_t ydiff = 0;
while (1) {
print_text(
ed,
start_x,
ed->l_cursor_y + ydiff,
line_buf + start_x);
line_buf += line_len + 1;
line_len = fx_wstrcspn(line_buf, L"\n");
start_x = 0;
if (*line_buf == '\0') {
break;
}
fputc('\r', stdout);
fputc('\n', stdout);
ydiff++;
}
/* finally, move the cursor BACK to the point where the two lines
* were concatenated. */
if (ydiff) {
// fprintf(stdout, "\033[%uA", ydiff);
fx_tty_move_cursor_y(ed->l_tty, FX_TTY_POS_CURSOR, ydiff);
}
// fprintf(stdout, "\033[%uG", new_x);
fx_tty_move_cursor_x(ed->l_tty, FX_TTY_POS_START, new_x);
fflush(stdout);
}
/* this function is called after a character is removed from the line_ed buffer.
* IF the character was not a linefeed.
*/
void backspace_simple_refresh(struct line_ed *ed, struct refresh_state *state)
{
/* get the data for the line that is being refreshed */
const fx_wchar *line_buf = line_start(ed, ed->l_cursor_y);
size_t line_len = fx_wstrcspn(line_buf, L"\n");
/* the index of the first char in line_buf that needs to be reprinted */
size_t start_x = ed->l_cursor_x;
// get_data_cursor_position(ed, &start_x, NULL);
/* the distance between the first char to be reprinted and the end
* of the line.
* the physical cursor will be moved back by this amount after the
* line is reprinted. */
long long cursor_rdelta = (long long)(line_len - start_x);
if (ed->l_flags & LINE_ED_FULL_REPRINT) {
if (start_x) {
// fprintf(stdout, "\033[%uD", start_x);
fx_tty_move_cursor_x(
ed->l_tty,
FX_TTY_POS_CURSOR,
-(long long)start_x);
}
start_x = 0;
}
// fputc('\010', stdout);
fx_tty_move_cursor_x(ed->l_tty, FX_TTY_POS_CURSOR, -1);
print_text(ed, start_x, ed->l_cursor_y, line_buf + start_x);
fputc(' ', stdout);
/* increment the rdelta (move the cursor back one more cell), so
* that the cursor will appear to move back one cell (to accord with
* the fact that backspace was just pressed) */
cursor_rdelta++;
fx_tty_move_cursor_x(ed->l_tty, FX_TTY_POS_CURSOR, -cursor_rdelta);
#if 0
for (unsigned int i = 0; i < cursor_rdelta; i++) {
fputs("\010", stdout);
}
#endif
fflush(stdout);
}
+33
View File
@@ -0,0 +1,33 @@
#ifndef LINE_ED_REFRESH_H_
#define LINE_ED_REFRESH_H_
#include <fx/encoding.h>
#include <stddef.h>
struct line_ed;
struct refresh_state {
/* cursor position before the update was performed (excluding the
* prompt) */
size_t r_prev_cursor_x, r_prev_cursor_y;
/* when a backspace results in two separate lines being combined,
* this property contains the length of the first of the two combined
* lines BEFORE the concotenation was performed */
size_t r_prev_line_len;
};
extern void print_text(
struct line_ed *ed,
size_t x,
size_t y,
const fx_wchar *s);
extern void print_buffer(struct line_ed *ed);
extern void put_refresh(struct line_ed *ed, struct refresh_state *state);
extern void backspace_nl_refresh(
struct line_ed *ed,
struct refresh_state *state);
extern void backspace_simple_refresh(
struct line_ed *ed,
struct refresh_state *state);
#endif
+39
View File
@@ -0,0 +1,39 @@
#include "line-source.h"
enum bshell_status line_source_get_name(
struct line_source *src,
char *buf,
size_t count,
size_t *nr_read)
{
if (src->s_get_name) {
return src->s_get_name(src, buf, count, nr_read);
}
return BSHELL_ERR_NOT_SUPPORTED;
}
enum bshell_status line_source_readline(
struct line_source *src,
fx_stringstream *out)
{
if (src->s_readline) {
return src->s_readline(src, out);
}
return BSHELL_ERR_NOT_SUPPORTED;
}
enum bshell_status line_source_get_row(
struct line_source *src,
size_t row,
char *buf,
size_t count,
size_t *nr_read)
{
if (src->s_get_row) {
return src->s_get_row(src, row, buf, count, nr_read);
}
return BSHELL_ERR_NOT_SUPPORTED;
}
+37
View File
@@ -0,0 +1,37 @@
#ifndef LINE_SOURCE_H_
#define LINE_SOURCE_H_
#include "status.h"
#include <fx/stringstream.h>
#include <stddef.h>
struct line_source {
enum bshell_status (
*s_get_name)(struct line_source *, char *, size_t, size_t *);
enum bshell_status (
*s_readline)(struct line_source *, fx_stringstream *);
enum bshell_status (*s_get_row)(
struct line_source *,
size_t,
char *,
size_t,
size_t *);
};
extern enum bshell_status line_source_get_name(
struct line_source *src,
char *buf,
size_t count,
size_t *nr_read);
extern enum bshell_status line_source_readline(
struct line_source *src,
fx_stringstream *out);
extern enum bshell_status line_source_get_row(
struct line_source *src,
size_t row,
char *buf,
size_t count,
size_t *nr_read);
#endif
+118
View File
@@ -0,0 +1,118 @@
#include "ast/ast.h"
#include "compile.h"
#include "debug.h"
#include "file.h"
#include "format/format.h"
#include "line-ed/line-ed.h"
#include "parse/lex.h"
#include "parse/parse.h"
#include "parse/token.h"
#include "runtime/runtime.h"
#include "verb.h"
#include <stdio.h>
static void print_token_queue(fx_queue *q)
{
fx_queue_entry *cur = fx_queue_first(q);
while (cur) {
struct lex_token *tok = fx_unbox(
struct lex_token,
cur,
tok_entry);
print_lex_token(tok);
cur = fx_queue_next(cur);
}
}
static void lex_only(struct lex_ctx *lex)
{
while (1) {
struct lex_token *tok = lex_ctx_peek(lex);
if (!tok) {
break;
}
lex_ctx_discard(lex);
}
}
static void lex_and_parse(struct parse_ctx *parse)
{
}
extern int compare_token_types(unsigned int a, unsigned int b);
int main(int argc, const char **argv)
{
printf("B Shell " BSHELL_VERSION "\n");
bshell_init_all_verbs();
table_format_init();
struct file *file = NULL;
struct line_ed *ed = NULL;
struct line_source *linesrc = NULL;
enum bshell_status status = BSHELL_SUCCESS;
if (argc > 1) {
status = file_open(argv[1], &file);
linesrc = &file->f_base;
} else {
#if BSHELL_INTERACTIVE == 1
ed = line_ed_create();
linesrc = line_ed_to_line_source(ed);
#else
fprintf(stderr, "Interactive mode is not supported.\n");
return -1;
#endif
}
struct lex_ctx lex = {0};
lex_ctx_init(&lex, LEX_PRINT_TOKENS, linesrc);
struct parse_ctx parse = {0};
parse_ctx_init(&parse, &lex);
#if 0
lex_only(&lex);
return 0;
#endif
bshell_scriptblock *block = bshell_scriptblock_create();
struct bshell_runtime *rt = bshell_runtime_create();
while (1) {
struct ast_node *node = parse_ctx_read_node(&parse);
if (!node) {
break;
}
print_ast_node_recursive(node);
printf("----\n");
bshell_scriptblock_clear_text(block);
bshell_compile(node, block);
print_scriptblock(block, 0);
fx_value result = bshell_runtime_eval_global(rt, block);
if (result.v_type != NULL) {
format_value_default(&result, fx_stdout);
}
}
parse_ctx_cleanup(&parse);
lex_ctx_cleanup(&lex);
#if BSHELL_INTERACTIVE == 1
if (ed) {
line_ed_destroy(ed);
}
#endif
if (file) {
file_close(file);
}
return 0;
}
+7
View File
@@ -0,0 +1,7 @@
#ifndef MISC_H_
#define MISC_H_
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#endif
+268
View File
@@ -0,0 +1,268 @@
#include "operator.h"
#include "parse/token.h"
#define OP(id, p, a, l, u) \
[OP_##id] = { \
.op_id = (OP_##id), \
.op_precedence = (PRECEDENCE_##p), \
.op_associativity = (ASSOCIATIVITY_##a), \
.op_location = (OPL_##l), \
.op_arity = (OPA_##u), \
}
#define TOK_OP(id, tok) [TOK_##tok - __TOK_INDEX_BASE] = &operators[OP_##id]
#define SYM_OP(id, sym) [SYM_##sym - __SYM_INDEX_BASE] = &operators[OP_##id]
#define KW_OP(id, kw) [KW_##kw - __KW_INDEX_BASE] = &operators[OP_##id]
#define TKOP_OP(id, kw) [TKOP_##kw - __TKOP_INDEX_BASE] = &operators[OP_##id]
/* clang-format off */
static const struct operator_info operators[] = {
OP(ACCESS, MEMBER_ACCESS, LEFT, INFIX, BINARY),
OP(CONDITIONAL_ACCESS, MEMBER_ACCESS, LEFT, INFIX, BINARY),
OP(STATIC_ACCESS, STATIC_ACCESS, LEFT, INFIX, BINARY),
OP(SUBSCRIPT, SUBSCRIPT, LEFT, INFIX, BINARY),
OP(CONDITIONAL_SUBSCRIPT, SUBSCRIPT, LEFT, INFIX, BINARY),
OP(CAST, CAST, LEFT, PREFIX, UNARY),
OP(USPLIT, SPLIT, LEFT, PREFIX, UNARY),
OP(UJOIN, JOIN, LEFT, PREFIX, UNARY),
OP(ARRAY_DELIMITER, ARRAY, LEFT, INFIX, BINARY),
OP(INCREMENT, INCREMENT, LEFT, INFIX, BINARY),
OP(LOGICAL_NOT, NOT, LEFT, PREFIX, UNARY),
OP(RANGE, RANGE, LEFT, INFIX, BINARY),
OP(FORMAT, FORMAT, LEFT, INFIX, BINARY),
OP(MULTIPLY, MULTIPLICATION, LEFT, INFIX, BINARY),
OP(DIVIDE, MULTIPLICATION, LEFT, INFIX, BINARY),
OP(MODULO, MULTIPLICATION, LEFT, INFIX, BINARY),
OP(ADD, ADDITION, LEFT, INFIX, BINARY),
OP(SUBTRACT, ADDITION, LEFT, INFIX, BINARY),
OP(BSPLIT, COMPARISON, LEFT, INFIX, BINARY),
OP(BJOIN, COMPARISON, LEFT, INFIX, BINARY),
OP(IS, COMPARISON, LEFT, INFIX, BINARY),
OP(ISNOT, COMPARISON, LEFT, INFIX, BINARY),
OP(AS, COMPARISON, LEFT, INFIX, BINARY),
OP(EQUAL, COMPARISON, LEFT, INFIX, BINARY),
OP(NOT_EQUAL, COMPARISON, LEFT, INFIX, BINARY),
OP(GREATER_THAN, COMPARISON, LEFT, INFIX, BINARY),
OP(LESS_THAN, COMPARISON, LEFT, INFIX, BINARY),
OP(GREATER_EQUAL, COMPARISON, LEFT, INFIX, BINARY),
OP(LESS_EQUAL, COMPARISON, LEFT, INFIX, BINARY),
OP(LIKE, COMPARISON, LEFT, INFIX, BINARY),
OP(NOTLIKE, COMPARISON, LEFT, INFIX, BINARY),
OP(MATCH, COMPARISON, LEFT, INFIX, BINARY),
OP(NOTMATCH, COMPARISON, LEFT, INFIX, BINARY),
OP(IN, COMPARISON, LEFT, INFIX, BINARY),
OP(NOTIN, COMPARISON, LEFT, INFIX, BINARY),
OP(CONTAINS, COMPARISON, LEFT, INFIX, BINARY),
OP(NOTCONTAINS, COMPARISON, LEFT, INFIX, BINARY),
OP(REPLACE, COMPARISON, LEFT, INFIX, BINARY),
OP(LOGICAL_AND, LOGICAL, LEFT, INFIX, BINARY),
OP(LOGICAL_OR, LOGICAL, LEFT, INFIX, BINARY),
OP(LOGICAL_XOR, LOGICAL, LEFT, INFIX, BINARY),
OP(BINARY_AND, BITWISE, LEFT, INFIX, BINARY),
OP(BINARY_OR, BITWISE, LEFT, INFIX, BINARY),
OP(BINARY_NOT, BITWISE, LEFT, INFIX, BINARY),
OP(BINARY_XOR, BITWISE, LEFT, INFIX, BINARY),
OP(LEFT_SHIFT, BITWISE, LEFT, INFIX, BINARY),
OP(RIGHT_SHIFT, BITWISE, LEFT, INFIX, BINARY),
OP(ASSIGN, ASSIGN, RIGHT, INFIX, BINARY),
OP(ADD_ASSIGN, ASSIGN, RIGHT, INFIX, BINARY),
OP(SUBTRACT_ASSIGN, ASSIGN, RIGHT, INFIX, BINARY),
OP(MULTIPLY_ASSIGN, ASSIGN, RIGHT, INFIX, BINARY),
OP(DIVIDE_ASSIGN, ASSIGN, RIGHT, INFIX, BINARY),
OP(MODULO_ASSIGN, ASSIGN, RIGHT, INFIX, BINARY),
/* these are not real operators, and are just used internally by the
* parser. */
OP(SUBEXPR, PARENTHESIS, LEFT, PREFIX, UNARY),
OP(ARRAY_START, PARENTHESIS, LEFT, PREFIX, UNARY),
OP(PAREN, PARENTHESIS, LEFT, PREFIX, UNARY),
OP(HASHTABLE_START, PARENTHESIS, LEFT, PREFIX, UNARY),
OP(SCRIPTBLOCK, PARENTHESIS, LEFT, PREFIX, UNARY),
};
static const size_t nr_operators = sizeof operators / sizeof operators[0];
static const struct operator_info *operator_symbols[] = {
SYM_OP(LOGICAL_NOT, BANG),
SYM_OP(ASSIGN, EQUAL),
SYM_OP(ADD, PLUS),
SYM_OP(SUBTRACT, HYPHEN),
SYM_OP(MULTIPLY, ASTERISK),
SYM_OP(DIVIDE, FORWARD_SLASH),
SYM_OP(MODULO, PERCENT),
SYM_OP(ADD_ASSIGN, PLUS_EQUAL),
SYM_OP(SUBTRACT_ASSIGN, HYPHEN_EQUAL),
SYM_OP(MULTIPLY_ASSIGN, ASTERISK_EQUAL),
SYM_OP(DIVIDE_ASSIGN, FORWARD_SLASH_EQUAL),
SYM_OP(MODULO_ASSIGN, PERCENT_EQUAL),
SYM_OP(RANGE, DOT_DOT),
SYM_OP(SUBSCRIPT, LEFT_BRACKET),
SYM_OP(CONDITIONAL_SUBSCRIPT, QUESTION_LEFT_BRACKET),
SYM_OP(ACCESS, DOT),
SYM_OP(CONDITIONAL_ACCESS, QUESTION_DOT),
SYM_OP(STATIC_ACCESS, COLON_COLON),
/* parser-internal pseudo-operators. */
/* CAST uses the same symbol as SUBSCRIPT */
/* SYM_OP(CAST, LEFT_BRACKET), */
SYM_OP(SUBEXPR, DOLLAR_LEFT_PAREN),
SYM_OP(PAREN, LEFT_PAREN),
SYM_OP(ARRAY_START, AT_LEFT_PAREN),
SYM_OP(HASHTABLE_START, AT_LEFT_BRACE),
};
static const size_t nr_operator_symbols = sizeof operator_symbols / sizeof operator_symbols[0];
static const struct operator_info *operator_token_ops[] = {
TKOP_OP(FORMAT, F),
TKOP_OP(BINARY_AND, BAND),
TKOP_OP(BINARY_OR, BOR),
TKOP_OP(BINARY_XOR, BXOR),
TKOP_OP(BINARY_NOT, BNOT),
TKOP_OP(LEFT_SHIFT, SHL),
TKOP_OP(RIGHT_SHIFT, SHR),
TKOP_OP(EQUAL, EQ),
TKOP_OP(NOT_EQUAL, NE),
TKOP_OP(GREATER_THAN, GT),
TKOP_OP(LESS_THAN, LT),
TKOP_OP(GREATER_EQUAL, GE),
TKOP_OP(LESS_EQUAL, LE),
TKOP_OP(MATCH, MATCH),
TKOP_OP(NOTMATCH, NOTMATCH),
TKOP_OP(REPLACE, REPLACE),
TKOP_OP(LIKE, LIKE),
TKOP_OP(NOTLIKE, NOTLIKE),
TKOP_OP(IN, IN),
TKOP_OP(NOTIN, NOTIN),
TKOP_OP(CONTAINS, CONTAINS),
TKOP_OP(NOTCONTAINS, NOTCONTAINS),
TKOP_OP(LOGICAL_AND, AND),
TKOP_OP(LOGICAL_OR, OR),
TKOP_OP(LOGICAL_XOR, XOR),
TKOP_OP(LOGICAL_NOT, NOT),
/* there are also unary versions of these operators */
TKOP_OP(BSPLIT, SPLIT),
TKOP_OP(BJOIN, JOIN),
TKOP_OP(IS, IS),
TKOP_OP(ISNOT, ISNOT),
TKOP_OP(AS, AS),
};
static const size_t nr_operator_token_ops = sizeof operator_token_ops / sizeof operator_token_ops[0];
/* clang-format on */
const struct operator_info *operator_get_by_token(unsigned int token)
{
const struct operator_info **op_list = NULL;
size_t base = 0;
size_t op_list_size = 0;
if (token > __TKOP_INDEX_BASE && token < __TKOP_INDEX_LIMIT) {
op_list = operator_token_ops;
base = __TKOP_INDEX_BASE;
op_list_size = nr_operator_token_ops;
} else if (token > __SYM_INDEX_BASE && token < __SYM_INDEX_LIMIT) {
op_list = operator_symbols;
base = __SYM_INDEX_BASE;
op_list_size = nr_operator_symbols;
} else {
return NULL;
}
if (token - base >= op_list_size) {
return NULL;
}
return op_list[token - base];
}
const struct operator_info *operator_get_by_id(enum operator_id id)
{
if (id >= nr_operators) {
return NULL;
}
const struct operator_info *op = &operators[id];
if (op->op_id != id) {
return NULL;
}
return op;
}
#define ENUM_STR(x) \
case x: \
return #x
const char *operator_id_to_string(enum operator_id op)
{
switch (op) {
ENUM_STR(OP_NONE);
ENUM_STR(OP_ADD);
ENUM_STR(OP_SUBTRACT);
ENUM_STR(OP_MULTIPLY);
ENUM_STR(OP_DIVIDE);
ENUM_STR(OP_MODULO);
ENUM_STR(OP_INCREMENT);
ENUM_STR(OP_DECREMENT);
ENUM_STR(OP_LEFT_SHIFT);
ENUM_STR(OP_RIGHT_SHIFT);
ENUM_STR(OP_BINARY_AND);
ENUM_STR(OP_BINARY_OR);
ENUM_STR(OP_BINARY_XOR);
ENUM_STR(OP_BINARY_NOT);
ENUM_STR(OP_LESS_THAN);
ENUM_STR(OP_GREATER_THAN);
ENUM_STR(OP_EQUAL);
ENUM_STR(OP_NOT_EQUAL);
ENUM_STR(OP_LESS_EQUAL);
ENUM_STR(OP_GREATER_EQUAL);
ENUM_STR(OP_ASSIGN);
ENUM_STR(OP_ADD_ASSIGN);
ENUM_STR(OP_SUBTRACT_ASSIGN);
ENUM_STR(OP_MULTIPLY_ASSIGN);
ENUM_STR(OP_DIVIDE_ASSIGN);
ENUM_STR(OP_MODULO_ASSIGN);
ENUM_STR(OP_LOGICAL_AND);
ENUM_STR(OP_LOGICAL_OR);
ENUM_STR(OP_LOGICAL_XOR);
ENUM_STR(OP_LOGICAL_NOT);
ENUM_STR(OP_RANGE);
ENUM_STR(OP_MATCH);
ENUM_STR(OP_NOTMATCH);
ENUM_STR(OP_REPLACE);
ENUM_STR(OP_LIKE);
ENUM_STR(OP_NOTLIKE);
ENUM_STR(OP_IN);
ENUM_STR(OP_NOTIN);
ENUM_STR(OP_FORMAT);
ENUM_STR(OP_CONTAINS);
ENUM_STR(OP_NOTCONTAINS);
ENUM_STR(OP_USPLIT);
ENUM_STR(OP_BSPLIT);
ENUM_STR(OP_UJOIN);
ENUM_STR(OP_BJOIN);
ENUM_STR(OP_IS);
ENUM_STR(OP_ISNOT);
ENUM_STR(OP_AS);
ENUM_STR(OP_SUBSCRIPT);
ENUM_STR(OP_CONDITIONAL_SUBSCRIPT);
ENUM_STR(OP_ARRAY_DELIMITER);
ENUM_STR(OP_ACCESS);
ENUM_STR(OP_STATIC_ACCESS);
ENUM_STR(OP_CONDITIONAL_ACCESS);
ENUM_STR(OP_CAST);
ENUM_STR(OP_SUBEXPR);
ENUM_STR(OP_PAREN);
ENUM_STR(OP_ARRAY_START);
ENUM_STR(OP_HASHTABLE_START);
default:
return "";
}
}
+124
View File
@@ -0,0 +1,124 @@
#ifndef OPERATOR_H_
#define OPERATOR_H_
enum operator_precedence {
PRECEDENCE_MINIMUM = 0,
PRECEDENCE_ASSIGN,
PRECEDENCE_PIPELINE,
PRECEDENCE_LOGICAL,
PRECEDENCE_BITWISE,
PRECEDENCE_COMPARISON,
PRECEDENCE_ADDITION,
PRECEDENCE_MULTIPLICATION,
PRECEDENCE_NEGATE,
PRECEDENCE_FORMAT,
PRECEDENCE_RANGE,
PRECEDENCE_NOT,
PRECEDENCE_INCREMENT,
PRECEDENCE_ARRAY,
PRECEDENCE_JOIN,
PRECEDENCE_SPLIT,
PRECEDENCE_CAST,
PRECEDENCE_SUBSCRIPT,
PRECEDENCE_STATIC_ACCESS,
PRECEDENCE_MEMBER_ACCESS,
PRECEDENCE_PARENTHESIS,
};
enum operator_associativity {
ASSOCIATIVITY_LEFT,
ASSOCIATIVITY_RIGHT,
};
enum operator_location {
OPL_PREFIX,
OPL_INFIX,
OPL_POSTFIX,
};
enum operator_arity {
OPA_UNARY,
OPA_BINARY,
};
enum operator_id {
OP_NONE = 0,
OP_ADD,
OP_SUBTRACT,
OP_MULTIPLY,
OP_DIVIDE,
OP_MODULO,
OP_INCREMENT,
OP_DECREMENT,
OP_LEFT_SHIFT,
OP_RIGHT_SHIFT,
OP_BINARY_AND,
OP_BINARY_OR,
OP_BINARY_XOR,
OP_BINARY_NOT,
OP_LESS_THAN,
OP_GREATER_THAN,
OP_EQUAL,
OP_NOT_EQUAL,
OP_LESS_EQUAL,
OP_GREATER_EQUAL,
OP_ASSIGN,
OP_ADD_ASSIGN,
OP_SUBTRACT_ASSIGN,
OP_MULTIPLY_ASSIGN,
OP_DIVIDE_ASSIGN,
OP_MODULO_ASSIGN,
OP_LOGICAL_AND,
OP_LOGICAL_OR,
OP_LOGICAL_XOR,
OP_LOGICAL_NOT,
OP_RANGE,
OP_MATCH,
OP_NOTMATCH,
OP_REPLACE,
OP_LIKE,
OP_NOTLIKE,
OP_IN,
OP_NOTIN,
OP_FORMAT,
OP_CONTAINS,
OP_NOTCONTAINS,
OP_USPLIT,
OP_BSPLIT,
OP_UJOIN,
OP_BJOIN,
OP_IS,
OP_ISNOT,
OP_AS,
OP_SUBSCRIPT,
OP_CONDITIONAL_SUBSCRIPT,
OP_ARRAY_DELIMITER,
OP_ACCESS,
OP_STATIC_ACCESS,
OP_CONDITIONAL_ACCESS,
/* these are not real operators, and are just used internally by the
* parser. */
OP_CAST,
OP_SUBEXPR,
OP_PAREN,
OP_SCRIPTBLOCK,
OP_ARRAY_START,
OP_HASHTABLE_START,
};
struct operator_info {
enum operator_id op_id;
enum operator_precedence op_precedence;
enum operator_associativity op_associativity;
enum operator_location op_location;
enum operator_arity op_arity;
};
extern const struct operator_info *operator_get_by_id(enum operator_id id);
extern const struct operator_info *operator_get_by_token(unsigned int token);
extern const char *operator_id_to_string(enum operator_id op);
#endif
+94
View File
@@ -0,0 +1,94 @@
#ifndef LEX_H_
#define LEX_H_
#include "../status.h"
#include "token.h"
#include <fx/queue.h>
#include <fx/string.h>
#include <fx/stringstream.h>
#define LEX_STATE_MAX_TERMINATORS 16
struct line_source;
enum lex_flags {
LEX_PRINT_TOKENS = 0x01u,
};
enum lex_token_flags {
/* a token with this flag not only interrupts the word currently being
* scanned, but also stops multi-words */
LEX_TOKEN_TERMINATES_WORD = 0x01u,
/* a token with this flag can appear at the start of an arithmetic
* expression. a statement that encounters this token as its first char
* will switch to arithmetic mode */
LEX_TOKEN_UNARY_ARITHMETIC = 0x02u,
/* if a token has this flag defined, the lexer will
* switch to command mode after encountering it. */
LEX_TOKEN_COMMAND_MODE = 0x08u,
/* if a token has this flag defined, the lexer will
* switch to statement mode after encountering it. */
LEX_TOKEN_STATEMENT_MODE = 0x10u,
};
enum lex_state_type_id {
LEX_STATE_STATEMENT = 0x01u,
LEX_STATE_COMMAND = 0x02u,
LEX_STATE_ARITHMETIC = 0x04u,
LEX_STATE_STRING = 0x08u,
LEX_STATE_WORD = 0x10u,
LEX_STATE_HASHTABLE = 0x20u,
};
struct lex_token_def {
int id;
const char *name;
uint64_t name_hash;
enum lex_state_type_id enabled_states;
enum lex_token_flags flags;
};
struct lex_symbol_node {
char s_char;
struct lex_token_def *s_def;
fx_queue_entry s_entry;
fx_queue s_children;
};
struct lex_state {
const struct lex_state_type *s_type;
unsigned int s_terminators[LEX_STATE_MAX_TERMINATORS];
unsigned int s_nr_terminators;
unsigned int s_paren_depth;
fx_queue_entry s_entry;
fx_string *s_tempstr;
unsigned int s_flags;
};
struct lex_ctx {
enum lex_flags lex_flags;
fx_queue lex_tokens;
struct line_source *lex_src;
fx_stringstream *lex_buf;
fx_string *lex_tmp;
fx_wchar lex_ch;
fx_queue lex_state;
enum token_type lex_prev_token;
struct char_cell lex_cursor, lex_start, lex_end;
struct lex_symbol_node *lex_sym_tree;
enum bshell_status lex_status;
};
extern enum bshell_status lex_ctx_init(
struct lex_ctx *ctx,
enum lex_flags flags,
struct line_source *src);
extern enum bshell_status lex_ctx_cleanup(struct lex_ctx *ctx);
extern struct lex_token *lex_ctx_peek(struct lex_ctx *ctx);
extern struct lex_token *lex_ctx_claim(struct lex_ctx *ctx);
extern void lex_ctx_discard(struct lex_ctx *ctx);
#endif
+232
View File
@@ -0,0 +1,232 @@
#include "lex-internal.h"
static enum bshell_status arithmetic_hyphen(struct lex_ctx *ctx)
{
fx_wchar c = peek_char(ctx);
if (!fx_wchar_is_alnum(c)) {
push_symbol(ctx, SYM_HYPHEN);
handle_lex_state_transition(ctx, SYM_HYPHEN);
return BSHELL_SUCCESS;
}
struct lex_token *tok = NULL;
enum bshell_status status = read_word(
ctx,
READ_NO_SET_TOKEN_START | READ_APPEND_HYPHEN,
&tok);
if (status != BSHELL_SUCCESS) {
return status;
}
unsigned int token_type = TOK_WORD;
if (convert_word_to_number(tok)) {
token_type = tok->tok_type;
/* because of APPEND_HYPHEN (which is needed to ensure operator
* tokens are detected properly), the resulting number will be
* negative.
* this token will be preceded by a HYPHEN token, so the number
* must be positive */
fx_value neg = FX_INT(-1);
fx_value_multiply(&tok->tok_number, &neg, &tok->tok_number);
push_symbol(ctx, SYM_HYPHEN);
} else if (convert_word_to_operator(ctx, tok)) {
token_type = tok->tok_operator;
}
enqueue_token(ctx, tok);
return BSHELL_SUCCESS;
}
static enum bshell_status arithmetic_symbol(struct lex_ctx *ctx)
{
const struct lex_token_def *sym = NULL;
enum bshell_status status = read_symbol(ctx, &sym);
if (status != BSHELL_SUCCESS) {
return status;
}
handle_lex_state_transition(ctx, sym->id);
struct lex_token *tok = NULL;
switch (sym->id) {
case SYM_DQUOTE:
return BSHELL_SUCCESS;
case SYM_SQUOTE:
status = read_literal_string(ctx, &tok);
if (status != BSHELL_SUCCESS) {
return status;
}
enqueue_token(ctx, tok);
return BSHELL_SUCCESS;
case SYM_HYPHEN:
return arithmetic_hyphen(ctx);
case SYM_HASH:
return read_line_comment(ctx);
case SYM_DOLLAR:
status = read_var(ctx, TOK_VAR, &tok);
if (status != BSHELL_SUCCESS) {
return status;
}
enqueue_token(ctx, tok);
return status;
case SYM_AT:
status = read_var(ctx, TOK_VAR_SPLAT, &tok);
if (status != BSHELL_SUCCESS) {
return status;
}
enqueue_token(ctx, tok);
return status;
case SYM_DOLLAR_LEFT_BRACE:
status = read_braced_var(ctx, TOK_VAR, &tok);
if (status != BSHELL_SUCCESS) {
return status;
}
enqueue_token(ctx, tok);
return status;
default:
break;
}
push_symbol(ctx, sym->id);
return BSHELL_SUCCESS;
}
static enum bshell_status arithmetic_word(struct lex_ctx *ctx)
{
struct lex_token *word = NULL;
enum bshell_status status = read_word(ctx, 0, &word);
if (status != BSHELL_SUCCESS) {
return status;
}
unsigned int token_type = TOK_WORD;
bool kw = false, number = false;
if (convert_word_to_keyword(word)) {
token_type = word->tok_keyword;
} else if (convert_word_to_number(word)) {
token_type = word->tok_type;
}
handle_lex_state_transition(ctx, token_type);
enqueue_token(ctx, word);
return BSHELL_SUCCESS;
}
static enum bshell_status arithmetic_pump_token(struct lex_ctx *ctx)
{
fx_wchar c = peek_char(ctx);
bool newline = false;
set_token_start(ctx);
while (fx_wchar_is_space(c)) {
if (c == '\n') {
newline = true;
}
set_token_end(ctx);
advance_char_noread(ctx);
c = peek_char_noread(ctx);
}
if (newline) {
struct lex_token *tok = lex_token_create(TOK_LINEFEED);
enqueue_token(ctx, tok);
handle_lex_state_transition(ctx, TOK_LINEFEED);
return BSHELL_SUCCESS;
}
if (char_can_begin_symbol(ctx, c)) {
return arithmetic_symbol(ctx);
}
return arithmetic_word(ctx);
}
static const struct lex_state_link links[] = {
LINK_CHANGE(SYM_EQUAL, LEX_STATE_STATEMENT),
LINK_PUSH(SYM_DQUOTE, LEX_STATE_STRING, 0),
LINK_PUSH(SYM_DOLLAR_LEFT_PAREN, LEX_STATE_STATEMENT, 0),
LINK_POP(SYM_RIGHT_PAREN),
LINK_CHANGE(SYM_SEMICOLON, LEX_STATE_STATEMENT),
LINK_CHANGE(TOK_LINEFEED, LEX_STATE_STATEMENT),
LINK_CHANGE(SYM_PIPE, LEX_STATE_STATEMENT),
LINK_PUSH(SYM_AT_LEFT_BRACE, LEX_STATE_HASHTABLE, 0),
LINK_PUSH(
SYM_LEFT_PAREN,
LEX_STATE_STATEMENT,
STATEMENT_F_DISABLE_KEYWORDS),
LINK_END,
};
static const unsigned int keywords[] = {
KW_IF,
KW_ELSEIF,
KW_ELSE,
KW_NONE,
};
static const unsigned int operators[] = {
TKOP_F, TKOP_BAND, TKOP_BOR, TKOP_BXOR,
TKOP_BNOT, TKOP_SHL, TKOP_SHR, TKOP_EQ,
TKOP_NE, TKOP_GT, TKOP_LT, TKOP_GE,
TKOP_LE, TKOP_MATCH, TKOP_NOTMATCH, TKOP_REPLACE,
TKOP_LIKE, TKOP_NOTLIKE, TKOP_IN, TKOP_NOTIN,
TKOP_CONTAINS, TKOP_NOTCONTAINS, TKOP_AND, TKOP_OR,
TKOP_XOR, TKOP_NOT, TKOP_SPLIT, TKOP_JOIN,
TKOP_IS, TKOP_ISNOT, TKOP_AS, TKOP_NONE,
};
static const unsigned int symbols[] = {
SYM_BANG,
SYM_PLUS,
SYM_HYPHEN,
SYM_FORWARD_SLASH,
SYM_ASTERISK,
SYM_AMPERSAND,
SYM_PERCENT,
SYM_SQUOTE,
SYM_DQUOTE,
SYM_HASH,
SYM_DOLLAR,
SYM_DOLLAR_LEFT_PAREN,
SYM_DOLLAR_LEFT_BRACE,
SYM_AT,
SYM_AT_LEFT_BRACE,
SYM_PIPE,
SYM_COMMA,
SYM_SEMICOLON,
SYM_LEFT_PAREN,
SYM_RIGHT_PAREN,
SYM_LEFT_BRACE,
SYM_RIGHT_BRACE,
SYM_LEFT_BRACKET,
SYM_RIGHT_BRACKET,
SYM_QUESTION_DOT,
SYM_QUESTION_LEFT_BRACKET,
SYM_EQUAL,
SYM_PLUS_EQUAL,
SYM_HYPHEN_EQUAL,
SYM_FORWARD_SLASH_EQUAL,
SYM_ASTERISK_EQUAL,
SYM_PERCENT_EQUAL,
SYM_DOT,
SYM_DOT_DOT,
SYM_COLON_COLON,
SYM_NONE,
};
const struct lex_state_type lex_arithmetic_state = {
.s_id = LEX_STATE_ARITHMETIC,
.s_pump_token = arithmetic_pump_token,
.s_links = links,
.s_keywords = keywords,
.s_operators = operators,
.s_symbols = symbols,
};
+226
View File
@@ -0,0 +1,226 @@
#include "../token.h"
#include "lex-internal.h"
static bool char_can_continue_word(struct lex_ctx *ctx, fx_wchar c)
{
if (fx_wchar_is_alnum(c)) {
return true;
}
if (fx_wchar_is_space(c)) {
return false;
}
if (c == '$') {
return true;
}
if (char_can_begin_symbol_in_state(ctx, c, LEX_STATE_WORD)) {
return false;
}
return true;
}
static enum bshell_status command_symbol(struct lex_ctx *ctx)
{
const struct lex_token_def *sym = NULL;
enum bshell_status status = read_symbol(ctx, &sym);
if (status != BSHELL_SUCCESS) {
return status;
}
handle_lex_state_transition(ctx, sym->id);
struct lex_token *tok = NULL;
switch (sym->id) {
case SYM_DQUOTE:
return BSHELL_SUCCESS;
case SYM_SQUOTE:
status = read_literal_string(ctx, &tok);
if (status != BSHELL_SUCCESS) {
return status;
}
enqueue_token(ctx, tok);
return BSHELL_SUCCESS;
case SYM_HASH:
return read_line_comment(ctx);
case SYM_DOLLAR:
status = read_var(ctx, TOK_VAR, &tok);
if (status != BSHELL_SUCCESS) {
return status;
}
if (char_can_continue_word(ctx, peek_char(ctx))) {
lex_state_push(ctx, LEX_STATE_WORD, 0);
}
enqueue_token(ctx, tok);
return status;
case SYM_AT:
status = read_var(ctx, TOK_VAR_SPLAT, &tok);
if (status != BSHELL_SUCCESS) {
return status;
}
enqueue_token(ctx, tok);
return status;
case SYM_DOLLAR_LEFT_BRACE:
status = read_braced_var(ctx, TOK_VAR, &tok);
if (status != BSHELL_SUCCESS) {
return status;
}
if (char_can_continue_word(ctx, peek_char(ctx))) {
lex_state_push(ctx, LEX_STATE_WORD, 0);
}
enqueue_token(ctx, tok);
return status;
default:
break;
}
push_symbol(ctx, sym->id);
return BSHELL_SUCCESS;
}
static bool string_is_redirection(const char *s)
{
if (!*s) {
return false;
}
if (!strcmp(s, ">") || !strcmp(s, ">>")) {
return true;
}
long nr_angles = 0;
for (size_t i = 0; s[i];) {
fx_wchar c = fx_wchar_utf8_codepoint_decode(s);
if (fx_wchar_is_number(c)) {
if (nr_angles) {
return false;
}
} else if (c == '>') {
nr_angles++;
if (nr_angles > 2) {
return false;
}
} else {
return false;
}
s += fx_wchar_utf8_codepoint_stride(s);
}
return true;
}
static enum bshell_status command_word(struct lex_ctx *ctx)
{
struct lex_token *word = NULL;
enum bshell_status status
= read_word(ctx, READ_NO_NUMBER_RECOGNITION, &word);
if (status != BSHELL_SUCCESS) {
return status;
}
bool continue_word = false;
fx_wchar c = peek_char(ctx);
const char *s = word->tok_str;
if (char_can_begin_symbol_in_state(ctx, c, LEX_STATE_WORD)) {
continue_word = true;
}
if (char_has_flags(ctx, c, LEX_TOKEN_TERMINATES_WORD)) {
continue_word = false;
}
if (string_is_redirection(s)) {
continue_word = false;
}
if (continue_word) {
lex_state_push(ctx, LEX_STATE_WORD, 0);
}
enqueue_token(ctx, word);
return BSHELL_SUCCESS;
}
enum bshell_status command_pump_token(struct lex_ctx *ctx)
{
fx_wchar c = peek_char(ctx);
bool newline = false;
set_token_start(ctx);
while (fx_wchar_is_space(c)) {
if (c == '\n') {
newline = true;
}
set_token_end(ctx);
advance_char_noread(ctx);
c = peek_char_noread(ctx);
}
if (newline) {
struct lex_token *tok = lex_token_create(TOK_LINEFEED);
enqueue_token(ctx, tok);
handle_lex_state_transition(ctx, TOK_LINEFEED);
return BSHELL_SUCCESS;
}
if (char_can_begin_symbol(ctx, c)) {
return command_symbol(ctx);
}
return command_word(ctx);
}
const struct lex_state_link links[] = {
LINK_PUSH(SYM_DQUOTE, LEX_STATE_STRING, 0),
LINK_PUSH(
SYM_LEFT_PAREN,
LEX_STATE_STATEMENT,
STATEMENT_F_DISABLE_KEYWORDS),
LINK_PUSH(SYM_DOLLAR_LEFT_PAREN, LEX_STATE_STATEMENT, 0),
LINK_POP(SYM_RIGHT_PAREN),
LINK_POP(SYM_RIGHT_BRACE),
LINK_CHANGE(SYM_SEMICOLON, LEX_STATE_STATEMENT),
LINK_PUSH(SYM_AT_LEFT_BRACE, LEX_STATE_HASHTABLE, 0),
LINK_CHANGE(TOK_LINEFEED, LEX_STATE_STATEMENT),
LINK_END,
};
static const unsigned int symbols[] = {
SYM_DQUOTE,
SYM_SQUOTE,
SYM_DOLLAR,
SYM_DOLLAR_LEFT_PAREN,
SYM_DOLLAR_LEFT_BRACE,
SYM_AT,
SYM_AT_LEFT_BRACE,
SYM_AT_LEFT_PAREN,
SYM_AMPERSAND,
SYM_PIPE,
SYM_SEMICOLON,
SYM_RIGHT_PAREN,
SYM_LEFT_PAREN,
SYM_LEFT_BRACE,
SYM_RIGHT_BRACE,
SYM_NONE,
};
const struct lex_state_type lex_command_state = {
.s_id = LEX_STATE_COMMAND,
.s_pump_token = command_pump_token,
.s_links = links,
.s_symbols = symbols,
};
+185
View File
@@ -0,0 +1,185 @@
#include "lex-internal.h"
static enum bshell_status hashtable_hyphen(struct lex_ctx *ctx)
{
fx_wchar c = peek_char(ctx);
if (!fx_wchar_is_alnum(c)) {
push_symbol(ctx, SYM_HYPHEN);
handle_lex_state_transition(ctx, SYM_HYPHEN);
return BSHELL_SUCCESS;
}
struct lex_token *tok = NULL;
enum bshell_status status = read_word(
ctx,
READ_NO_SET_TOKEN_START | READ_APPEND_HYPHEN,
&tok);
if (status != BSHELL_SUCCESS) {
return status;
}
unsigned int token_type = TOK_WORD;
if (convert_word_to_number(tok)) {
token_type = tok->tok_type;
/* because of APPEND_HYPHEN (which is needed to ensure operator
* tokens are detected properly), the resulting number will be
* negative.
* this token will be preceded by a HYPHEN token, so the number
* must be positive */
fx_value neg = FX_INT(-1);
fx_value_multiply(&tok->tok_number, &neg, &tok->tok_number);
push_symbol(ctx, SYM_HYPHEN);
} else if (convert_word_to_operator(ctx, tok)) {
token_type = tok->tok_operator;
}
handle_lex_state_transition(ctx, token_type);
enqueue_token(ctx, tok);
return BSHELL_SUCCESS;
}
static enum bshell_status hashtable_symbol(struct lex_ctx *ctx)
{
const struct lex_token_def *sym = NULL;
enum bshell_status status = read_symbol(ctx, &sym);
if (status != BSHELL_SUCCESS) {
return status;
}
handle_lex_state_transition(ctx, sym->id);
struct lex_token *tok = NULL;
switch (sym->id) {
case SYM_SQUOTE:
status = read_literal_string(ctx, &tok);
if (status != BSHELL_SUCCESS) {
return status;
}
enqueue_token(ctx, tok);
return BSHELL_SUCCESS;
case SYM_HYPHEN:
return hashtable_hyphen(ctx);
case SYM_HASH:
return read_line_comment(ctx);
case SYM_DOLLAR:
status = read_var(ctx, TOK_VAR, &tok);
if (status != BSHELL_SUCCESS) {
return status;
}
enqueue_token(ctx, tok);
return status;
case SYM_AT:
status = read_var(ctx, TOK_VAR_SPLAT, &tok);
if (status != BSHELL_SUCCESS) {
return status;
}
enqueue_token(ctx, tok);
return status;
case SYM_DOLLAR_LEFT_BRACE:
status = read_braced_var(ctx, TOK_VAR, &tok);
if (status != BSHELL_SUCCESS) {
return status;
}
enqueue_token(ctx, tok);
return status;
default:
break;
}
push_symbol(ctx, sym->id);
return BSHELL_SUCCESS;
}
static enum bshell_status hashtable_word(struct lex_ctx *ctx)
{
struct lex_token *word = NULL;
enum bshell_status status = read_word(ctx, 0, &word);
if (status != BSHELL_SUCCESS) {
return status;
}
convert_word_to_number(word);
handle_lex_state_transition(ctx, word->tok_type);
enqueue_token(ctx, word);
return BSHELL_SUCCESS;
}
static enum bshell_status hashtable_pump_token(struct lex_ctx *ctx)
{
fx_wchar c = peek_char(ctx);
bool newline = false;
set_token_start(ctx);
while (fx_wchar_is_space(c)) {
if (c == '\n') {
newline = true;
}
set_token_end(ctx);
advance_char_noread(ctx);
c = peek_char_noread(ctx);
}
#if 1
if (newline) {
struct lex_token *tok = lex_token_create(TOK_LINEFEED);
enqueue_token(ctx, tok);
return BSHELL_SUCCESS;
}
#endif
if (char_can_begin_symbol(ctx, c)) {
return hashtable_symbol(ctx);
}
return hashtable_word(ctx);
}
static const struct lex_state_link links[] = {
LINK_PUSH_WITH_TERM(
SYM_EQUAL,
LEX_STATE_STATEMENT,
0,
SYM_RIGHT_BRACE,
SYM_SEMICOLON,
TOK_LINEFEED),
LINK_PUSH_WITH_TERM(
TOK_LINEFEED,
LEX_STATE_STATEMENT,
0,
SYM_SEMICOLON,
TOK_LINEFEED),
LINK_PUSH(SYM_DQUOTE, LEX_STATE_STRING, 0),
LINK_PUSH(
SYM_LEFT_PAREN,
LEX_STATE_STATEMENT,
STATEMENT_F_DISABLE_KEYWORDS),
LINK_PUSH(SYM_DOLLAR_LEFT_PAREN, LEX_STATE_STATEMENT, 0),
LINK_POP2(SYM_RIGHT_BRACE, LINK_ALLOW_RECURSION),
LINK_END,
};
static const unsigned int symbols[] = {
SYM_EQUAL,
SYM_DQUOTE,
SYM_SQUOTE,
SYM_SEMICOLON,
SYM_RIGHT_BRACE,
SYM_DOLLAR_LEFT_PAREN,
SYM_LEFT_PAREN,
SYM_HASH,
SYM_NONE,
};
const struct lex_state_type lex_hashtable_state = {
.s_id = LEX_STATE_HASHTABLE,
.s_pump_token = hashtable_pump_token,
.s_links = links,
.s_symbols = symbols,
};
+198
View File
@@ -0,0 +1,198 @@
#ifndef PARSE_LEX_INTERNAL_H_
#define PARSE_LEX_INTERNAL_H_
#include "../../status.h"
#include "../lex.h"
#include "../token.h"
struct lex_ctx;
enum state_flags {
/* statement: don't convert matching words to keywords */
STATEMENT_F_DISABLE_KEYWORDS = 0x01u,
/* arithmetic: don't switch back to statement mode even when
* encountering a token that would otherwise require it. */
ARITHMETIC_F_DISABLE_STATEMENTS = 0x01u,
};
enum read_flags {
READ_APPEND_HYPHEN = 0x01u,
READ_NO_SET_TOKEN_START = 0x02u,
READ_NO_NUMBER_RECOGNITION = 0x04u,
};
enum link_flags {
LINK_ALLOW_RECURSION = 0x01u,
};
#define LINK_PUSH(tok, target, flags) \
((struct lex_state_link) { \
.l_token = (tok), \
.l_type = LEX_STATE_LINK_PUSH, \
.l_target = (target), \
.l_target_flags = (flags), \
})
#define LINK_PUSH_WITH_TERM(tok, target, flags, ...) \
((struct lex_state_link) { \
.l_token = (tok), \
.l_type = LEX_STATE_LINK_PUSH, \
.l_target = (target), \
.l_target_flags = (flags), \
.l_terminators = {__VA_ARGS__, TOK_NONE}, \
})
#define LINK_CHANGE(tok, target) \
((struct lex_state_link) { \
.l_token = (tok), \
.l_type = LEX_STATE_LINK_CHANGE, \
.l_target = (target), \
})
#define LINK_POP(tok) \
((struct lex_state_link) { \
.l_token = (tok), \
.l_type = LEX_STATE_LINK_POP, \
})
#define LINK_POP2(tok, flags) \
((struct lex_state_link) { \
.l_token = (tok), \
.l_type = LEX_STATE_LINK_POP, \
.l_flags = (flags), \
})
#define LINK_NONE(tok) \
((struct lex_state_link) { \
.l_token = (tok), \
.l_type = LEX_STATE_LINK_NONE, \
})
#define LINK_END ((struct lex_state_link) {})
struct lex_state_link {
unsigned int l_token;
enum {
LEX_STATE_LINK_NONE,
LEX_STATE_LINK_PUSH,
LEX_STATE_LINK_CHANGE,
LEX_STATE_LINK_POP,
} l_type;
enum link_flags l_flags;
enum lex_state_type_id l_target;
enum state_flags l_target_flags;
unsigned int l_terminators[LEX_STATE_MAX_TERMINATORS];
};
typedef enum bshell_status (*lex_state_pump_token)(struct lex_ctx *);
typedef enum bshell_status (*lex_state_begin)(struct lex_ctx *);
typedef enum bshell_status (*lex_state_end)(struct lex_ctx *);
struct lex_state_type {
enum lex_state_type_id s_id;
lex_state_pump_token s_pump_token;
lex_state_begin s_begin;
lex_state_end s_end;
const unsigned int *s_keywords;
const unsigned int *s_operators;
const unsigned int *s_symbols;
const struct lex_state_link *s_links;
};
extern enum bshell_status pump_token_statement(struct lex_ctx *ctx);
extern enum bshell_status pump_token_expression(struct lex_ctx *ctx);
extern enum bshell_status pump_token_command(struct lex_ctx *ctx);
extern enum bshell_status pump_token_arithmetic(struct lex_ctx *ctx);
extern enum bshell_status pump_token_string(struct lex_ctx *ctx);
extern void set_token_start(struct lex_ctx *ctx);
extern void set_token_end(struct lex_ctx *ctx);
extern struct lex_state *lex_state_push(
struct lex_ctx *ctx,
enum lex_state_type_id state_type,
enum state_flags flags);
extern void lex_state_pop(struct lex_ctx *ctx);
extern struct lex_state *lex_state_get(struct lex_ctx *ctx);
extern void lex_state_change(struct lex_ctx *ctx, enum lex_state_type_id type);
extern fx_string *lex_state_get_tempstr(struct lex_ctx *ctx);
extern void lex_state_add_terminator(struct lex_state *state, unsigned int tok);
extern bool lex_state_terminates_at_token(
struct lex_ctx *ctx,
unsigned int tok);
extern fx_wchar peek_char(struct lex_ctx *ctx);
extern fx_wchar peek_char_noread(struct lex_ctx *ctx);
extern fx_wchar peek2_char(struct lex_ctx *ctx);
extern fx_wchar peek2_char_noread(struct lex_ctx *ctx);
extern void advance_char(struct lex_ctx *ctx);
extern void advance_char_noread(struct lex_ctx *ctx);
extern bool string_is_valid_number(
const char *s,
fx_value *out,
enum token_type *out_type);
extern bool convert_word_to_number(struct lex_token *tok);
extern bool convert_word_to_keyword(struct lex_token *tok);
extern bool convert_word_to_operator(
struct lex_ctx *ctx,
struct lex_token *tok);
extern void enqueue_token(struct lex_ctx *ctx, struct lex_token *tok);
extern void enqueue_token_with_coordinates(
struct lex_ctx *ctx,
struct lex_token *tok,
const struct char_cell *start,
const struct char_cell *end);
extern enum bshell_status read_word(
struct lex_ctx *ctx,
enum read_flags flags,
struct lex_token **out);
extern enum bshell_status read_symbol(
struct lex_ctx *ctx,
const struct lex_token_def **out);
extern enum bshell_status read_literal_string(
struct lex_ctx *ctx,
struct lex_token **out);
extern enum bshell_status read_line_comment(struct lex_ctx *lex);
extern enum bshell_status read_var(
struct lex_ctx *ctx,
enum token_type type,
struct lex_token **out);
extern enum bshell_status read_braced_var(
struct lex_ctx *ctx,
enum token_type type,
struct lex_token **out);
extern enum bshell_status push_symbol(
struct lex_ctx *ctx,
enum token_symbol sym);
extern bool char_can_begin_symbol(struct lex_ctx *ctx, char c);
extern bool char_can_begin_symbol_in_state(
struct lex_ctx *ctx,
char c,
enum lex_state_type_id state_type);
extern bool char_has_flags(
struct lex_ctx *ctx,
char c,
enum lex_token_flags flags);
extern bool keyword_has_flags(
struct lex_ctx *ctx,
enum token_keyword kw,
enum lex_token_flags flags);
extern enum lex_token_flags keyword_get_flags(
struct lex_ctx *ctx,
enum token_keyword kw);
extern bool symbol_has_flags(
struct lex_ctx *ctx,
enum token_symbol sym,
enum lex_token_flags flags);
extern enum lex_token_flags symbol_get_flags(
struct lex_ctx *ctx,
enum token_symbol sym);
extern enum token_operator get_operator_with_string(
struct lex_ctx *ctx,
const char *s);
extern void handle_lex_state_transition(
struct lex_ctx *ctx,
unsigned int token);
#endif
File diff suppressed because it is too large Load Diff
+254
View File
@@ -0,0 +1,254 @@
#include "lex-internal.h"
static enum bshell_status statement_hyphen(struct lex_ctx *ctx)
{
fx_wchar c = peek_char(ctx);
if (!fx_wchar_is_alnum(c)) {
push_symbol(ctx, SYM_HYPHEN);
handle_lex_state_transition(ctx, SYM_HYPHEN);
return BSHELL_SUCCESS;
}
struct lex_token *tok = NULL;
enum bshell_status status = read_word(
ctx,
READ_NO_SET_TOKEN_START | READ_APPEND_HYPHEN,
&tok);
if (status != BSHELL_SUCCESS) {
return status;
}
unsigned int token_type = TOK_WORD;
if (convert_word_to_number(tok)) {
token_type = tok->tok_type;
/* because of APPEND_HYPHEN (which is needed to ensure operator
* tokens are detected properly), the resulting number will be
* negative.
* this token will be preceded by a HYPHEN token, so the number
* must be positive */
fx_value neg = FX_INT(-1);
fx_value_multiply(&tok->tok_number, &neg, &tok->tok_number);
push_symbol(ctx, SYM_HYPHEN);
} else if (convert_word_to_operator(ctx, tok)) {
token_type = tok->tok_operator;
}
handle_lex_state_transition(ctx, token_type);
enqueue_token(ctx, tok);
return BSHELL_SUCCESS;
}
static enum bshell_status statement_symbol(struct lex_ctx *ctx)
{
const struct lex_token_def *sym = NULL;
enum bshell_status status = read_symbol(ctx, &sym);
if (status != BSHELL_SUCCESS) {
return status;
}
handle_lex_state_transition(ctx, sym->id);
struct lex_token *tok = NULL;
switch (sym->id) {
case SYM_DQUOTE:
return BSHELL_SUCCESS;
case SYM_HYPHEN:
return statement_hyphen(ctx);
case SYM_SQUOTE:
status = read_literal_string(ctx, &tok);
if (status != BSHELL_SUCCESS) {
return status;
}
enqueue_token(ctx, tok);
return BSHELL_SUCCESS;
case SYM_HASH:
return read_line_comment(ctx);
case SYM_DOLLAR:
status = read_var(ctx, TOK_VAR, &tok);
if (status != BSHELL_SUCCESS) {
return status;
}
enqueue_token(ctx, tok);
return status;
case SYM_AT:
status = read_var(ctx, TOK_VAR_SPLAT, &tok);
if (status != BSHELL_SUCCESS) {
return status;
}
enqueue_token(ctx, tok);
return status;
case SYM_DOLLAR_LEFT_BRACE:
status = read_braced_var(ctx, TOK_VAR, &tok);
if (status != BSHELL_SUCCESS) {
return status;
}
enqueue_token(ctx, tok);
return status;
default:
break;
}
push_symbol(ctx, sym->id);
return BSHELL_SUCCESS;
}
static enum bshell_status statement_word(struct lex_ctx *ctx)
{
struct lex_token *word = NULL;
enum bshell_status status = read_word(ctx, 0, &word);
if (status != BSHELL_SUCCESS) {
return status;
}
struct lex_state *state = lex_state_get(ctx);
bool enable_keywords = !(state->s_flags & STATEMENT_F_DISABLE_KEYWORDS);
unsigned int token = TOK_WORD;
if (enable_keywords && convert_word_to_keyword(word)) {
token = word->tok_keyword;
} else if (convert_word_to_number(word)) {
token = word->tok_type;
}
handle_lex_state_transition(ctx, token);
enqueue_token(ctx, word);
return BSHELL_SUCCESS;
}
static enum bshell_status statement_pump_token(struct lex_ctx *ctx)
{
fx_wchar c = peek_char(ctx);
bool newline = false;
set_token_start(ctx);
while (fx_wchar_is_space(c)) {
if (c == '\n') {
newline = true;
}
set_token_end(ctx);
advance_char_noread(ctx);
c = peek_char_noread(ctx);
}
if (newline) {
struct lex_token *tok = lex_token_create(TOK_LINEFEED);
enqueue_token(ctx, tok);
handle_lex_state_transition(ctx, TOK_LINEFEED);
return BSHELL_SUCCESS;
}
if (char_can_begin_symbol(ctx, c)) {
return statement_symbol(ctx);
}
if (char_has_flags(ctx, c, LEX_TOKEN_UNARY_ARITHMETIC)) {
lex_state_change(ctx, LEX_STATE_ARITHMETIC);
return BSHELL_SUCCESS;
}
return statement_word(ctx);
}
static const struct lex_state_link links[] = {
LINK_PUSH(SYM_DQUOTE, LEX_STATE_STRING, 0),
/* arithmetic tokens */
LINK_CHANGE(TOK_KEYWORD, LEX_STATE_ARITHMETIC),
LINK_CHANGE(SYM_SQUOTE, LEX_STATE_ARITHMETIC),
LINK_CHANGE(TOK_INT, LEX_STATE_ARITHMETIC),
LINK_CHANGE(TOK_DOUBLE, LEX_STATE_ARITHMETIC),
LINK_PUSH(SYM_DOLLAR, LEX_STATE_ARITHMETIC, 0),
LINK_PUSH_WITH_TERM(
SYM_DOLLAR_LEFT_BRACE,
LEX_STATE_ARITHMETIC,
0,
SYM_RIGHT_BRACE),
LINK_CHANGE(SYM_AT_LEFT_BRACE, LEX_STATE_ARITHMETIC),
LINK_CHANGE(SYM_AT_LEFT_PAREN, LEX_STATE_ARITHMETIC),
LINK_PUSH_WITH_TERM(
SYM_AT_LEFT_BRACE,
LEX_STATE_HASHTABLE,
0,
SYM_RIGHT_BRACE),
LINK_PUSH(SYM_AT, LEX_STATE_ARITHMETIC, 0),
LINK_CHANGE(SYM_LEFT_PAREN, LEX_STATE_ARITHMETIC),
LINK_CHANGE(SYM_LEFT_BRACKET, LEX_STATE_ARITHMETIC),
LINK_CHANGE(SYM_BANG, LEX_STATE_ARITHMETIC),
LINK_PUSH_WITH_TERM(
SYM_LEFT_PAREN,
LEX_STATE_STATEMENT,
STATEMENT_F_DISABLE_KEYWORDS,
SYM_RIGHT_PAREN),
/* statement tokens */
LINK_PUSH_WITH_TERM(
SYM_LEFT_BRACE,
LEX_STATE_STATEMENT,
0,
SYM_RIGHT_BRACE),
LINK_PUSH_WITH_TERM(
SYM_DOLLAR_LEFT_PAREN,
LEX_STATE_STATEMENT,
0,
SYM_RIGHT_PAREN),
/* command tokens */
LINK_CHANGE(KW_FUNC, LEX_STATE_COMMAND),
LINK_CHANGE(SYM_AMPERSAND, LEX_STATE_COMMAND),
LINK_CHANGE(TOK_WORD, LEX_STATE_COMMAND),
LINK_END,
};
static const unsigned int keywords[] = {
KW_FUNC,
KW_IF,
KW_ELSEIF,
KW_ELSE,
KW_NONE,
};
static const unsigned int operators[] = {
TKOP_BNOT,
TKOP_NOT,
TKOP_NONE,
};
static const unsigned int symbols[] = {
SYM_AMPERSAND,
SYM_BANG,
SYM_SQUOTE,
SYM_DQUOTE,
SYM_HASH,
SYM_AT,
SYM_AT_LEFT_PAREN,
SYM_AT_LEFT_BRACE,
SYM_PIPE,
SYM_COMMA,
SYM_SEMICOLON,
SYM_LEFT_BRACE,
SYM_RIGHT_BRACE,
SYM_LEFT_BRACKET,
SYM_RIGHT_BRACKET,
SYM_LEFT_PAREN,
SYM_RIGHT_PAREN,
SYM_NONE,
};
const struct lex_state_type lex_statement_state = {
.s_id = LEX_STATE_STATEMENT,
.s_pump_token = statement_pump_token,
.s_links = links,
.s_keywords = keywords,
.s_operators = operators,
.s_symbols = symbols,
};

Some files were not shown because too many files have changed in this diff Show More