Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Python cache
__pycache__/
*.pyc
*.pyo
*.pyd
.Python

# Test outputs
*.tmp
73 changes: 73 additions & 0 deletions demo.fawk
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#!/usr/bin/env python3 fawk.py
# FAWK Demo - Showcasing all major features

# Define some helper functions
function double(x) { return x * 2 }
function square(x) { return x * x }

# Note: map, filter, reduce are built-in functions
# We use them directly without defining them

BEGIN {
print "==================================="
print "FAWK Demo - Functional AWK"
print "==================================="
print ""

# 1. First-class arrays
print "1. First-class arrays:"
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print " Numbers:", numbers

# 2. First-class functions
print ""
print "2. First-class functions:"
print " double(21) =", double(21)

# 3. Anonymous functions (lambdas)
print ""
print "3. Anonymous functions:"
cube = (x) => { x * x * x }
print " cube(3) =", cube(3)

# 4. Higher-order functions
print ""
print "4. Higher-order functions:"
doubled = map(double, [1, 2, 3, 4, 5])
print " map(double, [1,2,3,4,5]) =", doubled

evens = filter((n) => { n % 2 == 0 }, [1, 2, 3, 4, 5, 6])
print " filter(even, [1,2,3,4,5,6]) =", evens

# 5. Pipeline operator
print ""
print "5. Pipeline operator:"
result = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|> filter((n) => { n % 2 == 0 })
|> map(square)
|> reduce((a, b) => { a + b }, 0)
print " Sum of squares of evens:", result

# 6. Associative arrays
print ""
print "6. Associative arrays:"
scores = ["Alice" => 95, "Bob" => 87, "Carol" => 92]
print " Scores:", scores
high_scorers = scores |> filter((s) => { s >= 90 })
print " High scorers (>=90):", high_scorers

# 7. Nested arrays and complex operations
print ""
print "7. Nested arrays:"
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print " Matrix:"
for (i in matrix) {
row = matrix[i]
print " ", row[0], row[1], row[2]
}

print ""
print "==================================="
print "Demo complete!"
print "==================================="
}
9 changes: 9 additions & 0 deletions fawk
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/bash
# FAWK Wrapper Script
# Runs the Python FAWK interpreter

# Get the directory where this script is located
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

# Run the Python interpreter
exec python3 "$SCRIPT_DIR/fawk.py" "$@"
81 changes: 81 additions & 0 deletions fawk.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#!/usr/bin/env python3
"""
FAWK - Functional AWK Interpreter
A functional AWK dialect with first-class functions and arrays.
"""

import sys
from fawk_lexer import Lexer
from fawk_parser import Parser
from fawk_interpreter import Interpreter


def main():
if len(sys.argv) < 2:
print("Usage: fawk <script.fawk> [input_file]", file=sys.stderr)
sys.exit(1)

script_file = sys.argv[1]

# Read source code
try:
with open(script_file, 'r') as f:
source = f.read()
except FileNotFoundError:
print(f"Error: Script file '{script_file}' not found", file=sys.stderr)
sys.exit(1)
except IOError as e:
print(f"Error reading script file: {e}", file=sys.stderr)
sys.exit(1)

# Tokenize
try:
lexer = Lexer(source)
tokens = lexer.tokenize()
except SyntaxError as e:
print(f"Lexer error: {e}", file=sys.stderr)
sys.exit(1)

# Parse
try:
parser = Parser(tokens)
program = parser.parse()
except SyntaxError as e:
print(f"Parser error: {e}", file=sys.stderr)
sys.exit(1)

# Interpret
# Prepare ARGC and ARGV (mimicking AWK behavior)
argc = len(sys.argv)
argv = sys.argv # [fawk.py, script.fawk, input_file, ...]
interpreter = Interpreter(argc, argv)

# Read input if provided
input_lines = []
input_file = None
if len(sys.argv) > 2:
input_file = sys.argv[2]
interpreter.FILENAME = input_file
try:
with open(input_file, 'r') as f:
input_lines = f.readlines()
except FileNotFoundError:
print(f"Error: Input file '{input_file}' not found", file=sys.stderr)
sys.exit(1)
except IOError as e:
print(f"Error reading input file: {e}", file=sys.stderr)
sys.exit(1)

# Run
try:
interpreter.run(program, input_lines)
except RuntimeError as e:
print(f"Runtime error: {e}", file=sys.stderr)
sys.exit(1)
except KeyboardInterrupt:
print("\nInterrupted", file=sys.stderr)
sys.exit(130)


if __name__ == '__main__':
main()
167 changes: 167 additions & 0 deletions fawk_ast.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
"""
FAWK AST (Abstract Syntax Tree)
Defines all AST node classes
"""

from dataclasses import dataclass
from typing import List, Optional, Any


@dataclass
class ASTNode:
pass


@dataclass
class Program(ASTNode):
functions: List['FunctionDef']
begin_block: Optional['Block']
patterns: List['PatternAction']
end_block: Optional['Block']


@dataclass
class FunctionDef(ASTNode):
name: str
params: List[str]
body: 'Block'


@dataclass
class PatternAction(ASTNode):
pattern: Optional[ASTNode]
action: 'Block'


@dataclass
class Block(ASTNode):
statements: List[ASTNode]


@dataclass
class GlobalDecl(ASTNode):
names: List[str]


@dataclass
class IfStmt(ASTNode):
condition: ASTNode
then_block: 'Block'
else_block: Optional['Block']


@dataclass
class ForInStmt(ASTNode):
var: str
iterable: ASTNode
body: 'Block'


@dataclass
class WhileStmt(ASTNode):
condition: ASTNode
body: 'Block'


@dataclass
class ReturnStmt(ASTNode):
value: Optional[ASTNode]


@dataclass
class BreakStmt(ASTNode):
pass


@dataclass
class ContinueStmt(ASTNode):
pass


@dataclass
class PrintStmt(ASTNode):
args: List[ASTNode]


@dataclass
class ExprStmt(ASTNode):
expr: ASTNode


@dataclass
class BinaryOp(ASTNode):
op: str
left: ASTNode
right: ASTNode


@dataclass
class UnaryOp(ASTNode):
op: str
operand: ASTNode


@dataclass
class Assignment(ASTNode):
target: ASTNode
value: ASTNode


@dataclass
class ArrayLiteral(ASTNode):
elements: List[ASTNode]


@dataclass
class AssocArray(ASTNode):
pairs: List[tuple] # [(key_expr, value_expr), ...]


@dataclass
class ArrayAccess(ASTNode):
array: ASTNode
index: ASTNode


@dataclass
class FunctionCall(ASTNode):
func: ASTNode
args: List[ASTNode]


@dataclass
class Lambda(ASTNode):
params: List[str]
body: 'Block'


@dataclass
class Pipeline(ASTNode):
left: ASTNode
right: ASTNode


@dataclass
class Identifier(ASTNode):
name: str


@dataclass
class Number(ASTNode):
value: float


@dataclass
class String(ASTNode):
value: str


@dataclass
class Regex(ASTNode):
pattern: str
flags: str


@dataclass
class FieldAccess(ASTNode):
index: ASTNode
Loading